@302ai/media-studio-cli 0.1.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 +36 -9
- package/dist/index.js +757 -211
- package/package.json +1 -1
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.
|
|
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
|
}
|
|
@@ -9749,6 +9750,151 @@ function isString(value) {
|
|
|
9749
9750
|
return typeof value === "string";
|
|
9750
9751
|
}
|
|
9751
9752
|
// ../media-studio-contract/src/err-codes.ts
|
|
9753
|
+
var ErrCode = {
|
|
9754
|
+
MISSING_REQUIRED_PARAM: 10001,
|
|
9755
|
+
INVALID_PARAM_FORMAT: 10002,
|
|
9756
|
+
RESOURCE_NOT_FOUND: 10003,
|
|
9757
|
+
METHOD_NOT_ALLOWED: 10004,
|
|
9758
|
+
RATE_LIMITED: 10005,
|
|
9759
|
+
PAYLOAD_TOO_LARGE: 10006,
|
|
9760
|
+
UNSUPPORTED_MEDIA_TYPE: 10007,
|
|
9761
|
+
INTERNAL_ERROR: 10500,
|
|
9762
|
+
DEPENDENCY_UNAVAILABLE: 10501,
|
|
9763
|
+
DATABASE_ERROR: 10502,
|
|
9764
|
+
SERVICE_UNAVAILABLE: 10503,
|
|
9765
|
+
CACHE_UNAVAILABLE: 10504,
|
|
9766
|
+
UNAUTHORIZED: 20001,
|
|
9767
|
+
TOKEN_MISSING: 20002,
|
|
9768
|
+
TOKEN_INVALID: 20003,
|
|
9769
|
+
TOKEN_EXPIRED: 20004,
|
|
9770
|
+
TOKEN_REVOKED: 20005,
|
|
9771
|
+
REFRESH_TOKEN_INVALID: 20006,
|
|
9772
|
+
REFRESH_TOKEN_ROTATED: 20007,
|
|
9773
|
+
VERIFICATION_CODE_INVALID: 20101,
|
|
9774
|
+
VERIFICATION_CODE_EXPIRED: 20102,
|
|
9775
|
+
CODE_SEND_RATE_LIMITED: 20103,
|
|
9776
|
+
CODE_SEND_DAILY_LIMIT: 20104,
|
|
9777
|
+
EMAIL_SEND_FAILED: 20105,
|
|
9778
|
+
EMAIL_ALREADY_REGISTERED: 20106,
|
|
9779
|
+
EMAIL_NOT_REGISTERED: 20107,
|
|
9780
|
+
OAUTH_STATE_INVALID: 20201,
|
|
9781
|
+
OAUTH_CALLBACK_FAILED: 20202,
|
|
9782
|
+
OAUTH_ACCOUNT_ALREADY_BOUND: 20203,
|
|
9783
|
+
SSO_INFO_CONFLICT: 20204,
|
|
9784
|
+
FORBIDDEN: 20301,
|
|
9785
|
+
ADMIN_REQUIRED: 20302,
|
|
9786
|
+
ACCOUNT_DEACTIVATED: 20303,
|
|
9787
|
+
INSUFFICIENT_BALANCE: 30001,
|
|
9788
|
+
CREDIT_FREEZE_FAILED: 30002,
|
|
9789
|
+
FREEZE_RECORD_NOT_FOUND: 30003,
|
|
9790
|
+
FREEZE_ALREADY_SETTLED: 30004,
|
|
9791
|
+
CREDIT_SETTLE_FAILED: 30005,
|
|
9792
|
+
SETTLE_EXCEEDS_FREEZE: 30006,
|
|
9793
|
+
UNFREEZE_FAILED: 30007,
|
|
9794
|
+
CREDIT_BATCH_EXPIRED: 30008,
|
|
9795
|
+
CREDIT_BATCH_NOT_FOUND: 30009,
|
|
9796
|
+
CONCURRENT_MODIFICATION: 30010,
|
|
9797
|
+
PRICING_RULE_NOT_FOUND: 30101,
|
|
9798
|
+
MODEL_PRICE_MISSING: 30102,
|
|
9799
|
+
USAGE_RECORD_DUPLICATED: 30103,
|
|
9800
|
+
BALANCE_CACHE_REBUILD_FAILED: 30104,
|
|
9801
|
+
LEDGER_WRITE_FAILED: 30105,
|
|
9802
|
+
UPSTREAM_ERROR: 40001,
|
|
9803
|
+
UPSTREAM_TIMEOUT: 40002,
|
|
9804
|
+
UPSTREAM_RATE_LIMITED: 40003,
|
|
9805
|
+
UPSTREAM_UNAVAILABLE: 40004,
|
|
9806
|
+
UPSTREAM_AUTH_FAILED: 40005,
|
|
9807
|
+
UPSTREAM_DATA_INVALID: 40006,
|
|
9808
|
+
MODEL_UNSUPPORTED: 40101,
|
|
9809
|
+
MODEL_LABEL_INVALID: 40102,
|
|
9810
|
+
MODEL_PRICE_NOT_CONFIGURED: 40103,
|
|
9811
|
+
MODEL_LABEL_DEPRECATED: 40104,
|
|
9812
|
+
SANDBOX_TOKEN_INVALID: 40201,
|
|
9813
|
+
SANDBOX_TOKEN_EXPIRED: 40202,
|
|
9814
|
+
OSS_TRANSFER_FAILED: 40301,
|
|
9815
|
+
OSS_URL_PRESIGN_FAILED: 40302,
|
|
9816
|
+
PLAN_NOT_FOUND: 50001,
|
|
9817
|
+
PLAN_UNAVAILABLE: 50002,
|
|
9818
|
+
PLAN_PRICE_MISSING: 50003,
|
|
9819
|
+
ORDER_NOT_FOUND: 50101,
|
|
9820
|
+
ORDER_ALREADY_PAID: 50102,
|
|
9821
|
+
ORDER_EXPIRED: 50103,
|
|
9822
|
+
ORDER_AMOUNT_MISMATCH: 50104,
|
|
9823
|
+
ORDER_STATUS_NOT_ALLOWED: 50105,
|
|
9824
|
+
ORDER_CANCEL_FAILED: 50106,
|
|
9825
|
+
ORDER_PENDING_EXISTS: 50107,
|
|
9826
|
+
PAYMENT_FAILED: 50201,
|
|
9827
|
+
PAYMENT_REJECTED: 50202,
|
|
9828
|
+
PAYMENT_METHOD_NOT_FOUND: 50203,
|
|
9829
|
+
WEBHOOK_SIGNATURE_INVALID: 50204,
|
|
9830
|
+
WEBHOOK_EVENT_UNKNOWN: 50205,
|
|
9831
|
+
STRIPE_API_ERROR: 50206,
|
|
9832
|
+
SUBSCRIPTION_NOT_FOUND: 50301,
|
|
9833
|
+
SUBSCRIPTION_STATUS_NOT_ALLOWED: 50302,
|
|
9834
|
+
SUBSCRIPTION_CANCELED: 50303,
|
|
9835
|
+
UPGRADE_PRORATION_FAILED: 50304,
|
|
9836
|
+
DOWNGRADE_NOT_ALLOWED: 50305,
|
|
9837
|
+
AUTO_RENEW_FAILED: 50306,
|
|
9838
|
+
SAVED_CARD_INVALID: 50307,
|
|
9839
|
+
REFUND_NOT_ELIGIBLE: 50401,
|
|
9840
|
+
REFUND_WINDOW_EXPIRED: 50402,
|
|
9841
|
+
REFUND_THRESHOLD_EXCEEDED: 50403,
|
|
9842
|
+
REFUND_FAILED: 50404,
|
|
9843
|
+
TOPUP_SUBSCRIPTION_ONLY: 50501,
|
|
9844
|
+
TOPUP_FROZEN: 50502,
|
|
9845
|
+
TOPUP_NOT_FOUND: 50503,
|
|
9846
|
+
SESSION_NOT_FOUND: 60001,
|
|
9847
|
+
SESSION_ALREADY_EXISTS: 60002,
|
|
9848
|
+
SESSION_STATUS_NOT_ALLOWED: 60003,
|
|
9849
|
+
CHAT_SESSION_EXPIRED: 60004,
|
|
9850
|
+
CHAT_CONTEXT_MISSING: 60005,
|
|
9851
|
+
PROMPT_SUGGESTION_FAILED: 60006,
|
|
9852
|
+
TITLE_GENERATION_FAILED: 60007,
|
|
9853
|
+
CHAT_TASK_NOT_FOUND: 60101,
|
|
9854
|
+
CHAT_TASK_IN_PROGRESS: 60102,
|
|
9855
|
+
CHAT_TASK_CANCELED: 60103,
|
|
9856
|
+
CHAT_TASK_STATUS_NOT_ALLOWED: 60104,
|
|
9857
|
+
SANDBOX_CREATE_FAILED: 60201,
|
|
9858
|
+
SANDBOX_WARMUP_FAILED: 60202,
|
|
9859
|
+
SANDBOX_TIMEOUT: 60203,
|
|
9860
|
+
SANDBOX_UNAVAILABLE: 60204,
|
|
9861
|
+
SANDBOX_COMMAND_FAILED: 60205,
|
|
9862
|
+
SKILL_NOT_FOUND: 60301,
|
|
9863
|
+
SKILL_FILE_INVALID: 60302,
|
|
9864
|
+
SKILL_BUILTIN_IMMUTABLE: 60303,
|
|
9865
|
+
SKILL_UPLOAD_INVALID: 60304,
|
|
9866
|
+
ASSET_NOT_FOUND: 70001,
|
|
9867
|
+
ASSET_GROUP_NOT_FOUND: 70002,
|
|
9868
|
+
ASSET_GROUP_NAME_EXISTS: 70003,
|
|
9869
|
+
ASSET_UPLOAD_FAILED: 70004,
|
|
9870
|
+
ASSET_TOO_LARGE: 70005,
|
|
9871
|
+
ASSET_TYPE_NOT_ALLOWED: 70006,
|
|
9872
|
+
ASSET_GROUP_INVALID: 70007,
|
|
9873
|
+
OSS_UPLOAD_FAILED: 70101,
|
|
9874
|
+
OSS_DOWNLOAD_FAILED: 70102,
|
|
9875
|
+
COVER_UPLOAD_FAILED: 70103,
|
|
9876
|
+
OSS_DELETE_FAILED: 70104,
|
|
9877
|
+
FILE_NOT_FOUND: 70201,
|
|
9878
|
+
FILE_READ_FAILED: 70202,
|
|
9879
|
+
FILE_WRITE_FAILED: 70203,
|
|
9880
|
+
FILE_FORMAT_INVALID: 70204,
|
|
9881
|
+
FILE_FORMAT_TEXT_FILE_ONLY: 70205,
|
|
9882
|
+
FILE_OPERATION_INVALID: 70206,
|
|
9883
|
+
FILE_TARGET_PATH_REQUIRED: 70207,
|
|
9884
|
+
FILE_PATH_COUNT_MISMATCH: 70208,
|
|
9885
|
+
FILE_RENAME_BATCH_UNSUPPORTED: 70209,
|
|
9886
|
+
ADMIN_NOT_LOGGED_IN: 90001,
|
|
9887
|
+
ADMIN_2FA_REQUIRED: 90002,
|
|
9888
|
+
ADMIN_2FA_CODE_INVALID: 90003,
|
|
9889
|
+
ADMIN_2FA_RATE_LIMITED: 90004,
|
|
9890
|
+
ADMIN_FORBIDDEN: 90005,
|
|
9891
|
+
ADMIN_NOT_FOUND: 90006,
|
|
9892
|
+
RISK_CONTROL_TRIGGERED: 90101,
|
|
9893
|
+
RISK_CONTROL_RATE_LIMITED: 90102,
|
|
9894
|
+
RECONCILIATION_MISMATCH: 90103,
|
|
9895
|
+
RECONCILIATION_TASK_FAILED: 90104,
|
|
9896
|
+
AUDIT_LOG_WRITE_FAILED: 90105
|
|
9897
|
+
};
|
|
9752
9898
|
var ERR_NEXT_STEP = {
|
|
9753
9899
|
10001: "fix_input",
|
|
9754
9900
|
10002: "fix_input",
|
|
@@ -9928,11 +10074,14 @@ var APP_ENV_HOSTS = {
|
|
|
9928
10074
|
// ../media-studio-contract/src/skills/activation.ts
|
|
9929
10075
|
var SKILL_ACTIVATION_MARKER = "%%";
|
|
9930
10076
|
var SKILL_ACTIVATION_RE = /%%(\/[^%\s]+)%%/g;
|
|
10077
|
+
function createSkillActivationRegex() {
|
|
10078
|
+
return new RegExp(SKILL_ACTIVATION_RE.source, SKILL_ACTIVATION_RE.flags);
|
|
10079
|
+
}
|
|
9931
10080
|
function formatSkillActivation(command) {
|
|
9932
10081
|
return `${SKILL_ACTIVATION_MARKER}${command}${SKILL_ACTIVATION_MARKER}`;
|
|
9933
10082
|
}
|
|
9934
10083
|
function findSkillActivationMatches(text) {
|
|
9935
|
-
return Array.from(text.matchAll(
|
|
10084
|
+
return Array.from(text.matchAll(createSkillActivationRegex())).flatMap((match, index) => {
|
|
9936
10085
|
const start = match.index;
|
|
9937
10086
|
const command = match[1];
|
|
9938
10087
|
if (start === undefined || !command)
|
|
@@ -9949,6 +10098,25 @@ function findSkillActivationMatches(text) {
|
|
|
9949
10098
|
});
|
|
9950
10099
|
}
|
|
9951
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
|
+
|
|
9952
10120
|
// ../media-studio-contract/src/skills/compose-turn.ts
|
|
9953
10121
|
function applySkillActivations(text, spans) {
|
|
9954
10122
|
const ordered = spans.toSorted((a, b) => b.start - a.start);
|
|
@@ -10027,6 +10195,15 @@ function attempt2(func) {
|
|
|
10027
10195
|
return [error, null];
|
|
10028
10196
|
}
|
|
10029
10197
|
}
|
|
10198
|
+
|
|
10199
|
+
// ../../node_modules/es-toolkit/dist/util/attemptAsync.mjs
|
|
10200
|
+
async function attemptAsync(func) {
|
|
10201
|
+
try {
|
|
10202
|
+
return [null, await func()];
|
|
10203
|
+
} catch (error) {
|
|
10204
|
+
return [error, null];
|
|
10205
|
+
}
|
|
10206
|
+
}
|
|
10030
10207
|
// ../../node_modules/ts-pattern/dist/index.js
|
|
10031
10208
|
var t2 = Symbol.for("@ts-pattern/matcher");
|
|
10032
10209
|
var e2 = Symbol.for("@ts-pattern/isVariadic");
|
|
@@ -16721,9 +16898,7 @@ function validateApiBaseUrl(urlString) {
|
|
|
16721
16898
|
}
|
|
16722
16899
|
var AuthDataSchema = object2({
|
|
16723
16900
|
token: string4().min(1),
|
|
16724
|
-
apiUrl: string4().
|
|
16725
|
-
message: "Untrusted API URL in auth file"
|
|
16726
|
-
}).optional(),
|
|
16901
|
+
apiUrl: string4().optional(),
|
|
16727
16902
|
uid: string4().optional(),
|
|
16728
16903
|
username: string4().optional(),
|
|
16729
16904
|
email: string4().optional(),
|
|
@@ -16737,11 +16912,35 @@ var ConfigFileSchema = object2({
|
|
|
16737
16912
|
latest: string4().optional()
|
|
16738
16913
|
}).optional()
|
|
16739
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
|
+
}
|
|
16740
16924
|
function readAuthFile() {
|
|
16925
|
+
let raw;
|
|
16741
16926
|
try {
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
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();
|
|
16745
16944
|
} catch {
|
|
16746
16945
|
return null;
|
|
16747
16946
|
}
|
|
@@ -16795,7 +16994,8 @@ function saveUpdateCheck(check) {
|
|
|
16795
16994
|
writeConfigFile({ updateCheck: check });
|
|
16796
16995
|
}
|
|
16797
16996
|
function resolveApiKey() {
|
|
16798
|
-
const
|
|
16997
|
+
const auth = readAuthFile();
|
|
16998
|
+
const token = auth?.token;
|
|
16799
16999
|
if (!token?.trim()) {
|
|
16800
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.");
|
|
16801
17001
|
}
|
|
@@ -16804,7 +17004,7 @@ function resolveApiKey() {
|
|
|
16804
17004
|
function resolveApiBaseUrl(flag) {
|
|
16805
17005
|
if (flag)
|
|
16806
17006
|
return validateApiBaseUrl(flag);
|
|
16807
|
-
const fromAuth =
|
|
17007
|
+
const fromAuth = readAuthFileIfUsable()?.apiUrl;
|
|
16808
17008
|
if (typeof fromAuth === "string" && fromAuth.trim()) {
|
|
16809
17009
|
if (isAllowedCliApiOrigin(fromAuth)) {
|
|
16810
17010
|
return fromAuth;
|
|
@@ -16815,6 +17015,7 @@ function resolveApiBaseUrl(flag) {
|
|
|
16815
17015
|
|
|
16816
17016
|
// src/client/session-types.ts
|
|
16817
17017
|
var SessionSourceSchema = _enum2(["web", "cli"]);
|
|
17018
|
+
var CLI_DEFAULT_SESSION_NOTE = "Agent Task";
|
|
16818
17019
|
var ApiResponseBaseSchema = object2({
|
|
16819
17020
|
success: boolean4(),
|
|
16820
17021
|
error: object2({
|
|
@@ -16969,6 +17170,15 @@ class ClientSessions {
|
|
|
16969
17170
|
const url = `${this.baseUrl}/api/sessions${search ? `?${search}` : ""}`;
|
|
16970
17171
|
return requestJson(this.customFetch, url, { method: "GET", headers: this.authHeaders() }, ListSessionsResponseSchema, (status) => `Failed to fetch sessions with status ${status}`);
|
|
16971
17172
|
}
|
|
17173
|
+
async exists(sessionId) {
|
|
17174
|
+
const params = new URLSearchParams({ sessionId, limit: "1" });
|
|
17175
|
+
const [err] = await attemptAsync(() => requestJson(this.customFetch, `${this.baseUrl}/api/sessions/detail?${params.toString()}`, { method: "GET", headers: this.authHeaders() }, unknown2(), (status) => `Failed to verify session ${sessionId} with status ${status}`));
|
|
17176
|
+
if (!err)
|
|
17177
|
+
return true;
|
|
17178
|
+
if (err instanceof ChatUpstreamError && err.status === 404)
|
|
17179
|
+
return false;
|
|
17180
|
+
throw err;
|
|
17181
|
+
}
|
|
16972
17182
|
async artifacts(query) {
|
|
16973
17183
|
const params = new URLSearchParams({ sessionId: query.sessionId });
|
|
16974
17184
|
if (query.taskId !== undefined)
|
|
@@ -17006,12 +17216,17 @@ class ClientSessions {
|
|
|
17006
17216
|
signal
|
|
17007
17217
|
});
|
|
17008
17218
|
if (!response.ok) {
|
|
17009
|
-
await response.
|
|
17010
|
-
return;
|
|
17011
|
-
});
|
|
17219
|
+
const body = await response.text().catch(() => "");
|
|
17012
17220
|
const retryable = response.status >= 500 || response.status === 429;
|
|
17013
|
-
if (!retryable)
|
|
17221
|
+
if (!retryable) {
|
|
17222
|
+
if (!state.sessionReady) {
|
|
17223
|
+
state.outcome = {
|
|
17224
|
+
status: "failed",
|
|
17225
|
+
error: new ChatUpstreamError(`Session prewarm was rejected with status ${response.status}`, response.status, body)
|
|
17226
|
+
};
|
|
17227
|
+
}
|
|
17014
17228
|
break;
|
|
17229
|
+
}
|
|
17015
17230
|
continue;
|
|
17016
17231
|
}
|
|
17017
17232
|
await this.consumePrewarmStream(response, state, {
|
|
@@ -17030,7 +17245,10 @@ class ClientSessions {
|
|
|
17030
17245
|
}
|
|
17031
17246
|
if (state.outcome)
|
|
17032
17247
|
return state.outcome;
|
|
17033
|
-
return state.sessionReady ? { status: "fallback", session: state.sessionReady } : {
|
|
17248
|
+
return state.sessionReady ? { status: "fallback", session: state.sessionReady } : {
|
|
17249
|
+
status: "failed",
|
|
17250
|
+
error: new ChatUpstreamError("Session prewarm never completed: no session_ready event (request retries exhausted or timed out).", 0, "")
|
|
17251
|
+
};
|
|
17034
17252
|
}
|
|
17035
17253
|
async consumePrewarmStream(response, state, callbacks) {
|
|
17036
17254
|
if (!response.body)
|
|
@@ -17061,12 +17279,18 @@ class ClientSessions {
|
|
|
17061
17279
|
}
|
|
17062
17280
|
case "done": {
|
|
17063
17281
|
PrewarmDoneSchema.parse(data);
|
|
17064
|
-
state.outcome = state.sessionReady ? { status: "ready", session: state.sessionReady } : {
|
|
17282
|
+
state.outcome = state.sessionReady ? { status: "ready", session: state.sessionReady } : {
|
|
17283
|
+
status: "failed",
|
|
17284
|
+
error: new ChatUpstreamError("Session prewarm ended before the session was created upstream.", 0, "")
|
|
17285
|
+
};
|
|
17065
17286
|
return;
|
|
17066
17287
|
}
|
|
17067
17288
|
case "error": {
|
|
17068
|
-
PrewarmErrorSchema.parse(data);
|
|
17069
|
-
state.outcome = state.sessionReady ? { status: "fallback", session: state.sessionReady } : {
|
|
17289
|
+
const payload = PrewarmErrorSchema.parse(data);
|
|
17290
|
+
state.outcome = state.sessionReady ? { status: "fallback", session: state.sessionReady } : {
|
|
17291
|
+
status: "failed",
|
|
17292
|
+
error: new ChatUpstreamError(payload.message, 0, JSON.stringify({ error: payload }))
|
|
17293
|
+
};
|
|
17070
17294
|
return;
|
|
17071
17295
|
}
|
|
17072
17296
|
default:
|
|
@@ -17261,7 +17485,7 @@ class MediaStudioClient {
|
|
|
17261
17485
|
const nextKey = rotatedToken.trim();
|
|
17262
17486
|
this._apiKey = nextKey;
|
|
17263
17487
|
try {
|
|
17264
|
-
const authData =
|
|
17488
|
+
const authData = readAuthFileIfUsable();
|
|
17265
17489
|
if (authData) {
|
|
17266
17490
|
saveAuthFile({
|
|
17267
17491
|
...authData,
|
|
@@ -17584,10 +17808,50 @@ var NEXT_STEP_GUIDANCE = {
|
|
|
17584
17808
|
retry: "The failure looks transient. Retrying the same request may succeed.",
|
|
17585
17809
|
none: ""
|
|
17586
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
|
+
}
|
|
17587
17849
|
function reportCommandError(err, options) {
|
|
17588
|
-
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;
|
|
17589
17853
|
const upstream = err instanceof ChatUpstreamError ? err : null;
|
|
17590
|
-
const nextStep = upstream?.nextStep ?? (upstream?.status === 401 ? "relogin" : null);
|
|
17854
|
+
const nextStep = upstream?.nextStep ?? (upstream?.status === 401 || err instanceof UntrustedCredentialError ? "relogin" : null);
|
|
17591
17855
|
const status = upstream?.status !== undefined && upstream.status !== 0 ? upstream.status : null;
|
|
17592
17856
|
const guidance = nextStep ? NEXT_STEP_GUIDANCE[nextStep] : "";
|
|
17593
17857
|
if (options?.json) {
|
|
@@ -17597,7 +17861,8 @@ function reportCommandError(err, options) {
|
|
|
17597
17861
|
...upstream?.errCode != null ? { errCode: upstream.errCode } : {},
|
|
17598
17862
|
...status != null ? { httpStatus: status } : {},
|
|
17599
17863
|
...nextStep != null ? { nextStep } : {},
|
|
17600
|
-
...guidance ? { guidance } : {}
|
|
17864
|
+
...guidance ? { guidance } : {},
|
|
17865
|
+
...transport ? { transportCode: transport.code } : {}
|
|
17601
17866
|
};
|
|
17602
17867
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}
|
|
17603
17868
|
`);
|
|
@@ -17812,29 +18077,66 @@ async function resolvePromptAttachments(options) {
|
|
|
17812
18077
|
}
|
|
17813
18078
|
|
|
17814
18079
|
// src/services/session.ts
|
|
18080
|
+
var SESSION_GONE_ERR_CODES = [
|
|
18081
|
+
ErrCode.SESSION_NOT_FOUND,
|
|
18082
|
+
ErrCode.CHAT_SESSION_EXPIRED
|
|
18083
|
+
];
|
|
17815
18084
|
function isSessionGoneError(err) {
|
|
17816
18085
|
if (!(err instanceof ChatUpstreamError))
|
|
17817
18086
|
return false;
|
|
18087
|
+
if (err.errCode !== null) {
|
|
18088
|
+
return SESSION_GONE_ERR_CODES.includes(err.errCode);
|
|
18089
|
+
}
|
|
17818
18090
|
if (err.status === 404)
|
|
17819
18091
|
return true;
|
|
17820
18092
|
return err.body.includes(ERR_SESSION_EXPIRED);
|
|
17821
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
|
+
}
|
|
18104
|
+
async function ensureSessionExists(options) {
|
|
18105
|
+
const client = new MediaStudioClient({
|
|
18106
|
+
baseUrl: options.apiBaseUrl,
|
|
18107
|
+
apiKey: options.apiKey
|
|
18108
|
+
});
|
|
18109
|
+
if (await client.sessions.exists(options.sessionId))
|
|
18110
|
+
return;
|
|
18111
|
+
throw new SessionNotFoundError(options.sessionId);
|
|
18112
|
+
}
|
|
18113
|
+
function finalizeProvision(result, spinner) {
|
|
18114
|
+
spinner?.stop();
|
|
18115
|
+
if (result.status === "failed") {
|
|
18116
|
+
throw result.error;
|
|
18117
|
+
}
|
|
18118
|
+
if (result.status === "fallback") {
|
|
18119
|
+
consola.warn("Session created, but sandbox/skill setup did not fully complete. Plain-text chat still works.");
|
|
18120
|
+
}
|
|
18121
|
+
return result.sessionId;
|
|
18122
|
+
}
|
|
17822
18123
|
async function provisionSession(options) {
|
|
17823
|
-
const sessionId = nanoid3();
|
|
17824
18124
|
const client = new MediaStudioClient({
|
|
17825
18125
|
baseUrl: options.apiBaseUrl,
|
|
17826
18126
|
apiKey: options.apiKey
|
|
17827
18127
|
});
|
|
18128
|
+
const sessionId = nanoid3();
|
|
17828
18129
|
const outcome = await client.sessions.prewarm({
|
|
17829
18130
|
sessionId,
|
|
17830
18131
|
source: "cli",
|
|
18132
|
+
note: options.note ?? CLI_DEFAULT_SESSION_NOTE,
|
|
17831
18133
|
onSessionReady: () => saveLastSessionId(sessionId),
|
|
17832
|
-
|
|
18134
|
+
onStageChange: options.onStageChange
|
|
17833
18135
|
});
|
|
17834
|
-
|
|
17835
|
-
status,
|
|
17836
|
-
|
|
17837
|
-
|
|
18136
|
+
if (outcome.status === "failed") {
|
|
18137
|
+
return { status: "failed", error: outcome.error };
|
|
18138
|
+
}
|
|
18139
|
+
return { status: outcome.status, sessionId };
|
|
17838
18140
|
}
|
|
17839
18141
|
|
|
17840
18142
|
// src/lib/skill-refs.ts
|
|
@@ -17890,7 +18192,7 @@ async function ensureUsable(client, skill) {
|
|
|
17890
18192
|
});
|
|
17891
18193
|
if (ensured)
|
|
17892
18194
|
return;
|
|
17893
|
-
throw new SkillResolutionError(describeEnableFailure(skill, cause));
|
|
18195
|
+
throw new SkillResolutionError(describeEnableFailure(skill, cause), cause ? { cause } : undefined);
|
|
17894
18196
|
}
|
|
17895
18197
|
async function resolvePromptSkills(options) {
|
|
17896
18198
|
const spans = findSkillRefSpans(options.prompt);
|
|
@@ -17963,34 +18265,59 @@ function printTerminalMediaAssets(mediaAssets, fullText) {
|
|
|
17963
18265
|
process.stdout.write(`====================
|
|
17964
18266
|
`);
|
|
17965
18267
|
}
|
|
17966
|
-
function
|
|
17967
|
-
|
|
17968
|
-
|
|
17969
|
-
|
|
17970
|
-
}
|
|
17971
|
-
|
|
17972
|
-
|
|
17973
|
-
|
|
17974
|
-
|
|
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
|
+
}
|
|
18274
|
+
async function provisionNewSession(options) {
|
|
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
|
+
});
|
|
18279
|
+
const result = await provisionSession({
|
|
18280
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
18281
|
+
apiKey: options.apiKey,
|
|
18282
|
+
note,
|
|
18283
|
+
onStageChange
|
|
18284
|
+
});
|
|
18285
|
+
return finalizeProvision(result, options.spinner);
|
|
17975
18286
|
}
|
|
17976
18287
|
async function resolveChatSession(options) {
|
|
18288
|
+
if (options.newSession) {
|
|
18289
|
+
return {
|
|
18290
|
+
sessionId: await provisionNewSession({
|
|
18291
|
+
...options,
|
|
18292
|
+
prompt: options.prompt
|
|
18293
|
+
}),
|
|
18294
|
+
recreateOnGone: false
|
|
18295
|
+
};
|
|
18296
|
+
}
|
|
17977
18297
|
if (options.sessionFlag) {
|
|
17978
18298
|
saveLastSessionId(options.sessionFlag);
|
|
17979
|
-
return { sessionId: options.sessionFlag, recreateOnGone:
|
|
18299
|
+
return { sessionId: options.sessionFlag, recreateOnGone: false };
|
|
17980
18300
|
}
|
|
17981
18301
|
const pointer = readLastSessionId();
|
|
17982
18302
|
if (pointer) {
|
|
17983
18303
|
return { sessionId: pointer, recreateOnGone: true };
|
|
17984
18304
|
}
|
|
17985
|
-
|
|
17986
|
-
|
|
17987
|
-
|
|
17988
|
-
|
|
17989
|
-
|
|
17990
|
-
|
|
17991
|
-
}
|
|
17992
|
-
|
|
17993
|
-
|
|
18305
|
+
return {
|
|
18306
|
+
sessionId: await provisionNewSession({
|
|
18307
|
+
...options,
|
|
18308
|
+
prompt: options.prompt
|
|
18309
|
+
}),
|
|
18310
|
+
recreateOnGone: false
|
|
18311
|
+
};
|
|
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);
|
|
17994
18321
|
}
|
|
17995
18322
|
async function streamTurn(options) {
|
|
17996
18323
|
const { args, sessionId, spinner, autoEnabled, attachments } = options;
|
|
@@ -18011,84 +18338,103 @@ async function streamTurn(options) {
|
|
|
18011
18338
|
const steps = [];
|
|
18012
18339
|
const mediaAssets = [];
|
|
18013
18340
|
let hasFinished = false;
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
|
|
18017
|
-
|
|
18018
|
-
|
|
18019
|
-
|
|
18020
|
-
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;
|
|
18021
18347
|
}
|
|
18022
|
-
|
|
18023
|
-
|
|
18024
|
-
if (args["show-reasoning"] && !args.json) {
|
|
18348
|
+
M(ev).with({ type: "text-delta", delta: z2.string }, ({ delta }) => {
|
|
18349
|
+
isThinking = false;
|
|
18025
18350
|
spinner.stop();
|
|
18026
|
-
|
|
18027
|
-
|
|
18028
|
-
|
|
18029
|
-
|
|
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) {
|
|
18030
18358
|
spinner.stop();
|
|
18031
|
-
|
|
18359
|
+
process.stdout.write(delta);
|
|
18360
|
+
} else if (!args.json) {
|
|
18361
|
+
if (!isThinking) {
|
|
18362
|
+
isThinking = true;
|
|
18363
|
+
spinner.stop();
|
|
18364
|
+
if (fullText && !fullText.endsWith(`
|
|
18032
18365
|
`)) {
|
|
18033
|
-
|
|
18366
|
+
process.stdout.write(`
|
|
18034
18367
|
`);
|
|
18035
|
-
|
|
18368
|
+
fullText += `
|
|
18036
18369
|
`;
|
|
18037
|
-
|
|
18038
|
-
|
|
18370
|
+
}
|
|
18371
|
+
process.stdout.write(`[reasoning] Thinking...
|
|
18039
18372
|
`);
|
|
18040
|
-
|
|
18373
|
+
spinner.start("Thinking...");
|
|
18374
|
+
}
|
|
18041
18375
|
}
|
|
18042
|
-
}
|
|
18043
|
-
|
|
18044
|
-
|
|
18045
|
-
|
|
18046
|
-
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(`
|
|
18047
18380
|
`) && !args.json) {
|
|
18048
|
-
|
|
18381
|
+
process.stdout.write(`
|
|
18049
18382
|
`);
|
|
18050
|
-
|
|
18383
|
+
fullText += `
|
|
18051
18384
|
`;
|
|
18052
|
-
|
|
18053
|
-
|
|
18054
|
-
|
|
18055
|
-
|
|
18056
|
-
|
|
18385
|
+
}
|
|
18386
|
+
const formatted = formatToolMilestone(toolName, toolArgs);
|
|
18387
|
+
steps.push(formatted.milestoneLine);
|
|
18388
|
+
if (!args.json) {
|
|
18389
|
+
process.stdout.write(`${formatted.milestoneLine}
|
|
18057
18390
|
`);
|
|
18058
|
-
|
|
18059
|
-
|
|
18060
|
-
|
|
18061
|
-
|
|
18062
|
-
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
18066
|
-
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
|
|
18070
|
-
|
|
18071
|
-
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
18079
|
-
|
|
18080
|
-
|
|
18081
|
-
|
|
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)}
|
|
18082
18416
|
`);
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18086
|
-
|
|
18417
|
+
emitting = false;
|
|
18418
|
+
return;
|
|
18419
|
+
}
|
|
18420
|
+
printTerminalMediaAssets(mediaAssets, fullText);
|
|
18421
|
+
process.stdout.write(`session: ${sessionId}
|
|
18087
18422
|
`);
|
|
18088
|
-
|
|
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;
|
|
18089
18434
|
}
|
|
18090
18435
|
if (!hasFinished) {
|
|
18091
18436
|
spinner.stop();
|
|
18437
|
+
consola.warn(streamFailure === undefined ? "Stream ended early: no finish event" : `Stream ended early: ${describeStreamFailure(streamFailure)}`);
|
|
18092
18438
|
if (args.json) {
|
|
18093
18439
|
const output = {
|
|
18094
18440
|
status: "incomplete",
|
|
@@ -18113,20 +18459,49 @@ async function runTurn(options) {
|
|
|
18113
18459
|
try {
|
|
18114
18460
|
await streamTurn(options);
|
|
18115
18461
|
} catch (err) {
|
|
18116
|
-
|
|
18117
|
-
|
|
18118
|
-
|
|
18119
|
-
|
|
18120
|
-
apiKey: resolveApiKey(),
|
|
18121
|
-
...options.args.json ? {} : {
|
|
18122
|
-
onStageChange: (stage) => options.spinner.start(PROVISION_STAGE_LABELS[stage])
|
|
18123
|
-
}
|
|
18124
|
-
});
|
|
18125
|
-
const sessionId = finalizeProvision(result, options.spinner);
|
|
18126
|
-
await streamTurn({ ...options, sessionId, apiKey: resolveApiKey() });
|
|
18127
|
-
return;
|
|
18462
|
+
const sessionGone = isSessionGoneError(err);
|
|
18463
|
+
const ambiguousGone = err instanceof ChatUpstreamError && err.errCode === ErrCode.INVALID_PARAM_FORMAT;
|
|
18464
|
+
if (!sessionGone && !ambiguousGone) {
|
|
18465
|
+
throw err;
|
|
18128
18466
|
}
|
|
18129
|
-
|
|
18467
|
+
if (options.progress.emitted) {
|
|
18468
|
+
throw new Error(`Session ${options.sessionId} failed after this turn had already started.
|
|
18469
|
+
` + " → The partial result above stands; re-run the same prompt to continue in a new session.");
|
|
18470
|
+
}
|
|
18471
|
+
if (!options.recreateOnGone) {
|
|
18472
|
+
if (sessionGone || options.args.session) {
|
|
18473
|
+
throw new SessionNotFoundError(options.sessionId);
|
|
18474
|
+
}
|
|
18475
|
+
throw err;
|
|
18476
|
+
}
|
|
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
|
+
}
|
|
18491
|
+
}
|
|
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
|
+
});
|
|
18130
18505
|
}
|
|
18131
18506
|
}
|
|
18132
18507
|
var chatCommand = defineCommand({
|
|
@@ -18147,7 +18522,11 @@ var chatCommand = defineCommand({
|
|
|
18147
18522
|
},
|
|
18148
18523
|
session: {
|
|
18149
18524
|
type: "string",
|
|
18150
|
-
description: "
|
|
18525
|
+
description: "Chat in an existing session id (verified upstream); becomes the default session. A missing id fails — create one with --new"
|
|
18526
|
+
},
|
|
18527
|
+
new: {
|
|
18528
|
+
type: "boolean",
|
|
18529
|
+
description: "Create a new session and chat in it; becomes the default session"
|
|
18151
18530
|
},
|
|
18152
18531
|
"show-reasoning": {
|
|
18153
18532
|
type: "boolean",
|
|
@@ -18159,6 +18538,16 @@ var chatCommand = defineCommand({
|
|
|
18159
18538
|
const spinner = new AgentSpinner;
|
|
18160
18539
|
try {
|
|
18161
18540
|
const apiBaseUrl = resolveApiBaseUrl();
|
|
18541
|
+
if (args.new && args.session) {
|
|
18542
|
+
throw new Error("--new and --session cannot be combined: --session selects an existing session, --new creates one.");
|
|
18543
|
+
}
|
|
18544
|
+
if (args.session) {
|
|
18545
|
+
await ensureSessionExists({
|
|
18546
|
+
apiBaseUrl,
|
|
18547
|
+
apiKey: resolveApiKey(),
|
|
18548
|
+
sessionId: args.session
|
|
18549
|
+
});
|
|
18550
|
+
}
|
|
18162
18551
|
const { text, skillUrls, autoEnabled } = await resolvePromptSkills({
|
|
18163
18552
|
prompt: args.prompt,
|
|
18164
18553
|
apiBaseUrl,
|
|
@@ -18177,6 +18566,8 @@ var chatCommand = defineCommand({
|
|
|
18177
18566
|
});
|
|
18178
18567
|
const resolved = await resolveChatSession({
|
|
18179
18568
|
sessionFlag: args.session,
|
|
18569
|
+
newSession: args.new,
|
|
18570
|
+
prompt: buildNotePrompt(promptWithAttachments, attachments),
|
|
18180
18571
|
apiBaseUrl,
|
|
18181
18572
|
apiKey: resolveApiKey(),
|
|
18182
18573
|
spinner,
|
|
@@ -18187,6 +18578,7 @@ var chatCommand = defineCommand({
|
|
|
18187
18578
|
prompt: promptWithAttachments,
|
|
18188
18579
|
sessionId: resolved.sessionId,
|
|
18189
18580
|
recreateOnGone: resolved.recreateOnGone,
|
|
18581
|
+
progress: { emitted: false },
|
|
18190
18582
|
apiBaseUrl,
|
|
18191
18583
|
apiKey: resolveApiKey(),
|
|
18192
18584
|
spinner,
|
|
@@ -18243,7 +18635,11 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18243
18635
|
|
|
18244
18636
|
msc whoami
|
|
18245
18637
|
Displays your current account information (user, email, UID, server URL).
|
|
18246
|
-
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.
|
|
18247
18643
|
|
|
18248
18644
|
msc logout
|
|
18249
18645
|
Removes saved credentials on this machine.
|
|
@@ -18285,8 +18681,16 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18285
18681
|
"no such file". Remote URLs are not attachments and there is no @url form.
|
|
18286
18682
|
|
|
18287
18683
|
Session Switching:
|
|
18288
|
-
Pass \`--session <id>\` to direct a prompt to
|
|
18289
|
-
|
|
18684
|
+
Pass \`--session <id>\` to direct a prompt to an existing session and make it
|
|
18685
|
+
your new active session. The id is verified against the server first: the
|
|
18686
|
+
CLI only ever selects sessions that exist, so an unknown id fails with a
|
|
18687
|
+
hint instead of silently starting a different conversation.
|
|
18688
|
+
|
|
18689
|
+
Pass \`--new\` to start a fresh session (new id, never reused) and chat in
|
|
18690
|
+
it, ignoring the current default. \`--new\` and \`--session\` are mutually
|
|
18691
|
+
exclusive.
|
|
18692
|
+
msc chat "start over" --new # new session, new context
|
|
18693
|
+
msc sessions # pick an existing id for --session
|
|
18290
18694
|
|
|
18291
18695
|
4. USING SKILLS
|
|
18292
18696
|
|
|
@@ -18301,7 +18705,7 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18301
18705
|
msc skills add <slug> # add a skill from the plaza to your library
|
|
18302
18706
|
|
|
18303
18707
|
Referencing Skills in Prompts:
|
|
18304
|
-
Lead the prompt with your skill reference
|
|
18708
|
+
Lead the prompt with your skill reference - the message must start with it
|
|
18305
18709
|
for the skill to activate:
|
|
18306
18710
|
Slash form: msc chat "/<name> your prompt here"
|
|
18307
18711
|
Marker form: msc chat "%%/<name>%% your prompt here"
|
|
@@ -18327,7 +18731,13 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url} (${r.des
|
|
|
18327
18731
|
including download URLs and model metadata.
|
|
18328
18732
|
|
|
18329
18733
|
msc sessions use <session-id>
|
|
18330
|
-
Switch your active session pointer
|
|
18734
|
+
Switch your active session pointer to an existing session. The id is
|
|
18735
|
+
verified against the server; an unknown id fails here and leaves the
|
|
18736
|
+
pointer untouched.
|
|
18737
|
+
|
|
18738
|
+
msc sessions use --new
|
|
18739
|
+
Create a new session upstream and switch the pointer to it, without sending
|
|
18740
|
+
a prompt.
|
|
18331
18741
|
|
|
18332
18742
|
Session deletion is not available in the CLI. Open the corresponding session
|
|
18333
18743
|
in the Media Studio web app and delete it there manually.
|
|
@@ -18371,17 +18781,92 @@ ${getAvailableRegions().map((r) => ` ${r.key.padEnd(8)} - ${r.url}`).join(`
|
|
|
18371
18781
|
Extracting the result URL (e.g. with jq):
|
|
18372
18782
|
url=$(msc chat "a red balloon" --json | jq -r '.media[0].resultUrl')
|
|
18373
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
|
+
|
|
18374
18808
|
Error schema on failure (exit code 1):
|
|
18375
18809
|
{
|
|
18376
18810
|
"status": "error",
|
|
18377
18811
|
"message": "string", // human-readable error description
|
|
18378
18812
|
"errCode": 12345, // optional platform error code
|
|
18379
18813
|
"httpStatus": 401, // optional HTTP status code
|
|
18380
|
-
"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
|
|
18819
|
+
}
|
|
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; ..."
|
|
18381
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
|
+
|
|
18860
|
+
Session commands:
|
|
18861
|
+
msc sessions --json # list: [{ sessionId, note, ... }]
|
|
18862
|
+
msc sessions use <id> --json # { "sessionId": "string" }
|
|
18863
|
+
msc sessions use --new --json # { "sessionId": "string" } (newly created)
|
|
18864
|
+
\`--session\`/\`use <id>\` verify the id upstream; an unknown id exits 1 with
|
|
18865
|
+
the \`--new\` hint and leaves the default session untouched.
|
|
18382
18866
|
|
|
18383
18867
|
Recommended next steps:
|
|
18384
|
-
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\`
|
|
18385
18870
|
retry_later Rate limit reached; wait before re-sending
|
|
18386
18871
|
top_up_balance Insufficient balance; top up your account
|
|
18387
18872
|
fix_input Invalid parameters or prompt; adjust input
|
|
@@ -19294,7 +19779,7 @@ function applyForHost(urlString) {
|
|
|
19294
19779
|
return true;
|
|
19295
19780
|
}
|
|
19296
19781
|
function applyInsecureTlsIfRequested() {
|
|
19297
|
-
const target =
|
|
19782
|
+
const target = readAuthFileIfUsable()?.apiUrl || getDefaultApiBaseUrl();
|
|
19298
19783
|
return applyForHost(target);
|
|
19299
19784
|
}
|
|
19300
19785
|
function applyInsecureTlsForUrl(urlString) {
|
|
@@ -20167,37 +20652,36 @@ var loginCommand = defineCommand({
|
|
|
20167
20652
|
}
|
|
20168
20653
|
},
|
|
20169
20654
|
run: async ({ args }) => {
|
|
20170
|
-
|
|
20171
|
-
|
|
20172
|
-
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.
|
|
20173
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);
|
|
20174
20675
|
}
|
|
20175
|
-
|
|
20176
|
-
|
|
20177
|
-
|
|
20178
|
-
|
|
20179
|
-
|
|
20180
|
-
|
|
20181
|
-
|
|
20182
|
-
message: "Select server:",
|
|
20183
|
-
options: getAvailableRegions().map((opt) => ({
|
|
20184
|
-
label: opt.label,
|
|
20185
|
-
value: opt.url
|
|
20186
|
-
})),
|
|
20187
|
-
initialValue: SERVERS.default.url
|
|
20188
|
-
});
|
|
20189
|
-
if (isCancel(answer)) {
|
|
20190
|
-
process.exit(130);
|
|
20191
|
-
}
|
|
20192
|
-
targetApiUrl = String(answer);
|
|
20193
|
-
}
|
|
20194
|
-
applyInsecureTlsForUrl(targetApiUrl);
|
|
20195
|
-
const apiUrl = resolveApiBaseUrl(targetApiUrl);
|
|
20196
|
-
consola.info("Opening browser for sign-in...");
|
|
20197
|
-
if (!process.stdin.isTTY) {
|
|
20198
|
-
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(`
|
|
20199
20683
|
`);
|
|
20200
|
-
|
|
20684
|
+
process.stdout.write(`Sign-in needs a human at a browser on this machine:
|
|
20201
20685
|
` + ` 1. already signed in to the Media Studio web app there -> completes at once
|
|
20202
20686
|
` + ` 2. not signed in -> a browser opens; a human finishes it within 120s
|
|
20203
20687
|
` + `
|
|
@@ -20207,29 +20691,24 @@ ${regionList}
|
|
|
20207
20691
|
` + `
|
|
20208
20692
|
` + `If you are an agent, ask the user to sign in to the web app in their browser first
|
|
20209
20693
|
` + "or ask the user to run `msc login` in their own terminal,\n" + "then poll `msc whoami` until it succeeds.\n");
|
|
20210
|
-
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
|
|
20694
|
+
}
|
|
20695
|
+
const result = await runLoopbackLogin({
|
|
20696
|
+
apiUrl,
|
|
20697
|
+
onUrl: (url) => {
|
|
20698
|
+
consola.info(`If your browser does not open automatically, visit:
|
|
20215
20699
|
${url}
|
|
20216
20700
|
` + "Signed in to the Media Studio web app in that browser already? This completes without a prompt.");
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
} catch (err) {
|
|
20229
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
20230
|
-
consola.error(message);
|
|
20231
|
-
process.exit(1);
|
|
20232
|
-
}
|
|
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!");
|
|
20233
20712
|
}
|
|
20234
20713
|
});
|
|
20235
20714
|
|
|
@@ -20254,7 +20733,7 @@ function renderSessionsTable(sessions, currentSessionId) {
|
|
|
20254
20733
|
id: s.id === currentSessionId ? `${s.id} (current)` : s.id,
|
|
20255
20734
|
source: s.source,
|
|
20256
20735
|
lastActive: formatLastActive(s.lastActiveAt),
|
|
20257
|
-
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
|
|
20258
20737
|
}));
|
|
20259
20738
|
const headers = {
|
|
20260
20739
|
id: "ID",
|
|
@@ -20327,6 +20806,8 @@ var listSessionsCommand = defineCommand({
|
|
|
20327
20806
|
const currentId = readLastSessionId();
|
|
20328
20807
|
consola.log(renderSessionsTable(response.data, currentId));
|
|
20329
20808
|
} catch (err) {
|
|
20809
|
+
if (isTransportFailure(err))
|
|
20810
|
+
throw err;
|
|
20330
20811
|
reportCommandError(err, { json: args.json });
|
|
20331
20812
|
}
|
|
20332
20813
|
}
|
|
@@ -20365,6 +20846,8 @@ var artifactsCommand = defineCommand({
|
|
|
20365
20846
|
}
|
|
20366
20847
|
consola.log(renderArtifactsTable(response.data));
|
|
20367
20848
|
} catch (err) {
|
|
20849
|
+
if (isTransportFailure(err))
|
|
20850
|
+
throw err;
|
|
20368
20851
|
reportCommandError(err, { json: args.json });
|
|
20369
20852
|
}
|
|
20370
20853
|
}
|
|
@@ -20372,18 +20855,66 @@ var artifactsCommand = defineCommand({
|
|
|
20372
20855
|
var useSessionCommand = defineCommand({
|
|
20373
20856
|
meta: {
|
|
20374
20857
|
name: "use",
|
|
20375
|
-
description: "Switch default session
|
|
20858
|
+
description: "Switch the default session to an existing session, or create a fresh one with --new"
|
|
20376
20859
|
},
|
|
20377
20860
|
args: {
|
|
20378
20861
|
sessionId: {
|
|
20379
20862
|
type: "positional",
|
|
20380
|
-
required:
|
|
20381
|
-
description: "
|
|
20863
|
+
required: false,
|
|
20864
|
+
description: "Existing session id to set as default"
|
|
20865
|
+
},
|
|
20866
|
+
new: {
|
|
20867
|
+
type: "boolean",
|
|
20868
|
+
description: "Create a new session and make it the default"
|
|
20869
|
+
},
|
|
20870
|
+
json: {
|
|
20871
|
+
type: "boolean",
|
|
20872
|
+
alias: "j",
|
|
20873
|
+
description: "Output machine-readable JSON: { sessionId }"
|
|
20382
20874
|
}
|
|
20383
20875
|
},
|
|
20384
|
-
run: ({ args }) => {
|
|
20385
|
-
|
|
20386
|
-
|
|
20876
|
+
run: async ({ args }) => {
|
|
20877
|
+
try {
|
|
20878
|
+
const sessionId = args.sessionId;
|
|
20879
|
+
const apiBaseUrl = resolveApiBaseUrl();
|
|
20880
|
+
if (args.new) {
|
|
20881
|
+
if (sessionId) {
|
|
20882
|
+
throw new Error("--new and <session-id> cannot be combined: pass one, not both.");
|
|
20883
|
+
}
|
|
20884
|
+
const result = await provisionSession({
|
|
20885
|
+
apiBaseUrl,
|
|
20886
|
+
apiKey: resolveApiKey(),
|
|
20887
|
+
note: CLI_DEFAULT_SESSION_NOTE
|
|
20888
|
+
});
|
|
20889
|
+
const created = finalizeProvision(result);
|
|
20890
|
+
if (args.json) {
|
|
20891
|
+
process.stdout.write(`${JSON.stringify({ sessionId: created })}
|
|
20892
|
+
`);
|
|
20893
|
+
return;
|
|
20894
|
+
}
|
|
20895
|
+
consola.success(`Created and switched to session ${created}`);
|
|
20896
|
+
return;
|
|
20897
|
+
}
|
|
20898
|
+
if (!sessionId) {
|
|
20899
|
+
throw new Error("Pass the session id to switch to, or --new to create a new session.");
|
|
20900
|
+
}
|
|
20901
|
+
await ensureSessionExists({
|
|
20902
|
+
apiBaseUrl,
|
|
20903
|
+
apiKey: resolveApiKey(),
|
|
20904
|
+
sessionId
|
|
20905
|
+
});
|
|
20906
|
+
saveLastSessionId(sessionId);
|
|
20907
|
+
if (args.json) {
|
|
20908
|
+
process.stdout.write(`${JSON.stringify({ sessionId })}
|
|
20909
|
+
`);
|
|
20910
|
+
return;
|
|
20911
|
+
}
|
|
20912
|
+
consola.success(`Switched default session to ${sessionId}`);
|
|
20913
|
+
} catch (err) {
|
|
20914
|
+
if (isTransportFailure(err))
|
|
20915
|
+
throw err;
|
|
20916
|
+
reportCommandError(err, { json: args.json });
|
|
20917
|
+
}
|
|
20387
20918
|
}
|
|
20388
20919
|
});
|
|
20389
20920
|
var sessionsCommand = defineCommand({
|
|
@@ -20458,6 +20989,8 @@ var addSkillCommand = defineCommand({
|
|
|
20458
20989
|
}
|
|
20459
20990
|
consola.success(`Installed ${stripControlChars(skill.command)} (${stripControlChars(skill.name)}).`);
|
|
20460
20991
|
} catch (err) {
|
|
20992
|
+
if (isTransportFailure(err))
|
|
20993
|
+
throw err;
|
|
20461
20994
|
reportCommandError(err, { json: args.json });
|
|
20462
20995
|
}
|
|
20463
20996
|
}
|
|
@@ -20509,6 +21042,8 @@ var rmSkillCommand = defineCommand({
|
|
|
20509
21042
|
}
|
|
20510
21043
|
consola.success(`Skill "${args["skill-command"]}" removed from your library.`);
|
|
20511
21044
|
} catch (err) {
|
|
21045
|
+
if (isTransportFailure(err))
|
|
21046
|
+
throw err;
|
|
20512
21047
|
reportCommandError(err, { json: args.json });
|
|
20513
21048
|
}
|
|
20514
21049
|
}
|
|
@@ -20540,6 +21075,8 @@ function makeSetEnabledCommand(name, enabled, description) {
|
|
|
20540
21075
|
}
|
|
20541
21076
|
consola.success(`${enabled ? "Enabled" : "Disabled"} ${stripControlChars(skill.command)}.`);
|
|
20542
21077
|
} catch (err) {
|
|
21078
|
+
if (isTransportFailure(err))
|
|
21079
|
+
throw err;
|
|
20543
21080
|
reportCommandError(err, { json: args.json });
|
|
20544
21081
|
}
|
|
20545
21082
|
}
|
|
@@ -20632,13 +21169,14 @@ var skillsCommand = defineCommand({
|
|
|
20632
21169
|
].join(`
|
|
20633
21170
|
`));
|
|
20634
21171
|
} catch (err) {
|
|
21172
|
+
if (isTransportFailure(err))
|
|
21173
|
+
throw err;
|
|
20635
21174
|
reportCommandError(err, { json: args.json });
|
|
20636
21175
|
}
|
|
20637
21176
|
}
|
|
20638
21177
|
});
|
|
20639
21178
|
|
|
20640
21179
|
// src/commands/whoami.ts
|
|
20641
|
-
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
20642
21180
|
function maskToken(token) {
|
|
20643
21181
|
return `${token.slice(0, 10)}...${token.slice(-6)}`;
|
|
20644
21182
|
}
|
|
@@ -20648,7 +21186,16 @@ var whoamiCommand = defineCommand({
|
|
|
20648
21186
|
description: "Display current authentication status"
|
|
20649
21187
|
},
|
|
20650
21188
|
run: async () => {
|
|
20651
|
-
|
|
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
|
+
}
|
|
20652
21199
|
if (!auth) {
|
|
20653
21200
|
consola.error("Not logged in. Run `msc login` to authenticate - it opens a browser, so a human must complete it.");
|
|
20654
21201
|
process.exit(1);
|
|
@@ -20670,13 +21217,17 @@ var whoamiCommand = defineCommand({
|
|
|
20670
21217
|
const updatedUid = data.uid ?? auth.uid;
|
|
20671
21218
|
const updatedEmail = data.email ?? auth.email;
|
|
20672
21219
|
if (updatedName !== auth.username || updatedUid !== auth.uid || updatedEmail !== auth.email) {
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
|
|
20677
|
-
|
|
20678
|
-
|
|
20679
|
-
|
|
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
|
+
}
|
|
20680
21231
|
}
|
|
20681
21232
|
consola.success("Authenticated");
|
|
20682
21233
|
const lines = [
|
|
@@ -20684,27 +21235,16 @@ var whoamiCommand = defineCommand({
|
|
|
20684
21235
|
updatedUid ? ` UID: ${updatedUid}` : null,
|
|
20685
21236
|
updatedEmail ? ` Email: ${updatedEmail}` : null,
|
|
20686
21237
|
data.apiKeyMasked ? ` API Key: ${data.apiKeyMasked}` : null,
|
|
20687
|
-
` Token: ${maskToken(
|
|
20688
|
-
auth.updated_at ? import_picocolors3.gray(` Saved: ${auth.updated_at}`) : null
|
|
21238
|
+
` Token: ${maskToken(client.apiKey)}`
|
|
20689
21239
|
].filter(Boolean);
|
|
20690
21240
|
consola.log(`${lines.join(`
|
|
20691
21241
|
`)}
|
|
20692
21242
|
`);
|
|
20693
|
-
return;
|
|
20694
21243
|
} catch (err) {
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
|
|
20698
|
-
return;
|
|
20699
|
-
}
|
|
21244
|
+
if (isTransportFailure(err))
|
|
21245
|
+
throw err;
|
|
21246
|
+
reportCommandError(err);
|
|
20700
21247
|
}
|
|
20701
|
-
consola.info("Authentication Status (Cached)");
|
|
20702
|
-
consola.log(` Token: ${maskToken(auth.token)}
|
|
20703
|
-
` + (auth.username ? ` User: ${auth.username}
|
|
20704
|
-
` : "") + (auth.uid ? ` UID: ${auth.uid}
|
|
20705
|
-
` : "") + (auth.email ? ` Email: ${auth.email}
|
|
20706
|
-
` : "") + (auth.updated_at ? import_picocolors3.gray(` Saved: ${auth.updated_at}
|
|
20707
|
-
`) : ""));
|
|
20708
21248
|
}
|
|
20709
21249
|
});
|
|
20710
21250
|
|
|
@@ -20763,6 +21303,8 @@ AGENT QUICKSTART
|
|
|
20763
21303
|
1. msc login # a human must finish this in a browser; instant if
|
|
20764
21304
|
# that browser is already signed in to the web app
|
|
20765
21305
|
2. msc chat "a red car" --json # one turn; the conversation continues across runs
|
|
21306
|
+
# (--new starts a fresh session, --session <id> picks
|
|
21307
|
+
# an existing one; both are verified)
|
|
20766
21308
|
3. msc skills --plaza --json # find a skill, then reference it by leading the
|
|
20767
21309
|
# prompt with /name or %%/name%%
|
|
20768
21310
|
|
|
@@ -20774,6 +21316,10 @@ AGENT QUICKSTART
|
|
|
20774
21316
|
Failure exits 1 with a JSON error carrying message, errCode, httpStatus and
|
|
20775
21317
|
nextStep, which is the action to take:
|
|
20776
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.
|
|
20777
21323
|
A turn reports status "success", or "incomplete" if the stream ended early.
|
|
20778
21324
|
`;
|
|
20779
21325
|
await runMain({
|