npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@302ai/media-studio-cli

v0.1.0

Published

CLI for AI Media Studio — let external agents drive the core media-studio features

Readme

@302ai/media-studio-cli

CLI for AI Media Studio — drive media-studio chat from any terminal or external agent.

Install

npm install -g @302ai/media-studio-cli
# requires Node.js >= 22

No stable release exists yet. latest currently points at a prerelease. Staging- and Local-server visibility is gated on the package version carrying a prerelease tag — not on the dist-tag — so Staging and Local appear in msc login and in --region whichever way you install. A future stable release will hide them from both channels.

If you installed via bun and get command not found: bun's global bin dir is not on PATH by default. Run once:

echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc

Usage

msc login                   # authenticate in a browser
msc guide                   # the full agent-facing manual
msc whoami                  # show current auth status
msc chat "hi"               # one chat turn (multi-turn by default)
msc sessions                # list sessions from the server
msc skills                  # list the skill catalog you can reference
msc logout                  # clear stored credentials

media-studio-cli is the long alias for msc — the same program, both installed. The docs, the help output, and every error message use the short name:

msc chat "画一只猫" -r        # -r/--show-reasoning = 展示模型推理过程
msc chat "hi" --json          # machine-readable output
msc chat "hi" --session <id>  # 切换到指定会话并设为默认
msc sessions                  # 列出所有会话(标记当前默认 `(current)`)
msc sessions --json           # 机器可读的原始 JSON 数组
msc sessions use <id>         # 纯本地切换默认会话(零请求、不校验)
msc skills                    # 列出技能库(--plaza 浏览技能广场)
msc skills add <slug>          # 从广场安装技能(如 brainstorming,见 --plaza --json 的 slug)
msc skills rm <command>       # 从技能库移除(-y/--yes 跳过确认)
msc skills enable <command>   # 启用 / disable 停用
msc --help                    # all flags

Session deletion is not available in the CLI. To delete a session, open the corresponding session page in the Media Studio web app and delete it manually.

msc --help carries an agent-oriented quickstart — the auth prerequisite, the three canonical calls, and the --json contract. msc guide prints the full manual: every command, both JSON schemas, the nextStep values, exit codes, stream discipline, and skill semantics. Point an agent at those two instead of duplicating them here.

Headless environments cannot complete browser sign-in, so msc login refuses immediately instead of waiting out the callback deadline: CI (CI set), and Linux with neither DISPLAY nor WAYLAND_DISPLAY. Sign in from a machine whose browser can reach the loopback callback.

Multi-turn conversation

chat is multi-turn by default — no flag needed. The CLI keeps a single lastSessionId pointer in config.json (under the config dir):

  • First-ever chat provisions a session via the same prewarm flow the web app uses (source cli), printing stage progress (creating / sandbox / skills / finishing), then chats. The pointer is written the moment the session is durably persisted upstream — an interrupted provision never loses a live session.
  • Later chats continue that session — upstream remembers the conversation by session id, so no history is resent.
  • If the session was deleted elsewhere, the CLI transparently provisions a fresh one and continues — you are never stranded.
  • --session <id> switches to a specific session (including web-created ones) and makes it the default, without provisioning.

Manual proof of continuity:

msc chat "记住数字42"
msc chat "刚才的数字是几?"   # answers 42

Point at a different server via msc login --region default|cn|local (the server you log into is saved and reused; local — like staging — is a prerelease-only region, see the note at the top).

Configuration and storage

The CLI stores credentials and local settings in:

  • Default: ~/.config/media-studio-cli/
  • XDG standard: $XDG_CONFIG_HOME/media-studio-cli/
  • Override: MEDIA_STUDIO_CONFIG_DIR=/path/to/dir

Key files:

  • auth.json: stores active platform credentials and user session metadata (permissions 0600)
  • config.json: stores CLI client settings, including lastSessionId pointer

Skills

Discover what your account can reference, then reference it — the reference must name a skill that exists in your catalog:

msc skills                     # list your library
msc skills --json              # raw catalog array (see fields below)
msc skills --plaza             # browse the Skill Plaza with your library
                               # state merged in (built-ins excluded)
msc skills --plaza --json

msc skills add <slug>           # install a plaza entry by slug
msc skills rm <command>        # remove a library skill by command
msc skills enable <command>    # flip a library skill's enabled flag
msc skills disable <command>

add takes the entry's plaza slug (brainstorming, shown in msc skills --plaza --json as slug); the library mutations take the command string (brainstorming and /brainstorming are the same entry) — the same string the prompt uses to reference the skill.

--json is the intended output for agents: the catalog is the input to a decision the calling program makes itself. Both descriptionEn/descriptionZh are passed through untouched — upstream does not guarantee the En slot is English, so the CLI picks nothing. The human rendering lists names only (the command string is available in --json) with the full descriptionEn on a second line, and marks [on] enabled / [off] disabled / [-] not installed in the plaza view.

Shared API endpoints: mutations use the exact same endpoints as the web app (POST /api/skills for installs, DELETE /api/skills?skillId= for removal, POST /api/skills/enabled for enable/disable). The CLI resolves the slug/command to the entry/id before calling the endpoint. rm requires --yes outside a terminal. Built-in entries are rejected (422): they sync into your library from the backend. Managing skills (upload, cover) stays in the Media Studio web app.

Then reference a skill in the prompt and the CLI installs it into the session sandbox on demand:

msc chat "/always-hello 不管我说什么"      # leading slash command
msc chat "%%/always-hello%%不管我说什么"    # web composer marker, paste-compatible

Both spellings resolve to the same thing. The upstream installs a skill only from the turn's skill_urls field, so the CLI looks the reference up in your catalog (GET /api/skills) and sends the matching package URL — prompt text alone triggers nothing.

  • A bare /command counts only at the start of the prompt, so mid-sentence slashes stay prose (check /etc/hosts is not a skill reference). The %%…%% marker is also recognized anywhere — a web prompt pasted verbatim keeps its token — but the skill activates upstream only when the marker leads the message, so lead the prompt with it. The leading token can be any bare slug, single-segment included — /tmp what is this resolves /tmp — and a reference that matches nothing fails the turn with the catalog listed, so a typo never silently degrades to a skill-less answer.
  • The catalog is fetched only when the prompt references a skill; skill-free turns cost no extra request.
  • Unknown skills fail the turn with a non-zero exit before any session is provisioned. A disabled skill is auto-enabled first — the same ADR-0001 behavior as the web's "Use in Chat" — because the upstream treats enabled as a hard precondition for invocation and would otherwise download the package and silently never run it. The enable flips the skill's enabled flag on your account (a persistent change), is reported as Auto-enabled for this turn: …, and a failed enable aborts the turn.

Attachments

Attach local files to a chat turn using @path anywhere in the prompt:

msc chat "看看 @cover.png 这张图"             # bare path (ends at whitespace)
msc chat "总结 @/abs/path/notes.pdf"          # absolute path
msc chat '对比 @"my docs/report.docx" 内容'   # quoted path with spaces

The CLI mirrors the web composer's upload flow:

  1. Validates each referenced file locally (exists, readable, supported extension, under 50 MB) before any network call.
  2. Uploads each file via the web app's POST /api/chat/attachments endpoint with a terminal spinner (Uploading attachment 1/2: cover.png).
  3. Rewrites the prompt to inline the remote URL using the web's &&<url>&& marker, which the upstream model and web session history natively understand.
  4. Up to 5 unique attachments per turn (same as the web composer); referencing the same file twice uploads it once.
  5. An unattachable reference (missing file, unsupported format) fails the turn before any session is provisioned.
  6. Prose mentions (@team 辛苦了, email addresses) stay literal text — only tokens with a file extension or path slash are treated as attachments.
  7. @path is local-filesystem-only: @https://example.com/a.png fails with no such file. There is no @url form — remote URLs are not attachments.

In --json mode the uploaded attachments are surfaced under the attachments array: [{ name, type, size, remoteUrl }].

Staging server (expired TLS certificate)

The staging server's certificate is expired (ops declines renewal), so the CLI skips TLS certificate verification only while pointed at the staging host; the server choice itself is the opt-in, and the default / CN hosts always verify. The interactive msc login region list shows each server's URL and marks Staging with the auto-off behavior; msc login --region staging opts in.

Stable (production) builds hide Staging and Local from the msc login region menu and --region, but a persisted auth.json from a prerelease login keeps pointing at that server — and a staging one still auto-bypasses TLS verification. This is intentional (the login persists across upgrades), not an oversight.

The whitelist is the only way the bypass turns on — no environment variable can extend it to another server, so production can never skip verification. To force verification back on while pointed at staging (once ops renews the certificate, or to debug its chain):

MEDIA_STUDIO_INSECURE_TLS=0 msc chat "hi"   # also accepts "false"

Plain-http:// servers skip the bypass entirely (nothing to verify). The bypass prints nothing: the staging choice is already stated in the msc login menu and here, so no warning repeats on every command (--help and --version included, whose output scripts and agents parse).

TLS-intercepting proxies

If browser SSO login works but every API call fails with fetch failed, a TLS-intercepting proxy (Clash/FlClash TUN mode, corporate MITM) is breaking certificate verification (unable to verify the first certificate). Node/Bun do not use the OS keychain, so a proxy CA trusted by your browser is not trusted by the CLI. Either trust the proxy CA explicitly:

export NODE_EXTRA_CA_CERTS=/path/to/proxy-ca.crt

or skip verification for the CLI only (no certificate checks). The staging host already does this automatically, so this is only needed for other servers — and it is now the only way to bypass a non-staging host, since no msc flag or env var does so. Note it is process-global: every host the CLI talks to stops being verified.

NODE_TLS_REJECT_UNAUTHORIZED=0 msc sessions

Development

bun run dev                   # run from TS source (bun), no build step
bun run build                 # bundled JS entry -> dist/ (the npm artifact)
bun run build:binary          # standalone single-file binary -> bin/

Testing (before shipping any change)

Test layers from fast to faithful. Minimum bar: layer 2 passes; before an actual publish also run layer 4.

1. Dev iteration (fastest)

bun run dev -- chat "hi"      # runs TS source directly, no packaging

2. Automated external-consumer check (required)

# from repo root
bun run package:cli:check

Builds, type-checks, packs the real tarball, installs it into a temp directory outside the repo, and executes the installed binaries (--version / --help assertions). Catches packaging-only breakage (missing shebang, lost exec bit, stale bin target) that in-repo runs never see.

3. Manual user-install simulation

cd packages/media-studio-cli
bun pm pack --destination /tmp/cli-test

# option A: clean local consumer
mkdir /tmp/cli-consumer && cd /tmp/cli-consumer
bun add /tmp/cli-test/302ai-media-studio-cli-0.1.0.tgz
./node_modules/.bin/msc --version

# option B: global install (also verifies the short name doesn't collide)
bun add -g /tmp/cli-test/302ai-media-studio-cli-0.1.0.tgz
msc --version

# cleanup afterwards — one command removes BOTH bin names
bun remove -g @302ai/media-studio-cli

4. End-to-end against a real server

# terminal 1: local gateway (CLI defaults to localhost:3000)
bun run dev

# terminal 2:
msc login                     # browser sign-in
msc whoami                    # verify auth state
msc chat "画一只猫" -r        # real streaming turn
msc chat "hi" --json

# Multi-turn continuity acceptance recipe:
msc chat "记住数字42"
msc chat "刚才的数字是几?"     # answers 42

# Session management verification:
msc sessions                  # shows session list with (current) marker
msc chat "hi" --session <id>  # switch to specific session
msc sessions use <id>         # switch default pointer locally

Without the local server, point at production with msc login --region default.

Notes learned the hard way

  • bun pm pack does NOT run publish lifecycle scripts; the smoke script builds explicitly first. Don't assume pack produces a fresh dist.
  • Two bin entries pointing at the SAME file make bun pm pack emit duplicate tarball entries — that's why msc targets the tiny dist/msc.js shim instead of dist/index.js. Keep bin targets unique.
  • bun publish requires a modern bun (lifecycle scripts); the repo standard publish gate is bun run package:cli:check first.

License

Apache-2.0