@302ai/media-studio-cli 0.1.2-beta.0 → 0.1.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +130 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -215,7 +215,7 @@ The CLI mirrors the web composer's upload flow:
|
|
|
215
215
|
1. Validates each referenced file locally (exists, readable, supported extension, under 50 MB) before any network call.
|
|
216
216
|
2. Uploads each file via the web app's `POST /api/chat/attachments` endpoint with a terminal spinner (`Uploading attachment 1/2: cover.png`).
|
|
217
217
|
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.
|
|
218
|
-
4. Up to
|
|
218
|
+
4. Up to 10 unique attachments per turn (same as the web composer); referencing the same file twice uploads it once.
|
|
219
219
|
5. An unattachable reference (missing file, unsupported format) fails the turn before any session is provisioned.
|
|
220
220
|
6. Prose mentions (`@team 辛苦了`, email addresses) stay literal text — only tokens with a file extension or path slash are treated as attachments.
|
|
221
221
|
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.
|
package/dist/index.js
CHANGED
|
@@ -4284,7 +4284,7 @@ var consola = createConsola2();
|
|
|
4284
4284
|
// package.json
|
|
4285
4285
|
var package_default = {
|
|
4286
4286
|
name: "@302ai/media-studio-cli",
|
|
4287
|
-
version: "0.1.2-beta.
|
|
4287
|
+
version: "0.1.2-beta.1",
|
|
4288
4288
|
description: "CLI for AI Media Studio — let external agents drive the core media-studio features",
|
|
4289
4289
|
author: "302.AI",
|
|
4290
4290
|
license: "Apache-2.0",
|
|
@@ -9648,7 +9648,7 @@ var ArtifactItemSchema = object({
|
|
|
9648
9648
|
var ArtifactItemsSchema = array(ArtifactItemSchema);
|
|
9649
9649
|
|
|
9650
9650
|
// ../media-studio-contract/src/attachments/constants.ts
|
|
9651
|
-
var MAX_ATTACHMENT_COUNT =
|
|
9651
|
+
var MAX_ATTACHMENT_COUNT = 10;
|
|
9652
9652
|
var MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
|
|
9653
9653
|
var ATTACHMENT_MIME_BY_EXTENSION = {
|
|
9654
9654
|
png: "image/png",
|
|
@@ -10058,6 +10058,17 @@ var MEDIA_STUDIO_URLS = {
|
|
|
10058
10058
|
cn: "https://media.302ai.cn",
|
|
10059
10059
|
staging: "https://media.tool302.com"
|
|
10060
10060
|
};
|
|
10061
|
+
var OSS_GLOBAL_ORIGIN = "https://sonier-file-oss.small-base-aaeb.workers.dev";
|
|
10062
|
+
var OSS_CN_ORIGIN = "https://sonier.oss-cn-shenzhen.aliyuncs.com";
|
|
10063
|
+
function resolveOssUrl(url, useCnDomain) {
|
|
10064
|
+
if (!useCnDomain || !url.startsWith(OSS_GLOBAL_ORIGIN))
|
|
10065
|
+
return url;
|
|
10066
|
+
const suffix = url.slice(OSS_GLOBAL_ORIGIN.length);
|
|
10067
|
+
const boundary = suffix[0];
|
|
10068
|
+
if (boundary && !"/?#".includes(boundary))
|
|
10069
|
+
return url;
|
|
10070
|
+
return OSS_CN_ORIGIN + suffix;
|
|
10071
|
+
}
|
|
10061
10072
|
var MEDIA_STUDIO_HOSTS = [
|
|
10062
10073
|
new URL(MEDIA_STUDIO_URLS.default).hostname,
|
|
10063
10074
|
new URL(MEDIA_STUDIO_URLS.cn).hostname,
|
|
@@ -10073,6 +10084,34 @@ var APP_ENV_HOSTS = {
|
|
|
10073
10084
|
]
|
|
10074
10085
|
};
|
|
10075
10086
|
|
|
10087
|
+
// ../media-studio-contract/src/model-flag.ts
|
|
10088
|
+
var MODEL_FLAG_BLOCK_RE = /\$(?=\s*--model\b)([^$]+)\$/gi;
|
|
10089
|
+
var MODEL_FLAG_HEAD_RE = /^--model\s+("(?:[^"\\]|\\.)+"|[^\s]+)/i;
|
|
10090
|
+
function findModelFlagMatches(text) {
|
|
10091
|
+
const pattern = new RegExp(MODEL_FLAG_BLOCK_RE.source, "gi");
|
|
10092
|
+
return Array.from(text.matchAll(pattern)).flatMap((match) => {
|
|
10093
|
+
const start = match.index;
|
|
10094
|
+
const body = match[1]?.trim();
|
|
10095
|
+
if (start === undefined || !body)
|
|
10096
|
+
return [];
|
|
10097
|
+
const token = body.match(MODEL_FLAG_HEAD_RE)?.[1];
|
|
10098
|
+
if (!token)
|
|
10099
|
+
return [];
|
|
10100
|
+
let modelId = token;
|
|
10101
|
+
if (token.startsWith('"')) {
|
|
10102
|
+
try {
|
|
10103
|
+
const decoded = JSON.parse(token);
|
|
10104
|
+
if (typeof decoded !== "string")
|
|
10105
|
+
return [];
|
|
10106
|
+
modelId = decoded;
|
|
10107
|
+
} catch {
|
|
10108
|
+
return [];
|
|
10109
|
+
}
|
|
10110
|
+
}
|
|
10111
|
+
return [{ start, end: start + match[0].length, body, modelId }];
|
|
10112
|
+
});
|
|
10113
|
+
}
|
|
10114
|
+
|
|
10076
10115
|
// ../media-studio-contract/src/skills/activation.ts
|
|
10077
10116
|
var SKILL_ACTIVATION_MARKER = "%%";
|
|
10078
10117
|
var SKILL_ACTIVATION_RE = /%%(\/[^%\s]+)%%/g;
|
|
@@ -10102,21 +10141,20 @@ function findSkillActivationMatches(text) {
|
|
|
10102
10141
|
|
|
10103
10142
|
// ../media-studio-contract/src/sessions/derive-first-turn-note.ts
|
|
10104
10143
|
var DEFAULT_FALLBACK_TITLE = "Agent Task";
|
|
10105
|
-
var
|
|
10144
|
+
var MAX_PROJECT_TITLE_LENGTH = 64;
|
|
10145
|
+
function clampSessionTitle(title) {
|
|
10146
|
+
return Array.from(title).slice(0, MAX_PROJECT_TITLE_LENGTH).join("");
|
|
10147
|
+
}
|
|
10106
10148
|
function deriveFirstTurnNote(rawText, options) {
|
|
10107
10149
|
const fallback = options?.fallbackTitle ?? DEFAULT_FALLBACK_TITLE;
|
|
10108
10150
|
if (!rawText) {
|
|
10109
10151
|
return fallback;
|
|
10110
10152
|
}
|
|
10111
|
-
const unwrapped = rawText.
|
|
10153
|
+
const unwrapped = findModelFlagMatches(rawText).toReversed().reduce((text, match) => `${text.slice(0, match.start)}${match.modelId} ${text.slice(match.end)}`, rawText).replace(SKILL_ACTIVATION_RE, "$1").replace(ATTACHMENT_MARKER_REGEX, "$1").replace(/\s+/g, " ").trim();
|
|
10112
10154
|
if (!unwrapped) {
|
|
10113
10155
|
return fallback;
|
|
10114
10156
|
}
|
|
10115
|
-
|
|
10116
|
-
if (codePoints.length > MAX_NOTE_LENGTH) {
|
|
10117
|
-
return codePoints.slice(0, MAX_NOTE_LENGTH).join("");
|
|
10118
|
-
}
|
|
10119
|
-
return unwrapped;
|
|
10157
|
+
return clampSessionTitle(unwrapped);
|
|
10120
10158
|
}
|
|
10121
10159
|
|
|
10122
10160
|
// ../media-studio-contract/src/skills/compose-turn.ts
|
|
@@ -17909,6 +17947,26 @@ function reportCommandError(err, options) {
|
|
|
17909
17947
|
process.exit(1);
|
|
17910
17948
|
}
|
|
17911
17949
|
|
|
17950
|
+
// src/lib/open-url.ts
|
|
17951
|
+
var OPEN_URL_NOTE = "Open or render open_url. Submit or forward url unchanged.";
|
|
17952
|
+
function loginUsesCnOss(apiBaseUrl) {
|
|
17953
|
+
try {
|
|
17954
|
+
return new URL(apiBaseUrl).origin === MEDIA_STUDIO_URLS.cn;
|
|
17955
|
+
} catch {
|
|
17956
|
+
return false;
|
|
17957
|
+
}
|
|
17958
|
+
}
|
|
17959
|
+
function resolveOpenUrl(url, apiBaseUrl) {
|
|
17960
|
+
return resolveOssUrl(url, loginUsesCnOss(apiBaseUrl));
|
|
17961
|
+
}
|
|
17962
|
+
function deriveOpenUrl(rawUrl, apiBaseUrl) {
|
|
17963
|
+
const sanitized = rawUrl?.replace(/\r?\n/g, " ").trim();
|
|
17964
|
+
if (!sanitized)
|
|
17965
|
+
return;
|
|
17966
|
+
const openUrl = resolveOpenUrl(sanitized, apiBaseUrl);
|
|
17967
|
+
return openUrl === sanitized ? undefined : openUrl;
|
|
17968
|
+
}
|
|
17969
|
+
|
|
17912
17970
|
// src/lib/spinner.ts
|
|
17913
17971
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
17914
17972
|
var BRAILLE_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
@@ -18272,7 +18330,11 @@ var PROVISION_STAGE_LABELS = {
|
|
|
18272
18330
|
skills: "Installing skills...",
|
|
18273
18331
|
finishing: "Finishing session setup..."
|
|
18274
18332
|
};
|
|
18275
|
-
function
|
|
18333
|
+
function withMediaOpenUrl(asset, apiBaseUrl) {
|
|
18334
|
+
const openUrl = deriveOpenUrl(asset.resultUrl, apiBaseUrl);
|
|
18335
|
+
return openUrl ? { ...asset, openUrl } : asset;
|
|
18336
|
+
}
|
|
18337
|
+
function printTerminalMediaAssets(mediaAssets, fullText, apiBaseUrl) {
|
|
18276
18338
|
if (mediaAssets.length === 0) {
|
|
18277
18339
|
if (fullText && !fullText.endsWith(`
|
|
18278
18340
|
`)) {
|
|
@@ -18289,13 +18351,20 @@ function printTerminalMediaAssets(mediaAssets, fullText) {
|
|
|
18289
18351
|
process.stdout.write(`
|
|
18290
18352
|
=== MEDIA ASSETS ===
|
|
18291
18353
|
`);
|
|
18354
|
+
let noted = false;
|
|
18292
18355
|
for (const asset of mediaAssets) {
|
|
18356
|
+
const openUrl = deriveOpenUrl(asset.resultUrl, apiBaseUrl);
|
|
18357
|
+
noted ||= Boolean(openUrl);
|
|
18293
18358
|
process.stdout.write(`- type: ${asset.domain ?? "media"}
|
|
18294
18359
|
` + (asset.resultUrl ? `- url: ${asset.resultUrl}
|
|
18360
|
+
` : "") + (openUrl ? `- open_url: ${openUrl}
|
|
18295
18361
|
` : "") + (asset.modelUsed ? `- model: ${asset.modelUsed}
|
|
18296
18362
|
` : "") + (asset.taskId ? `- task_id: ${asset.taskId}
|
|
18297
18363
|
` : ""));
|
|
18298
18364
|
}
|
|
18365
|
+
if (noted)
|
|
18366
|
+
process.stdout.write(`${OPEN_URL_NOTE}
|
|
18367
|
+
`);
|
|
18299
18368
|
process.stdout.write(`====================
|
|
18300
18369
|
`);
|
|
18301
18370
|
}
|
|
@@ -18354,7 +18423,7 @@ function describeStreamFailure(err) {
|
|
|
18354
18423
|
return String(err);
|
|
18355
18424
|
}
|
|
18356
18425
|
async function streamTurn(options) {
|
|
18357
|
-
const { args, sessionId, spinner, autoEnabled, attachments } = options;
|
|
18426
|
+
const { args, sessionId, spinner, autoEnabled, attachments, apiBaseUrl } = options;
|
|
18358
18427
|
if (!args.json) {
|
|
18359
18428
|
spinner.start("Connecting to Media Studio...");
|
|
18360
18429
|
}
|
|
@@ -18441,7 +18510,7 @@ async function streamTurn(options) {
|
|
|
18441
18510
|
sessionId,
|
|
18442
18511
|
text: fullText,
|
|
18443
18512
|
steps,
|
|
18444
|
-
media: mediaAssets,
|
|
18513
|
+
media: mediaAssets.map((asset) => withMediaOpenUrl(asset, apiBaseUrl)),
|
|
18445
18514
|
...attachments.length > 0 ? { attachments } : {},
|
|
18446
18515
|
...autoEnabled.length > 0 ? { autoEnabled } : {},
|
|
18447
18516
|
...args["show-reasoning"] && reasoning ? { reasoning } : {}
|
|
@@ -18451,7 +18520,7 @@ async function streamTurn(options) {
|
|
|
18451
18520
|
emitting = false;
|
|
18452
18521
|
return;
|
|
18453
18522
|
}
|
|
18454
|
-
printTerminalMediaAssets(mediaAssets, fullText);
|
|
18523
|
+
printTerminalMediaAssets(mediaAssets, fullText, apiBaseUrl);
|
|
18455
18524
|
process.stdout.write(`session: ${sessionId}
|
|
18456
18525
|
`);
|
|
18457
18526
|
emitting = false;
|
|
@@ -18475,7 +18544,7 @@ async function streamTurn(options) {
|
|
|
18475
18544
|
sessionId,
|
|
18476
18545
|
text: fullText,
|
|
18477
18546
|
steps,
|
|
18478
|
-
media: mediaAssets,
|
|
18547
|
+
media: mediaAssets.map((asset) => withMediaOpenUrl(asset, apiBaseUrl)),
|
|
18479
18548
|
...attachments.length > 0 ? { attachments } : {},
|
|
18480
18549
|
...autoEnabled.length > 0 ? { autoEnabled } : {},
|
|
18481
18550
|
...args["show-reasoning"] && reasoning ? { reasoning } : {}
|
|
@@ -18483,7 +18552,7 @@ async function streamTurn(options) {
|
|
|
18483
18552
|
process.stdout.write(`${JSON.stringify(output, null, 2)}
|
|
18484
18553
|
`);
|
|
18485
18554
|
} else {
|
|
18486
|
-
printTerminalMediaAssets(mediaAssets, fullText);
|
|
18555
|
+
printTerminalMediaAssets(mediaAssets, fullText, apiBaseUrl);
|
|
18487
18556
|
process.stdout.write(`session: ${sessionId}
|
|
18488
18557
|
`);
|
|
18489
18558
|
}
|
|
@@ -18720,7 +18789,7 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18720
18789
|
|
|
18721
18790
|
The CLI validates files locally (existence, supported format, under 50 MB),
|
|
18722
18791
|
uploads them with progress indication, and inlines remote URLs into the prompt.
|
|
18723
|
-
Up to
|
|
18792
|
+
Up to 10 attachments per turn. Referencing the same file multiple times
|
|
18724
18793
|
uploads it only once. Plain prose mentions (@team) and emails are ignored.
|
|
18725
18794
|
|
|
18726
18795
|
@path is local-filesystem-only: @https://example.com/a.png fails with
|
|
@@ -18774,7 +18843,9 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18774
18843
|
|
|
18775
18844
|
msc sessions artifacts <session-id>
|
|
18776
18845
|
List all generated assets (images, videos, audio) produced in that session,
|
|
18777
|
-
including download URLs and model metadata.
|
|
18846
|
+
including download URLs and model metadata. Text output adds an OPEN column
|
|
18847
|
+
only when the signed-in site needs a different origin; JSON adds the openUrl
|
|
18848
|
+
field in the same case. Submit or forward the link unchanged.
|
|
18778
18849
|
|
|
18779
18850
|
msc sessions use <session-id>
|
|
18780
18851
|
Switch your active session pointer to an existing session. The id is
|
|
@@ -18808,7 +18879,8 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url}`).join(`
|
|
|
18808
18879
|
"status": "completed",
|
|
18809
18880
|
"domain": "image", // "image" | "video" | etc.
|
|
18810
18881
|
"operation": "t2i", // "t2i", "t2v", etc.
|
|
18811
|
-
"resultUrl": "https://...",
|
|
18882
|
+
"resultUrl": "https://...", // stored address; submit this unchanged
|
|
18883
|
+
"openUrl": "https://...", // present only when rendering needs another origin
|
|
18812
18884
|
"modelUsed": "string",
|
|
18813
18885
|
"taskId": "string"
|
|
18814
18886
|
}
|
|
@@ -18824,8 +18896,17 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url}`).join(`
|
|
|
18824
18896
|
"reasoning": "string?" // present if --show-reasoning was passed
|
|
18825
18897
|
}
|
|
18826
18898
|
|
|
18827
|
-
|
|
18828
|
-
|
|
18899
|
+
resultUrl is the stored address. Submit or forward it unchanged. openUrl is
|
|
18900
|
+
present only when the signed-in site needs a different origin to render the
|
|
18901
|
+
object. Text output names that address open_url and adds one fixed note;
|
|
18902
|
+
JSON does not contain the note.
|
|
18903
|
+
|
|
18904
|
+
Extracting each role (e.g. with jq):
|
|
18905
|
+
result=$(msc chat "a red balloon" --json)
|
|
18906
|
+
url=$(printf '%s
|
|
18907
|
+
' "$result" | jq -r '.media[0].resultUrl')
|
|
18908
|
+
open=$(printf '%s
|
|
18909
|
+
' "$result" | jq -r '.media[0].openUrl // .media[0].resultUrl')
|
|
18829
18910
|
|
|
18830
18911
|
Exit codes: 0 = the turn ran to completion OR its stream ended early; 1 =
|
|
18831
18912
|
\`status: "error"\`, a usage error, or any other failure. Only what the parser
|
|
@@ -18907,6 +18988,7 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url}`).join(`
|
|
|
18907
18988
|
msc sessions --json # list: [{ sessionId, note, ... }]
|
|
18908
18989
|
msc sessions use <id> --json # { "sessionId": "string" }
|
|
18909
18990
|
msc sessions use --new --json # { "sessionId": "string" } (newly created)
|
|
18991
|
+
msc sessions artifacts <id> --json # [{..., link, openUrl?}]
|
|
18910
18992
|
\`--session\`/\`use <id>\` verify the id upstream; an unknown id exits 1 with
|
|
18911
18993
|
the \`--new\` hint and leaves the default session untouched.
|
|
18912
18994
|
|
|
@@ -20813,22 +20895,31 @@ ${bodyLines.join(`
|
|
|
20813
20895
|
`)}
|
|
20814
20896
|
`;
|
|
20815
20897
|
}
|
|
20816
|
-
function
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
|
|
20822
|
-
|
|
20898
|
+
function artifactRows(artifacts, apiBaseUrl) {
|
|
20899
|
+
return artifacts.map((a) => {
|
|
20900
|
+
const link = a.link ? a.link.replace(/\r?\n/g, " ").trim() || "-" : "-";
|
|
20901
|
+
const openUrl = link === "-" ? undefined : deriveOpenUrl(link, apiBaseUrl);
|
|
20902
|
+
return {
|
|
20903
|
+
type: a.type,
|
|
20904
|
+
status: a.status,
|
|
20905
|
+
title: a.title ? a.title.replace(/\r?\n/g, " ").trim() || "-" : "-",
|
|
20906
|
+
link,
|
|
20907
|
+
openUrl: openUrl ?? ""
|
|
20908
|
+
};
|
|
20909
|
+
});
|
|
20910
|
+
}
|
|
20911
|
+
function renderArtifactsTable(rows) {
|
|
20912
|
+
const showOpen = rows.some((row) => row.openUrl);
|
|
20823
20913
|
const headers = {
|
|
20824
20914
|
type: "TYPE",
|
|
20825
20915
|
status: "STATUS",
|
|
20826
20916
|
title: "TITLE",
|
|
20827
|
-
link: "LINK"
|
|
20917
|
+
link: "LINK",
|
|
20918
|
+
openUrl: "OPEN"
|
|
20828
20919
|
};
|
|
20829
20920
|
const typeWidth = Math.max(headers.type.length, ...rows.map((r) => r.type.length));
|
|
20830
20921
|
const statusWidth = Math.max(headers.status.length, ...rows.map((r) => r.status.length));
|
|
20831
|
-
const formatRow = (r) => `${r.type.padEnd(typeWidth)} ${r.status.padEnd(statusWidth)} ${r.title} ${r.link}`;
|
|
20922
|
+
const formatRow = (r) => `${r.type.padEnd(typeWidth)} ${r.status.padEnd(statusWidth)} ${r.title} ${r.link}${showOpen ? ` ${r.openUrl}` : ""}`;
|
|
20832
20923
|
return `${formatRow(headers)}
|
|
20833
20924
|
${rows.map(formatRow).join(`
|
|
20834
20925
|
`)}
|
|
@@ -20885,7 +20976,12 @@ var artifactsCommand = defineCommand({
|
|
|
20885
20976
|
sessionId: args.sessionId
|
|
20886
20977
|
});
|
|
20887
20978
|
if (args.json) {
|
|
20888
|
-
|
|
20979
|
+
const apiBaseUrl = resolveApiBaseUrl();
|
|
20980
|
+
const data = response.data.map((item) => {
|
|
20981
|
+
const openUrl = deriveOpenUrl(item.link, apiBaseUrl);
|
|
20982
|
+
return openUrl ? { ...item, openUrl } : item;
|
|
20983
|
+
});
|
|
20984
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
20889
20985
|
`);
|
|
20890
20986
|
return;
|
|
20891
20987
|
}
|
|
@@ -20893,7 +20989,10 @@ var artifactsCommand = defineCommand({
|
|
|
20893
20989
|
consola.info("No artifacts found.");
|
|
20894
20990
|
return;
|
|
20895
20991
|
}
|
|
20896
|
-
|
|
20992
|
+
const rows = artifactRows(response.data, resolveApiBaseUrl());
|
|
20993
|
+
consola.log(renderArtifactsTable(rows));
|
|
20994
|
+
if (rows.some((row) => row.openUrl))
|
|
20995
|
+
consola.log(OPEN_URL_NOTE);
|
|
20897
20996
|
}, { json: args.json })
|
|
20898
20997
|
});
|
|
20899
20998
|
var useSessionCommand = defineCommand({
|
package/package.json
CHANGED