@302ai/media-studio-cli 0.1.1-beta.0 → 0.1.1-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 +14 -1
- package/dist/index.js +426 -182
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,9 +115,22 @@ The CLI stores credentials and local settings in:
|
|
|
115
115
|
- **Override**: `MEDIA_STUDIO_CONFIG_DIR=/path/to/dir`
|
|
116
116
|
|
|
117
117
|
Key files:
|
|
118
|
-
- `auth.json`: stores active platform credentials and user session metadata (permissions `0600`)
|
|
118
|
+
- `auth.json`: stores active platform credentials and user session metadata (permissions `0600` on POSIX)
|
|
119
119
|
- `config.json`: stores CLI client settings, including `lastSessionId` pointer
|
|
120
120
|
|
|
121
|
+
`auth.json`'s `apiUrl` is only accepted when its host is on the CLI allowlist:
|
|
122
|
+
the official hosts (which the rejection message lists) plus the local-dev
|
|
123
|
+
hosts `localhost` / `127.0.0.1` / `::1`. A hand-edited URL outside that list is
|
|
124
|
+
refused before any request is sent, and the CLI reports it as a rejected
|
|
125
|
+
credential naming the URL — not as "not logged in", since the token itself may
|
|
126
|
+
be fine. `msc login` rewrites the file with a server-supplied URL and is the
|
|
127
|
+
fix; the refused URL is never used, and base-URL resolution falls back to the
|
|
128
|
+
official default host.
|
|
129
|
+
|
|
130
|
+
In `--json` mode the two auth states are distinguishable without reading the
|
|
131
|
+
message: a rejected credential carries `"nextStep": "relogin"` (and its
|
|
132
|
+
`guidance`), while no credential at all reports `status` and `message` only.
|
|
133
|
+
|
|
121
134
|
### Skills
|
|
122
135
|
|
|
123
136
|
Discover what your account can reference, then reference it — the reference
|
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.1-beta.
|
|
4287
|
+
version: "0.1.1-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",
|
|
@@ -9701,6 +9701,7 @@ var ATTACHMENT_MIME_BY_EXTENSION = {
|
|
|
9701
9701
|
|
|
9702
9702
|
// ../media-studio-contract/src/attachments/markers.ts
|
|
9703
9703
|
var ATTACHMENT_MARKER = "&&";
|
|
9704
|
+
var ATTACHMENT_MARKER_REGEX = /&&([^&\s]+)&&/g;
|
|
9704
9705
|
function formatAttachmentMarker(remoteUrl) {
|
|
9705
9706
|
return `${ATTACHMENT_MARKER}${remoteUrl}${ATTACHMENT_MARKER}`;
|
|
9706
9707
|
}
|
|
@@ -10073,11 +10074,14 @@ var APP_ENV_HOSTS = {
|
|
|
10073
10074
|
// ../media-studio-contract/src/skills/activation.ts
|
|
10074
10075
|
var SKILL_ACTIVATION_MARKER = "%%";
|
|
10075
10076
|
var SKILL_ACTIVATION_RE = /%%(\/[^%\s]+)%%/g;
|
|
10077
|
+
function createSkillActivationRegex() {
|
|
10078
|
+
return new RegExp(SKILL_ACTIVATION_RE.source, SKILL_ACTIVATION_RE.flags);
|
|
10079
|
+
}
|
|
10076
10080
|
function formatSkillActivation(command) {
|
|
10077
10081
|
return `${SKILL_ACTIVATION_MARKER}${command}${SKILL_ACTIVATION_MARKER}`;
|
|
10078
10082
|
}
|
|
10079
10083
|
function findSkillActivationMatches(text) {
|
|
10080
|
-
return Array.from(text.matchAll(
|
|
10084
|
+
return Array.from(text.matchAll(createSkillActivationRegex())).flatMap((match, index) => {
|
|
10081
10085
|
const start = match.index;
|
|
10082
10086
|
const command = match[1];
|
|
10083
10087
|
if (start === undefined || !command)
|
|
@@ -10094,6 +10098,25 @@ function findSkillActivationMatches(text) {
|
|
|
10094
10098
|
});
|
|
10095
10099
|
}
|
|
10096
10100
|
|
|
10101
|
+
// ../media-studio-contract/src/sessions/derive-first-turn-note.ts
|
|
10102
|
+
var DEFAULT_FALLBACK_TITLE = "Agent Task";
|
|
10103
|
+
var MAX_NOTE_LENGTH = 64;
|
|
10104
|
+
function deriveFirstTurnNote(rawText, options) {
|
|
10105
|
+
const fallback = options?.fallbackTitle ?? DEFAULT_FALLBACK_TITLE;
|
|
10106
|
+
if (!rawText) {
|
|
10107
|
+
return fallback;
|
|
10108
|
+
}
|
|
10109
|
+
const unwrapped = rawText.replace(/\$--model\s+"([^"$]+)"[^$]*\$/gi, "$1 ").replace(/\$--model\s+([^$\s]+)[^$]*\$/gi, "$1 ").replace(SKILL_ACTIVATION_RE, "$1").replace(ATTACHMENT_MARKER_REGEX, "$1").replace(/\s+/g, " ").trim();
|
|
10110
|
+
if (!unwrapped) {
|
|
10111
|
+
return fallback;
|
|
10112
|
+
}
|
|
10113
|
+
const codePoints = Array.from(unwrapped);
|
|
10114
|
+
if (codePoints.length > MAX_NOTE_LENGTH) {
|
|
10115
|
+
return codePoints.slice(0, MAX_NOTE_LENGTH).join("");
|
|
10116
|
+
}
|
|
10117
|
+
return unwrapped;
|
|
10118
|
+
}
|
|
10119
|
+
|
|
10097
10120
|
// ../media-studio-contract/src/skills/compose-turn.ts
|
|
10098
10121
|
function applySkillActivations(text, spans) {
|
|
10099
10122
|
const ordered = spans.toSorted((a, b) => b.start - a.start);
|
|
@@ -16875,9 +16898,7 @@ function validateApiBaseUrl(urlString) {
|
|
|
16875
16898
|
}
|
|
16876
16899
|
var AuthDataSchema = object2({
|
|
16877
16900
|
token: string4().min(1),
|
|
16878
|
-
apiUrl: string4().
|
|
16879
|
-
message: "Untrusted API URL in auth file"
|
|
16880
|
-
}).optional(),
|
|
16901
|
+
apiUrl: string4().optional(),
|
|
16881
16902
|
uid: string4().optional(),
|
|
16882
16903
|
username: string4().optional(),
|
|
16883
16904
|
email: string4().optional(),
|
|
@@ -16891,11 +16912,35 @@ var ConfigFileSchema = object2({
|
|
|
16891
16912
|
latest: string4().optional()
|
|
16892
16913
|
}).optional()
|
|
16893
16914
|
});
|
|
16915
|
+
|
|
16916
|
+
class UntrustedCredentialError extends Error {
|
|
16917
|
+
rejectedUrl;
|
|
16918
|
+
constructor(rejectedUrl) {
|
|
16919
|
+
super(`Untrusted apiUrl in the saved credential: "${rejectedUrl}". ` + `Allowed hosts: ${MEDIA_STUDIO_HOSTS.join(", ")}. ` + `Run \`msc login\` to sign in - it opens a browser, so a human must complete it.`);
|
|
16920
|
+
this.name = "UntrustedCredentialError";
|
|
16921
|
+
this.rejectedUrl = rejectedUrl;
|
|
16922
|
+
}
|
|
16923
|
+
}
|
|
16894
16924
|
function readAuthFile() {
|
|
16925
|
+
let raw;
|
|
16895
16926
|
try {
|
|
16896
|
-
|
|
16897
|
-
|
|
16898
|
-
return
|
|
16927
|
+
raw = JSON.parse(readFileSync(getAuthFilePath(), "utf8"));
|
|
16928
|
+
} catch {
|
|
16929
|
+
return null;
|
|
16930
|
+
}
|
|
16931
|
+
const shape = AuthDataSchema.safeParse(raw);
|
|
16932
|
+
if (!shape.success || !shape.data.token.trim()) {
|
|
16933
|
+
return null;
|
|
16934
|
+
}
|
|
16935
|
+
const savedUrl = shape.data.apiUrl?.trim();
|
|
16936
|
+
if (savedUrl && !isAllowedCliApiOrigin(savedUrl)) {
|
|
16937
|
+
throw new UntrustedCredentialError(savedUrl);
|
|
16938
|
+
}
|
|
16939
|
+
return shape.data;
|
|
16940
|
+
}
|
|
16941
|
+
function readAuthFileIfUsable() {
|
|
16942
|
+
try {
|
|
16943
|
+
return readAuthFile();
|
|
16899
16944
|
} catch {
|
|
16900
16945
|
return null;
|
|
16901
16946
|
}
|
|
@@ -16949,7 +16994,8 @@ function saveUpdateCheck(check) {
|
|
|
16949
16994
|
writeConfigFile({ updateCheck: check });
|
|
16950
16995
|
}
|
|
16951
16996
|
function resolveApiKey() {
|
|
16952
|
-
const
|
|
16997
|
+
const auth = readAuthFile();
|
|
16998
|
+
const token = auth?.token;
|
|
16953
16999
|
if (!token?.trim()) {
|
|
16954
17000
|
throw new Error("Missing authentication. Run `msc login` to sign in - it opens a browser, so a human " + "must complete it. Instant if that browser is already signed in to the web app.");
|
|
16955
17001
|
}
|
|
@@ -16958,7 +17004,7 @@ function resolveApiKey() {
|
|
|
16958
17004
|
function resolveApiBaseUrl(flag) {
|
|
16959
17005
|
if (flag)
|
|
16960
17006
|
return validateApiBaseUrl(flag);
|
|
16961
|
-
const fromAuth =
|
|
17007
|
+
const fromAuth = readAuthFileIfUsable()?.apiUrl;
|
|
16962
17008
|
if (typeof fromAuth === "string" && fromAuth.trim()) {
|
|
16963
17009
|
if (isAllowedCliApiOrigin(fromAuth)) {
|
|
16964
17010
|
return fromAuth;
|
|
@@ -16969,6 +17015,7 @@ function resolveApiBaseUrl(flag) {
|
|
|
16969
17015
|
|
|
16970
17016
|
// src/client/session-types.ts
|
|
16971
17017
|
var SessionSourceSchema = _enum2(["web", "cli"]);
|
|
17018
|
+
var CLI_DEFAULT_SESSION_NOTE = "Agent Task";
|
|
16972
17019
|
var ApiResponseBaseSchema = object2({
|
|
16973
17020
|
success: boolean4(),
|
|
16974
17021
|
error: object2({
|
|
@@ -17438,7 +17485,7 @@ class MediaStudioClient {
|
|
|
17438
17485
|
const nextKey = rotatedToken.trim();
|
|
17439
17486
|
this._apiKey = nextKey;
|
|
17440
17487
|
try {
|
|
17441
|
-
const authData =
|
|
17488
|
+
const authData = readAuthFileIfUsable();
|
|
17442
17489
|
if (authData) {
|
|
17443
17490
|
saveAuthFile({
|
|
17444
17491
|
...authData,
|
|
@@ -17761,10 +17808,50 @@ var NEXT_STEP_GUIDANCE = {
|
|
|
17761
17808
|
retry: "The failure looks transient. Retrying the same request may succeed.",
|
|
17762
17809
|
none: ""
|
|
17763
17810
|
};
|
|
17811
|
+
var TRANSPORT_CAUSE_DEPTH = 5;
|
|
17812
|
+
var TRANSPORT_MESSAGE = /fetch failed|Unable to connect/i;
|
|
17813
|
+
var ERRNO_SHAPE = /^[A-Z][A-Z0-9_]*$/;
|
|
17814
|
+
function isTerminalControl(codePoint) {
|
|
17815
|
+
return codePoint <= 8 && codePoint !== 9 || codePoint >= 14 && codePoint <= 31 || codePoint >= 127 && codePoint <= 159 || codePoint === 8206 || codePoint === 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8294 && codePoint <= 8297;
|
|
17816
|
+
}
|
|
17817
|
+
function stripTerminalControls(text) {
|
|
17818
|
+
let out = "";
|
|
17819
|
+
for (const char of text) {
|
|
17820
|
+
if (!isTerminalControl(char.codePointAt(0) ?? -1))
|
|
17821
|
+
out += char;
|
|
17822
|
+
}
|
|
17823
|
+
return out;
|
|
17824
|
+
}
|
|
17825
|
+
function detailOf(node, code) {
|
|
17826
|
+
if (!("message" in node))
|
|
17827
|
+
return "";
|
|
17828
|
+
const { message } = node;
|
|
17829
|
+
if (typeof message !== "string")
|
|
17830
|
+
return "";
|
|
17831
|
+
return stripTerminalControls(message.split(code).join(" ")).replace(/\s+/g, " ").trim().replace(/^[:-]\s*/, "");
|
|
17832
|
+
}
|
|
17833
|
+
function findTransportErrno(err) {
|
|
17834
|
+
let current = err;
|
|
17835
|
+
for (let depth = 0;depth < TRANSPORT_CAUSE_DEPTH; depth++) {
|
|
17836
|
+
if (!(current instanceof Error))
|
|
17837
|
+
return null;
|
|
17838
|
+
const code = "code" in current ? current.code : undefined;
|
|
17839
|
+
const errno = typeof code === "string" && ERRNO_SHAPE.test(code) ? code : null;
|
|
17840
|
+
if (errno)
|
|
17841
|
+
return { code: errno, detail: detailOf(current, errno) };
|
|
17842
|
+
current = current.cause;
|
|
17843
|
+
}
|
|
17844
|
+
return null;
|
|
17845
|
+
}
|
|
17846
|
+
function isTransportFailure(err) {
|
|
17847
|
+
return findTransportErrno(err) !== null || err instanceof Error && TRANSPORT_MESSAGE.test(err.message);
|
|
17848
|
+
}
|
|
17764
17849
|
function reportCommandError(err, options) {
|
|
17765
|
-
const
|
|
17850
|
+
const plain = err instanceof Error ? err.message : String(err);
|
|
17851
|
+
const transport = findTransportErrno(err);
|
|
17852
|
+
const message = transport && !plain.includes(transport.code) ? `${plain} (${transport.code}${transport.detail ? `: ${transport.detail}` : ""})` : plain;
|
|
17766
17853
|
const upstream = err instanceof ChatUpstreamError ? err : null;
|
|
17767
|
-
const nextStep = upstream?.nextStep ?? (upstream?.status === 401 ? "relogin" : null);
|
|
17854
|
+
const nextStep = upstream?.nextStep ?? (upstream?.status === 401 || err instanceof UntrustedCredentialError ? "relogin" : null);
|
|
17768
17855
|
const status = upstream?.status !== undefined && upstream.status !== 0 ? upstream.status : null;
|
|
17769
17856
|
const guidance = nextStep ? NEXT_STEP_GUIDANCE[nextStep] : "";
|
|
17770
17857
|
if (options?.json) {
|
|
@@ -17774,7 +17861,8 @@ function reportCommandError(err, options) {
|
|
|
17774
17861
|
...upstream?.errCode != null ? { errCode: upstream.errCode } : {},
|
|
17775
17862
|
...status != null ? { httpStatus: status } : {},
|
|
17776
17863
|
...nextStep != null ? { nextStep } : {},
|
|
17777
|
-
...guidance ? { guidance } : {}
|
|
17864
|
+
...guidance ? { guidance } : {},
|
|
17865
|
+
...transport ? { transportCode: transport.code } : {}
|
|
17778
17866
|
};
|
|
17779
17867
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}
|
|
17780
17868
|
`);
|
|
@@ -18003,6 +18091,16 @@ function isSessionGoneError(err) {
|
|
|
18003
18091
|
return true;
|
|
18004
18092
|
return err.body.includes(ERR_SESSION_EXPIRED);
|
|
18005
18093
|
}
|
|
18094
|
+
|
|
18095
|
+
class SessionNotFoundError extends Error {
|
|
18096
|
+
sessionId;
|
|
18097
|
+
constructor(sessionId) {
|
|
18098
|
+
super(`Session ${sessionId} does not exist.
|
|
18099
|
+
` + ' → Create one: `msc sessions use --new`, or `msc chat --new "<prompt>"` to also send a prompt.');
|
|
18100
|
+
this.name = "SessionNotFoundError";
|
|
18101
|
+
this.sessionId = sessionId;
|
|
18102
|
+
}
|
|
18103
|
+
}
|
|
18006
18104
|
async function ensureSessionExists(options) {
|
|
18007
18105
|
const client = new MediaStudioClient({
|
|
18008
18106
|
baseUrl: options.apiBaseUrl,
|
|
@@ -18010,8 +18108,7 @@ async function ensureSessionExists(options) {
|
|
|
18010
18108
|
});
|
|
18011
18109
|
if (await client.sessions.exists(options.sessionId))
|
|
18012
18110
|
return;
|
|
18013
|
-
throw new
|
|
18014
|
-
` + ' → Create one: `msc sessions use --new`, or `msc chat --new "<prompt>"` to also send a prompt.');
|
|
18111
|
+
throw new SessionNotFoundError(options.sessionId);
|
|
18015
18112
|
}
|
|
18016
18113
|
function finalizeProvision(result, spinner) {
|
|
18017
18114
|
spinner?.stop();
|
|
@@ -18032,6 +18129,7 @@ async function provisionSession(options) {
|
|
|
18032
18129
|
const outcome = await client.sessions.prewarm({
|
|
18033
18130
|
sessionId,
|
|
18034
18131
|
source: "cli",
|
|
18132
|
+
note: options.note ?? CLI_DEFAULT_SESSION_NOTE,
|
|
18035
18133
|
onSessionReady: () => saveLastSessionId(sessionId),
|
|
18036
18134
|
onStageChange: options.onStageChange
|
|
18037
18135
|
});
|
|
@@ -18094,7 +18192,7 @@ async function ensureUsable(client, skill) {
|
|
|
18094
18192
|
});
|
|
18095
18193
|
if (ensured)
|
|
18096
18194
|
return;
|
|
18097
|
-
throw new SkillResolutionError(describeEnableFailure(skill, cause));
|
|
18195
|
+
throw new SkillResolutionError(describeEnableFailure(skill, cause), cause ? { cause } : undefined);
|
|
18098
18196
|
}
|
|
18099
18197
|
async function resolvePromptSkills(options) {
|
|
18100
18198
|
const spans = findSkillRefSpans(options.prompt);
|
|
@@ -18167,11 +18265,21 @@ function printTerminalMediaAssets(mediaAssets, fullText) {
|
|
|
18167
18265
|
process.stdout.write(`====================
|
|
18168
18266
|
`);
|
|
18169
18267
|
}
|
|
18268
|
+
function buildNotePrompt(prompt, attachments) {
|
|
18269
|
+
return prompt.replace(ATTACHMENT_MARKER_REGEX, (marker, url) => {
|
|
18270
|
+
const attachment = attachments.find((a) => a.remoteUrl === url);
|
|
18271
|
+
return attachment ? attachment.name || attachment.remoteUrl : marker;
|
|
18272
|
+
});
|
|
18273
|
+
}
|
|
18170
18274
|
async function provisionNewSession(options) {
|
|
18171
18275
|
const onStageChange = options.quiet ? undefined : (stage) => options.spinner.start(PROVISION_STAGE_LABELS[stage]);
|
|
18276
|
+
const note = deriveFirstTurnNote(options.prompt, {
|
|
18277
|
+
fallbackTitle: CLI_DEFAULT_SESSION_NOTE
|
|
18278
|
+
});
|
|
18172
18279
|
const result = await provisionSession({
|
|
18173
18280
|
apiBaseUrl: options.apiBaseUrl,
|
|
18174
18281
|
apiKey: options.apiKey,
|
|
18282
|
+
note,
|
|
18175
18283
|
onStageChange
|
|
18176
18284
|
});
|
|
18177
18285
|
return finalizeProvision(result, options.spinner);
|
|
@@ -18179,7 +18287,10 @@ async function provisionNewSession(options) {
|
|
|
18179
18287
|
async function resolveChatSession(options) {
|
|
18180
18288
|
if (options.newSession) {
|
|
18181
18289
|
return {
|
|
18182
|
-
sessionId: await provisionNewSession(
|
|
18290
|
+
sessionId: await provisionNewSession({
|
|
18291
|
+
...options,
|
|
18292
|
+
prompt: options.prompt
|
|
18293
|
+
}),
|
|
18183
18294
|
recreateOnGone: false
|
|
18184
18295
|
};
|
|
18185
18296
|
}
|
|
@@ -18192,10 +18303,22 @@ async function resolveChatSession(options) {
|
|
|
18192
18303
|
return { sessionId: pointer, recreateOnGone: true };
|
|
18193
18304
|
}
|
|
18194
18305
|
return {
|
|
18195
|
-
sessionId: await provisionNewSession(
|
|
18306
|
+
sessionId: await provisionNewSession({
|
|
18307
|
+
...options,
|
|
18308
|
+
prompt: options.prompt
|
|
18309
|
+
}),
|
|
18196
18310
|
recreateOnGone: false
|
|
18197
18311
|
};
|
|
18198
18312
|
}
|
|
18313
|
+
function describeStreamFailure(err) {
|
|
18314
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
18315
|
+
return "aborted";
|
|
18316
|
+
}
|
|
18317
|
+
if (err instanceof Error && err.message) {
|
|
18318
|
+
return err.message;
|
|
18319
|
+
}
|
|
18320
|
+
return String(err);
|
|
18321
|
+
}
|
|
18199
18322
|
async function streamTurn(options) {
|
|
18200
18323
|
const { args, sessionId, spinner, autoEnabled, attachments } = options;
|
|
18201
18324
|
if (!args.json) {
|
|
@@ -18215,87 +18338,103 @@ async function streamTurn(options) {
|
|
|
18215
18338
|
const steps = [];
|
|
18216
18339
|
const mediaAssets = [];
|
|
18217
18340
|
let hasFinished = false;
|
|
18218
|
-
|
|
18219
|
-
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
|
|
18223
|
-
|
|
18224
|
-
spinner.stop();
|
|
18225
|
-
fullText += delta;
|
|
18226
|
-
if (!args.json) {
|
|
18227
|
-
process.stdout.write(delta);
|
|
18341
|
+
let emitting = false;
|
|
18342
|
+
let streamFailure;
|
|
18343
|
+
try {
|
|
18344
|
+
for await (const ev of stream) {
|
|
18345
|
+
if (ev.type !== "finish" && ev.type !== "error") {
|
|
18346
|
+
options.progress.emitted = true;
|
|
18228
18347
|
}
|
|
18229
|
-
|
|
18230
|
-
|
|
18231
|
-
if (args["show-reasoning"] && !args.json) {
|
|
18348
|
+
M(ev).with({ type: "text-delta", delta: z2.string }, ({ delta }) => {
|
|
18349
|
+
isThinking = false;
|
|
18232
18350
|
spinner.stop();
|
|
18233
|
-
|
|
18234
|
-
|
|
18235
|
-
|
|
18236
|
-
|
|
18351
|
+
fullText += delta;
|
|
18352
|
+
if (!args.json) {
|
|
18353
|
+
process.stdout.write(delta);
|
|
18354
|
+
}
|
|
18355
|
+
}).with({ type: "reasoning-delta", delta: z2.string }, ({ delta }) => {
|
|
18356
|
+
reasoning += delta;
|
|
18357
|
+
if (args["show-reasoning"] && !args.json) {
|
|
18237
18358
|
spinner.stop();
|
|
18238
|
-
|
|
18359
|
+
process.stdout.write(delta);
|
|
18360
|
+
} else if (!args.json) {
|
|
18361
|
+
if (!isThinking) {
|
|
18362
|
+
isThinking = true;
|
|
18363
|
+
spinner.stop();
|
|
18364
|
+
if (fullText && !fullText.endsWith(`
|
|
18239
18365
|
`)) {
|
|
18240
|
-
|
|
18366
|
+
process.stdout.write(`
|
|
18241
18367
|
`);
|
|
18242
|
-
|
|
18368
|
+
fullText += `
|
|
18243
18369
|
`;
|
|
18244
|
-
|
|
18245
|
-
|
|
18370
|
+
}
|
|
18371
|
+
process.stdout.write(`[reasoning] Thinking...
|
|
18246
18372
|
`);
|
|
18247
|
-
|
|
18373
|
+
spinner.start("Thinking...");
|
|
18374
|
+
}
|
|
18248
18375
|
}
|
|
18249
|
-
}
|
|
18250
|
-
|
|
18251
|
-
|
|
18252
|
-
|
|
18253
|
-
if (fullText && !fullText.endsWith(`
|
|
18376
|
+
}).with({ type: "tool-call", toolName: z2.string }, ({ toolName, args: toolArgs }) => {
|
|
18377
|
+
isThinking = false;
|
|
18378
|
+
spinner.stop();
|
|
18379
|
+
if (fullText && !fullText.endsWith(`
|
|
18254
18380
|
`) && !args.json) {
|
|
18255
|
-
|
|
18381
|
+
process.stdout.write(`
|
|
18256
18382
|
`);
|
|
18257
|
-
|
|
18383
|
+
fullText += `
|
|
18258
18384
|
`;
|
|
18259
|
-
|
|
18260
|
-
|
|
18261
|
-
|
|
18262
|
-
|
|
18263
|
-
|
|
18385
|
+
}
|
|
18386
|
+
const formatted = formatToolMilestone(toolName, toolArgs);
|
|
18387
|
+
steps.push(formatted.milestoneLine);
|
|
18388
|
+
if (!args.json) {
|
|
18389
|
+
process.stdout.write(`${formatted.milestoneLine}
|
|
18264
18390
|
`);
|
|
18265
|
-
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
|
|
18269
|
-
|
|
18270
|
-
|
|
18271
|
-
|
|
18272
|
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
|
|
18278
|
-
|
|
18279
|
-
|
|
18280
|
-
|
|
18281
|
-
|
|
18282
|
-
|
|
18283
|
-
|
|
18284
|
-
|
|
18285
|
-
|
|
18286
|
-
|
|
18287
|
-
|
|
18288
|
-
|
|
18391
|
+
spinner.start(formatted.actionLabel);
|
|
18392
|
+
}
|
|
18393
|
+
}).with({ type: "media-result" }, ({ result }) => {
|
|
18394
|
+
spinner.stop();
|
|
18395
|
+
addOrMergeMediaResult(mediaAssets, result);
|
|
18396
|
+
}).with({ type: "error" }, (ev) => {
|
|
18397
|
+
spinner.stop();
|
|
18398
|
+
const raw = ("error" in ev && typeof ev.error === "string" ? ev.error : null) ?? ("message" in ev && typeof ev.message === "string" ? ev.message : null) ?? "Upstream stream error";
|
|
18399
|
+
throw new ChatUpstreamError(extractUpstreamErrorMessage(raw) || raw, 0, raw);
|
|
18400
|
+
}).with({ type: "finish" }, () => {
|
|
18401
|
+
emitting = true;
|
|
18402
|
+
hasFinished = true;
|
|
18403
|
+
spinner.stop();
|
|
18404
|
+
if (args.json) {
|
|
18405
|
+
const output = {
|
|
18406
|
+
status: "success",
|
|
18407
|
+
sessionId,
|
|
18408
|
+
text: fullText,
|
|
18409
|
+
steps,
|
|
18410
|
+
media: mediaAssets,
|
|
18411
|
+
...attachments.length > 0 ? { attachments } : {},
|
|
18412
|
+
...autoEnabled.length > 0 ? { autoEnabled } : {},
|
|
18413
|
+
...args["show-reasoning"] && reasoning ? { reasoning } : {}
|
|
18414
|
+
};
|
|
18415
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}
|
|
18289
18416
|
`);
|
|
18290
|
-
|
|
18291
|
-
|
|
18292
|
-
|
|
18293
|
-
|
|
18417
|
+
emitting = false;
|
|
18418
|
+
return;
|
|
18419
|
+
}
|
|
18420
|
+
printTerminalMediaAssets(mediaAssets, fullText);
|
|
18421
|
+
process.stdout.write(`session: ${sessionId}
|
|
18294
18422
|
`);
|
|
18295
|
-
|
|
18423
|
+
emitting = false;
|
|
18424
|
+
}).with(z2._, () => {}).exhaustive();
|
|
18425
|
+
}
|
|
18426
|
+
} catch (err) {
|
|
18427
|
+
if (err instanceof ChatUpstreamError)
|
|
18428
|
+
throw err;
|
|
18429
|
+
if (emitting)
|
|
18430
|
+
throw err;
|
|
18431
|
+
if (!hasFinished && !options.progress.emitted)
|
|
18432
|
+
throw err;
|
|
18433
|
+
streamFailure = err;
|
|
18296
18434
|
}
|
|
18297
18435
|
if (!hasFinished) {
|
|
18298
18436
|
spinner.stop();
|
|
18437
|
+
consola.warn(streamFailure === undefined ? "Stream ended early: no finish event" : `Stream ended early: ${describeStreamFailure(streamFailure)}`);
|
|
18299
18438
|
if (args.json) {
|
|
18300
18439
|
const output = {
|
|
18301
18440
|
status: "incomplete",
|
|
@@ -18321,30 +18460,48 @@ async function runTurn(options) {
|
|
|
18321
18460
|
await streamTurn(options);
|
|
18322
18461
|
} catch (err) {
|
|
18323
18462
|
const sessionGone = isSessionGoneError(err);
|
|
18324
|
-
|
|
18463
|
+
const ambiguousGone = err instanceof ChatUpstreamError && err.errCode === ErrCode.INVALID_PARAM_FORMAT;
|
|
18464
|
+
if (!sessionGone && !ambiguousGone) {
|
|
18465
|
+
throw err;
|
|
18466
|
+
}
|
|
18467
|
+
if (options.progress.emitted) {
|
|
18325
18468
|
throw new Error(`Session ${options.sessionId} failed after this turn had already started.
|
|
18326
18469
|
` + " → The partial result above stands; re-run the same prompt to continue in a new session.");
|
|
18327
18470
|
}
|
|
18328
|
-
if (
|
|
18329
|
-
|
|
18330
|
-
|
|
18471
|
+
if (!options.recreateOnGone) {
|
|
18472
|
+
if (sessionGone || options.args.session) {
|
|
18473
|
+
throw new SessionNotFoundError(options.sessionId);
|
|
18474
|
+
}
|
|
18475
|
+
throw err;
|
|
18331
18476
|
}
|
|
18332
|
-
if (
|
|
18333
|
-
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
|
|
18338
|
-
|
|
18339
|
-
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18477
|
+
if (ambiguousGone) {
|
|
18478
|
+
let confirmedGone = false;
|
|
18479
|
+
try {
|
|
18480
|
+
await ensureSessionExists({
|
|
18481
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
18482
|
+
apiKey: resolveApiKey(),
|
|
18483
|
+
sessionId: options.sessionId
|
|
18484
|
+
});
|
|
18485
|
+
} catch (checkErr) {
|
|
18486
|
+
confirmedGone = checkErr instanceof SessionNotFoundError;
|
|
18487
|
+
}
|
|
18488
|
+
if (!confirmedGone) {
|
|
18489
|
+
throw err;
|
|
18490
|
+
}
|
|
18346
18491
|
}
|
|
18347
|
-
|
|
18492
|
+
consola.warn(`Session ${options.sessionId} is no longer available. Provisioning a new one...`);
|
|
18493
|
+
const sessionId = await provisionNewSession({
|
|
18494
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
18495
|
+
apiKey: resolveApiKey(),
|
|
18496
|
+
spinner: options.spinner,
|
|
18497
|
+
quiet: options.args.json === true,
|
|
18498
|
+
prompt: buildNotePrompt(options.prompt, options.attachments)
|
|
18499
|
+
});
|
|
18500
|
+
await streamTurn({
|
|
18501
|
+
...options,
|
|
18502
|
+
sessionId,
|
|
18503
|
+
apiKey: resolveApiKey()
|
|
18504
|
+
});
|
|
18348
18505
|
}
|
|
18349
18506
|
}
|
|
18350
18507
|
var chatCommand = defineCommand({
|
|
@@ -18410,6 +18567,7 @@ var chatCommand = defineCommand({
|
|
|
18410
18567
|
const resolved = await resolveChatSession({
|
|
18411
18568
|
sessionFlag: args.session,
|
|
18412
18569
|
newSession: args.new,
|
|
18570
|
+
prompt: buildNotePrompt(promptWithAttachments, attachments),
|
|
18413
18571
|
apiBaseUrl,
|
|
18414
18572
|
apiKey: resolveApiKey(),
|
|
18415
18573
|
spinner,
|
|
@@ -18477,7 +18635,11 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18477
18635
|
|
|
18478
18636
|
msc whoami
|
|
18479
18637
|
Displays your current account information (user, email, UID, server URL).
|
|
18480
|
-
Exits 0
|
|
18638
|
+
Exits 0 only when the platform confirmed the session, and the fields it
|
|
18639
|
+
prints are the platform's answer. Exits 1 otherwise - unauthenticated,
|
|
18640
|
+
expired, or the request never reached the platform (DNS, refused, TLS):
|
|
18641
|
+
a saved credential describes a session that was never checked, so it is
|
|
18642
|
+
never printed in place of an answer.
|
|
18481
18643
|
|
|
18482
18644
|
msc logout
|
|
18483
18645
|
Removes saved credentials on this machine.
|
|
@@ -18543,7 +18705,7 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18543
18705
|
msc skills add <slug> # add a skill from the plaza to your library
|
|
18544
18706
|
|
|
18545
18707
|
Referencing Skills in Prompts:
|
|
18546
|
-
Lead the prompt with your skill reference
|
|
18708
|
+
Lead the prompt with your skill reference - the message must start with it
|
|
18547
18709
|
for the skill to activate:
|
|
18548
18710
|
Slash form: msc chat "/<name> your prompt here"
|
|
18549
18711
|
Marker form: msc chat "%%/<name>%% your prompt here"
|
|
@@ -18619,15 +18781,82 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url}`).join(`
|
|
|
18619
18781
|
Extracting the result URL (e.g. with jq):
|
|
18620
18782
|
url=$(msc chat "a red balloon" --json | jq -r '.media[0].resultUrl')
|
|
18621
18783
|
|
|
18784
|
+
Exit codes: 0 = the turn ran to completion OR its stream ended early; 1 =
|
|
18785
|
+
\`status: "error"\`, a usage error, or any other failure. Only what the parser
|
|
18786
|
+
itself rejects (unknown flag, missing \`<prompt>\`) exits 1 before the command
|
|
18787
|
+
runs with an empty stdout; argument conflicts checked inside the command --
|
|
18788
|
+
\`--new\` with \`--session\`, \`sessions use --new <id>\` -- do print the error
|
|
18789
|
+
payload below. Either way, check the exit code first, then branch on
|
|
18790
|
+
\`.status\`.
|
|
18791
|
+
Exit code 1 does not imply a payload. A bare transport failure -- the host
|
|
18792
|
+
does not resolve, the port is refused, TLS fails -- is printed by the runtime
|
|
18793
|
+
with its \`cause\` chain instead, so \`--json\` stdout stays empty there: to
|
|
18794
|
+
tell "could not reach the platform" from "the platform answered with a
|
|
18795
|
+
failure", read the exit code, then check whether stdout has a payload at all.
|
|
18796
|
+
\`"success" | "incomplete" | "error"\` are \`chat\`-envelope values: \`chat\` is
|
|
18797
|
+
the only command whose *success* payload carries a \`status\`, and every
|
|
18798
|
+
command that supports \`--json\` puts \`"status": "error"\` on its failure
|
|
18799
|
+
payload. \`login\`, \`logout\` and \`whoami\` define no \`--json\` and emit no
|
|
18800
|
+
payload at all -- their failures are prose on stderr with exit 1. Do not
|
|
18801
|
+
reuse that vocabulary elsewhere -- \`sessions --json\` items carry
|
|
18802
|
+
\`status: { chat, task }\` (a boolean pair) and \`sessions artifacts <id> --json\`
|
|
18803
|
+
items carry the task lifecycle
|
|
18804
|
+
(\`pending | running | processing | completed | failed | cancelled\`).
|
|
18805
|
+
Human stdout carries no status marker -- an early end only warns on
|
|
18806
|
+
stderr -- so never parse it in scripts.
|
|
18807
|
+
|
|
18622
18808
|
Error schema on failure (exit code 1):
|
|
18623
18809
|
{
|
|
18624
18810
|
"status": "error",
|
|
18625
18811
|
"message": "string", // human-readable error description
|
|
18626
18812
|
"errCode": 12345, // optional platform error code
|
|
18627
18813
|
"httpStatus": 401, // optional HTTP status code
|
|
18628
|
-
"nextStep": "relogin"
|
|
18814
|
+
"nextStep": "relogin", // recommended action keyword
|
|
18815
|
+
"guidance": "string?", // present when nextStep names an action;
|
|
18816
|
+
// absent for nextStep "none"
|
|
18817
|
+
"transportCode": "ECONNREFUSED" // optional: errno of a transport failure
|
|
18818
|
+
// the error wraps, when it had one
|
|
18629
18819
|
}
|
|
18630
18820
|
|
|
18821
|
+
Classifying a failure (branch on these fields, not on message prose):
|
|
18822
|
+
A consumer reads \`errCode\`, \`httpStatus\`, \`nextStep\` and
|
|
18823
|
+
\`transportCode\` - never the prose in \`message\`, which is written for a
|
|
18824
|
+
human and may change between versions.
|
|
18825
|
+
\`errCode\`, \`httpStatus\`, \`nextStep\` and \`transportCode\` are optional
|
|
18826
|
+
on *every* failure. All four absent means unclassified: nothing structured
|
|
18827
|
+
to act on, so report the payload as-is.
|
|
18828
|
+
\`transportCode\` is diagnostic, not a branch: it names the errno a
|
|
18829
|
+
transport failure left on the error's cause chain when a wrapper copied
|
|
18830
|
+
only the constant message. A bare transport failure does not reach this
|
|
18831
|
+
reporter at all - the runtime prints it with its cause chain and the
|
|
18832
|
+
command exits 1.
|
|
18833
|
+
|
|
18834
|
+
A rejected saved credential (auth.json's apiUrl is off the CLI allowlist)
|
|
18835
|
+
reports the same error shape with no errCode/httpStatus - nothing came from
|
|
18836
|
+
the platform - and nextStep "relogin", because \`msc login\` rewrites the file:
|
|
18837
|
+
{
|
|
18838
|
+
"status": "error",
|
|
18839
|
+
"message": "Untrusted apiUrl in the saved credential: ...",
|
|
18840
|
+
"nextStep": "relogin",
|
|
18841
|
+
"guidance": "Run \`msc login\` to re-authenticate; ..."
|
|
18842
|
+
}
|
|
18843
|
+
With no credential at all the payload stays minimal (status + message only),
|
|
18844
|
+
so an agent can tell "credential file rejected" from "not logged in" without
|
|
18845
|
+
reading the message.
|
|
18846
|
+
|
|
18847
|
+
A chat turn whose stream is open and then ends before the finish event --
|
|
18848
|
+
the connection dropped, or the response was truncated -- is not a failure: it
|
|
18849
|
+
exits 0 with \`status: "incomplete"\` and whatever
|
|
18850
|
+
\`text\`/\`steps\`/\`media\` had arrived. An early end may therefore carry an
|
|
18851
|
+
empty \`text\` and an empty \`media\` array, so do not treat \`incomplete\` as
|
|
18852
|
+
proof that a partial result exists. This applies once the turn has observed
|
|
18853
|
+
any frame at all; a stream request that fails before that -- the host does not
|
|
18854
|
+
resolve, the port is refused, TLS fails, and no event ever arrived -- never
|
|
18855
|
+
reaches \`incomplete\`: it is \`status: "error"\` with exit 1, carrying the
|
|
18856
|
+
errno in \`transportCode\` (and naming it in \`message\`). A
|
|
18857
|
+
platform error (an upstream \`error\` frame, or a non-2xx response) is likewise
|
|
18858
|
+
\`status: "error"\` with exit 1.
|
|
18859
|
+
|
|
18631
18860
|
Session commands:
|
|
18632
18861
|
msc sessions --json # list: [{ sessionId, note, ... }]
|
|
18633
18862
|
msc sessions use <id> --json # { "sessionId": "string" }
|
|
@@ -18636,7 +18865,8 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url}`).join(`
|
|
|
18636
18865
|
the \`--new\` hint and leaves the default session untouched.
|
|
18637
18866
|
|
|
18638
18867
|
Recommended next steps:
|
|
18639
|
-
relogin Session expired or missing
|
|
18868
|
+
relogin Session expired or missing, or a saved credential was
|
|
18869
|
+
rejected (its apiUrl is off the allowlist); run \`msc login\`
|
|
18640
18870
|
retry_later Rate limit reached; wait before re-sending
|
|
18641
18871
|
top_up_balance Insufficient balance; top up your account
|
|
18642
18872
|
fix_input Invalid parameters or prompt; adjust input
|
|
@@ -19549,7 +19779,7 @@ function applyForHost(urlString) {
|
|
|
19549
19779
|
return true;
|
|
19550
19780
|
}
|
|
19551
19781
|
function applyInsecureTlsIfRequested() {
|
|
19552
|
-
const target =
|
|
19782
|
+
const target = readAuthFileIfUsable()?.apiUrl || getDefaultApiBaseUrl();
|
|
19553
19783
|
return applyForHost(target);
|
|
19554
19784
|
}
|
|
19555
19785
|
function applyInsecureTlsForUrl(urlString) {
|
|
@@ -20422,37 +20652,36 @@ var loginCommand = defineCommand({
|
|
|
20422
20652
|
}
|
|
20423
20653
|
},
|
|
20424
20654
|
run: async ({ args }) => {
|
|
20425
|
-
|
|
20426
|
-
|
|
20427
|
-
throw new Error(`Browser sign-in needs a graphical session on this machine, so it cannot complete here.
|
|
20655
|
+
if (process.env.CI || process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
|
|
20656
|
+
throw new Error(`Browser sign-in needs a graphical session on this machine, so it cannot complete here.
|
|
20428
20657
|
` + "Run `msc login` from a terminal on a machine whose browser is signed in to Media Studio.\n" + "See `msc guide` for the full authentication flow.");
|
|
20658
|
+
}
|
|
20659
|
+
let targetApiUrl;
|
|
20660
|
+
if (args.region) {
|
|
20661
|
+
targetApiUrl = matchRegionUrl(args.region);
|
|
20662
|
+
} else if (process.env.CI || false || !process.stdin.isTTY) {
|
|
20663
|
+
targetApiUrl = SERVERS.default.url;
|
|
20664
|
+
} else {
|
|
20665
|
+
const answer = await select({
|
|
20666
|
+
message: "Select server:",
|
|
20667
|
+
options: getAvailableRegions().map((opt) => ({
|
|
20668
|
+
label: opt.label,
|
|
20669
|
+
value: opt.url
|
|
20670
|
+
})),
|
|
20671
|
+
initialValue: SERVERS.default.url
|
|
20672
|
+
});
|
|
20673
|
+
if (isCancel(answer)) {
|
|
20674
|
+
process.exit(130);
|
|
20429
20675
|
}
|
|
20430
|
-
|
|
20431
|
-
|
|
20432
|
-
|
|
20433
|
-
|
|
20434
|
-
|
|
20435
|
-
|
|
20436
|
-
|
|
20437
|
-
message: "Select server:",
|
|
20438
|
-
options: getAvailableRegions().map((opt) => ({
|
|
20439
|
-
label: opt.label,
|
|
20440
|
-
value: opt.url
|
|
20441
|
-
})),
|
|
20442
|
-
initialValue: SERVERS.default.url
|
|
20443
|
-
});
|
|
20444
|
-
if (isCancel(answer)) {
|
|
20445
|
-
process.exit(130);
|
|
20446
|
-
}
|
|
20447
|
-
targetApiUrl = String(answer);
|
|
20448
|
-
}
|
|
20449
|
-
applyInsecureTlsForUrl(targetApiUrl);
|
|
20450
|
-
const apiUrl = resolveApiBaseUrl(targetApiUrl);
|
|
20451
|
-
consola.info("Opening browser for sign-in...");
|
|
20452
|
-
if (!process.stdin.isTTY) {
|
|
20453
|
-
const regionList = getAvailableRegions().map((r) => ` - ${r.key.padEnd(8)}: ${r.url} (${r.description})`).join(`
|
|
20676
|
+
targetApiUrl = String(answer);
|
|
20677
|
+
}
|
|
20678
|
+
applyInsecureTlsForUrl(targetApiUrl);
|
|
20679
|
+
const apiUrl = resolveApiBaseUrl(targetApiUrl);
|
|
20680
|
+
consola.info("Opening browser for sign-in...");
|
|
20681
|
+
if (!process.stdin.isTTY) {
|
|
20682
|
+
const regionList = getAvailableRegions().map((r) => ` - ${r.key.padEnd(8)}: ${r.url} (${r.description})`).join(`
|
|
20454
20683
|
`);
|
|
20455
|
-
|
|
20684
|
+
process.stdout.write(`Sign-in needs a human at a browser on this machine:
|
|
20456
20685
|
` + ` 1. already signed in to the Media Studio web app there -> completes at once
|
|
20457
20686
|
` + ` 2. not signed in -> a browser opens; a human finishes it within 120s
|
|
20458
20687
|
` + `
|
|
@@ -20462,29 +20691,24 @@ ${regionList}
|
|
|
20462
20691
|
` + `
|
|
20463
20692
|
` + `If you are an agent, ask the user to sign in to the web app in their browser first
|
|
20464
20693
|
` + "or ask the user to run `msc login` in their own terminal,\n" + "then poll `msc whoami` until it succeeds.\n");
|
|
20465
|
-
|
|
20466
|
-
|
|
20467
|
-
|
|
20468
|
-
|
|
20469
|
-
|
|
20694
|
+
}
|
|
20695
|
+
const result = await runLoopbackLogin({
|
|
20696
|
+
apiUrl,
|
|
20697
|
+
onUrl: (url) => {
|
|
20698
|
+
consola.info(`If your browser does not open automatically, visit:
|
|
20470
20699
|
${url}
|
|
20471
20700
|
` + "Signed in to the Media Studio web app in that browser already? This completes without a prompt.");
|
|
20472
|
-
|
|
20473
|
-
|
|
20474
|
-
|
|
20475
|
-
|
|
20476
|
-
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
|
|
20480
|
-
|
|
20481
|
-
|
|
20482
|
-
|
|
20483
|
-
} catch (err) {
|
|
20484
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
20485
|
-
consola.error(message);
|
|
20486
|
-
process.exit(1);
|
|
20487
|
-
}
|
|
20701
|
+
}
|
|
20702
|
+
});
|
|
20703
|
+
saveAuthFile({
|
|
20704
|
+
token: result.token,
|
|
20705
|
+
apiUrl,
|
|
20706
|
+
uid: result.uid,
|
|
20707
|
+
username: result.username,
|
|
20708
|
+
email: result.email,
|
|
20709
|
+
updated_at: new Date().toISOString()
|
|
20710
|
+
});
|
|
20711
|
+
consola.success("Successfully logged in!");
|
|
20488
20712
|
}
|
|
20489
20713
|
});
|
|
20490
20714
|
|
|
@@ -20509,7 +20733,7 @@ function renderSessionsTable(sessions, currentSessionId) {
|
|
|
20509
20733
|
id: s.id === currentSessionId ? `${s.id} (current)` : s.id,
|
|
20510
20734
|
source: s.source,
|
|
20511
20735
|
lastActive: formatLastActive(s.lastActiveAt),
|
|
20512
|
-
note: s.note ? s.note.replace(/\r?\n/g, " ").trim() :
|
|
20736
|
+
note: s.note ? s.note.replace(/\r?\n/g, " ").trim() || CLI_DEFAULT_SESSION_NOTE : CLI_DEFAULT_SESSION_NOTE
|
|
20513
20737
|
}));
|
|
20514
20738
|
const headers = {
|
|
20515
20739
|
id: "ID",
|
|
@@ -20582,6 +20806,8 @@ var listSessionsCommand = defineCommand({
|
|
|
20582
20806
|
const currentId = readLastSessionId();
|
|
20583
20807
|
consola.log(renderSessionsTable(response.data, currentId));
|
|
20584
20808
|
} catch (err) {
|
|
20809
|
+
if (isTransportFailure(err))
|
|
20810
|
+
throw err;
|
|
20585
20811
|
reportCommandError(err, { json: args.json });
|
|
20586
20812
|
}
|
|
20587
20813
|
}
|
|
@@ -20620,6 +20846,8 @@ var artifactsCommand = defineCommand({
|
|
|
20620
20846
|
}
|
|
20621
20847
|
consola.log(renderArtifactsTable(response.data));
|
|
20622
20848
|
} catch (err) {
|
|
20849
|
+
if (isTransportFailure(err))
|
|
20850
|
+
throw err;
|
|
20623
20851
|
reportCommandError(err, { json: args.json });
|
|
20624
20852
|
}
|
|
20625
20853
|
}
|
|
@@ -20655,7 +20883,8 @@ var useSessionCommand = defineCommand({
|
|
|
20655
20883
|
}
|
|
20656
20884
|
const result = await provisionSession({
|
|
20657
20885
|
apiBaseUrl,
|
|
20658
|
-
apiKey: resolveApiKey()
|
|
20886
|
+
apiKey: resolveApiKey(),
|
|
20887
|
+
note: CLI_DEFAULT_SESSION_NOTE
|
|
20659
20888
|
});
|
|
20660
20889
|
const created = finalizeProvision(result);
|
|
20661
20890
|
if (args.json) {
|
|
@@ -20682,6 +20911,8 @@ var useSessionCommand = defineCommand({
|
|
|
20682
20911
|
}
|
|
20683
20912
|
consola.success(`Switched default session to ${sessionId}`);
|
|
20684
20913
|
} catch (err) {
|
|
20914
|
+
if (isTransportFailure(err))
|
|
20915
|
+
throw err;
|
|
20685
20916
|
reportCommandError(err, { json: args.json });
|
|
20686
20917
|
}
|
|
20687
20918
|
}
|
|
@@ -20758,6 +20989,8 @@ var addSkillCommand = defineCommand({
|
|
|
20758
20989
|
}
|
|
20759
20990
|
consola.success(`Installed ${stripControlChars(skill.command)} (${stripControlChars(skill.name)}).`);
|
|
20760
20991
|
} catch (err) {
|
|
20992
|
+
if (isTransportFailure(err))
|
|
20993
|
+
throw err;
|
|
20761
20994
|
reportCommandError(err, { json: args.json });
|
|
20762
20995
|
}
|
|
20763
20996
|
}
|
|
@@ -20809,6 +21042,8 @@ var rmSkillCommand = defineCommand({
|
|
|
20809
21042
|
}
|
|
20810
21043
|
consola.success(`Skill "${args["skill-command"]}" removed from your library.`);
|
|
20811
21044
|
} catch (err) {
|
|
21045
|
+
if (isTransportFailure(err))
|
|
21046
|
+
throw err;
|
|
20812
21047
|
reportCommandError(err, { json: args.json });
|
|
20813
21048
|
}
|
|
20814
21049
|
}
|
|
@@ -20840,6 +21075,8 @@ function makeSetEnabledCommand(name, enabled, description) {
|
|
|
20840
21075
|
}
|
|
20841
21076
|
consola.success(`${enabled ? "Enabled" : "Disabled"} ${stripControlChars(skill.command)}.`);
|
|
20842
21077
|
} catch (err) {
|
|
21078
|
+
if (isTransportFailure(err))
|
|
21079
|
+
throw err;
|
|
20843
21080
|
reportCommandError(err, { json: args.json });
|
|
20844
21081
|
}
|
|
20845
21082
|
}
|
|
@@ -20932,13 +21169,14 @@ var skillsCommand = defineCommand({
|
|
|
20932
21169
|
].join(`
|
|
20933
21170
|
`));
|
|
20934
21171
|
} catch (err) {
|
|
21172
|
+
if (isTransportFailure(err))
|
|
21173
|
+
throw err;
|
|
20935
21174
|
reportCommandError(err, { json: args.json });
|
|
20936
21175
|
}
|
|
20937
21176
|
}
|
|
20938
21177
|
});
|
|
20939
21178
|
|
|
20940
21179
|
// src/commands/whoami.ts
|
|
20941
|
-
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
20942
21180
|
function maskToken(token) {
|
|
20943
21181
|
return `${token.slice(0, 10)}...${token.slice(-6)}`;
|
|
20944
21182
|
}
|
|
@@ -20948,7 +21186,16 @@ var whoamiCommand = defineCommand({
|
|
|
20948
21186
|
description: "Display current authentication status"
|
|
20949
21187
|
},
|
|
20950
21188
|
run: async () => {
|
|
20951
|
-
|
|
21189
|
+
let auth;
|
|
21190
|
+
try {
|
|
21191
|
+
auth = readAuthFile();
|
|
21192
|
+
} catch (err) {
|
|
21193
|
+
consola.error(err instanceof Error ? err.message : String(err));
|
|
21194
|
+
consola.error(`
|
|
21195
|
+
→ ${NEXT_STEP_GUIDANCE.relogin}`);
|
|
21196
|
+
process.exit(1);
|
|
21197
|
+
return;
|
|
21198
|
+
}
|
|
20952
21199
|
if (!auth) {
|
|
20953
21200
|
consola.error("Not logged in. Run `msc login` to authenticate - it opens a browser, so a human must complete it.");
|
|
20954
21201
|
process.exit(1);
|
|
@@ -20970,13 +21217,17 @@ var whoamiCommand = defineCommand({
|
|
|
20970
21217
|
const updatedUid = data.uid ?? auth.uid;
|
|
20971
21218
|
const updatedEmail = data.email ?? auth.email;
|
|
20972
21219
|
if (updatedName !== auth.username || updatedUid !== auth.uid || updatedEmail !== auth.email) {
|
|
20973
|
-
|
|
20974
|
-
|
|
20975
|
-
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
|
|
21220
|
+
try {
|
|
21221
|
+
saveAuthFile({
|
|
21222
|
+
...auth,
|
|
21223
|
+
token: client.apiKey,
|
|
21224
|
+
username: updatedName,
|
|
21225
|
+
uid: updatedUid,
|
|
21226
|
+
email: updatedEmail
|
|
21227
|
+
});
|
|
21228
|
+
} catch (err) {
|
|
21229
|
+
consola.warn(err instanceof Error ? err.message : String(err));
|
|
21230
|
+
}
|
|
20980
21231
|
}
|
|
20981
21232
|
consola.success("Authenticated");
|
|
20982
21233
|
const lines = [
|
|
@@ -20984,27 +21235,16 @@ var whoamiCommand = defineCommand({
|
|
|
20984
21235
|
updatedUid ? ` UID: ${updatedUid}` : null,
|
|
20985
21236
|
updatedEmail ? ` Email: ${updatedEmail}` : null,
|
|
20986
21237
|
data.apiKeyMasked ? ` API Key: ${data.apiKeyMasked}` : null,
|
|
20987
|
-
` Token: ${maskToken(
|
|
20988
|
-
auth.updated_at ? import_picocolors3.gray(` Saved: ${auth.updated_at}`) : null
|
|
21238
|
+
` Token: ${maskToken(client.apiKey)}`
|
|
20989
21239
|
].filter(Boolean);
|
|
20990
21240
|
consola.log(`${lines.join(`
|
|
20991
21241
|
`)}
|
|
20992
21242
|
`);
|
|
20993
|
-
return;
|
|
20994
21243
|
} catch (err) {
|
|
20995
|
-
|
|
20996
|
-
|
|
20997
|
-
|
|
20998
|
-
return;
|
|
20999
|
-
}
|
|
21244
|
+
if (isTransportFailure(err))
|
|
21245
|
+
throw err;
|
|
21246
|
+
reportCommandError(err);
|
|
21000
21247
|
}
|
|
21001
|
-
consola.info("Authentication Status (Cached)");
|
|
21002
|
-
consola.log(` Token: ${maskToken(auth.token)}
|
|
21003
|
-
` + (auth.username ? ` User: ${auth.username}
|
|
21004
|
-
` : "") + (auth.uid ? ` UID: ${auth.uid}
|
|
21005
|
-
` : "") + (auth.email ? ` Email: ${auth.email}
|
|
21006
|
-
` : "") + (auth.updated_at ? import_picocolors3.gray(` Saved: ${auth.updated_at}
|
|
21007
|
-
`) : ""));
|
|
21008
21248
|
}
|
|
21009
21249
|
});
|
|
21010
21250
|
|
|
@@ -21076,6 +21316,10 @@ AGENT QUICKSTART
|
|
|
21076
21316
|
Failure exits 1 with a JSON error carrying message, errCode, httpStatus and
|
|
21077
21317
|
nextStep, which is the action to take:
|
|
21078
21318
|
relogin | retry_later | top_up_balance | fix_input | retry | none.
|
|
21319
|
+
A rejected saved credential (auth.json's apiUrl is off the allowlist) reports
|
|
21320
|
+
no errCode/httpStatus - nothing came from the platform - and nextStep
|
|
21321
|
+
"relogin", since \`msc login\` rewrites the file. No credential at all reports
|
|
21322
|
+
no nextStep, so the two states differ without parsing the message.
|
|
21079
21323
|
A turn reports status "success", or "incomplete" if the stream ended early.
|
|
21080
21324
|
`;
|
|
21081
21325
|
await runMain({
|
package/package.json
CHANGED