@ait-co/console-cli 0.1.5 → 0.1.7
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/dist/cli.mjs +674 -115
- package/dist/cli.mjs.map +1 -1
- package/package.json +4 -2
package/dist/cli.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
|
-
import { chmod, mkdir, mkdtemp, readFile, rename, rm, unlink, writeFile } from "node:fs/promises";
|
|
4
|
-
import { basename, dirname, join, win32 } from "node:path";
|
|
3
|
+
import { access, chmod, mkdir, mkdtemp, readFile, rename, rm, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, resolve, win32 } from "node:path";
|
|
5
5
|
import { homedir, tmpdir } from "node:os";
|
|
6
|
+
import { parse } from "yaml";
|
|
7
|
+
import { imageSize } from "image-size";
|
|
6
8
|
import { spawn } from "node:child_process";
|
|
7
9
|
import { constants } from "node:fs";
|
|
8
10
|
//#region src/api/http.ts
|
|
@@ -119,10 +121,25 @@ async function requestConsoleApi(options) {
|
|
|
119
121
|
headers["Content-Type"] = "application/json";
|
|
120
122
|
init.body = JSON.stringify(options.body);
|
|
121
123
|
}
|
|
122
|
-
|
|
124
|
+
return executeAndUnwrap(url, init, options.fetchImpl);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Send a pre-built `RequestInit` against the console API and unwrap the
|
|
128
|
+
* Toss envelope. Use this when the caller needs to build the body itself
|
|
129
|
+
* (multipart uploads, binary requests, anything that can't live under
|
|
130
|
+
* `requestConsoleApi`'s JSON-body assumption). Cookie header composition
|
|
131
|
+
* and any additional headers remain the caller's responsibility.
|
|
132
|
+
*
|
|
133
|
+
* Exists so `uploadMiniAppResource` doesn't have to re-implement the
|
|
134
|
+
* text→JSON→envelope→error branch in `requestConsoleApi`; drift between
|
|
135
|
+
* the two paths has bitten us once (cf. the refactor in the
|
|
136
|
+
* `app register` review).
|
|
137
|
+
*/
|
|
138
|
+
async function executeAndUnwrap(url, init, fetchImpl) {
|
|
139
|
+
const impl = fetchImpl ?? ((input, i) => fetch(input, i));
|
|
123
140
|
let res;
|
|
124
141
|
try {
|
|
125
|
-
res = await
|
|
142
|
+
res = await impl(url, init);
|
|
126
143
|
} catch (err) {
|
|
127
144
|
throw new NetworkError(url.toString(), err);
|
|
128
145
|
}
|
|
@@ -187,6 +204,60 @@ async function fetchReviewStatus(workspaceId, cookies, opts = {}) {
|
|
|
187
204
|
})
|
|
188
205
|
};
|
|
189
206
|
}
|
|
207
|
+
async function createMiniApp(workspaceId, payload, cookies, opts = {}) {
|
|
208
|
+
return normalizeCreateResult(await requestConsoleApi({
|
|
209
|
+
url: `${BASE$2}/workspaces/${workspaceId}/mini-app/review`,
|
|
210
|
+
method: "POST",
|
|
211
|
+
cookies,
|
|
212
|
+
body: payload,
|
|
213
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
function normalizeCreateResult(raw) {
|
|
217
|
+
if (raw === null || typeof raw !== "object") return {
|
|
218
|
+
miniAppId: void 0,
|
|
219
|
+
reviewState: void 0,
|
|
220
|
+
extra: {}
|
|
221
|
+
};
|
|
222
|
+
const rec = raw;
|
|
223
|
+
const rawId = rec.miniAppId ?? rec.id ?? rec.appId;
|
|
224
|
+
const miniAppId = typeof rawId === "string" || typeof rawId === "number" ? rawId : void 0;
|
|
225
|
+
const rawState = rec.reviewState ?? rec.status;
|
|
226
|
+
return {
|
|
227
|
+
miniAppId,
|
|
228
|
+
reviewState: typeof rawState === "string" ? rawState : void 0,
|
|
229
|
+
extra: rec
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Upload an image to `/resource/:wid/upload?validWidth=W&validHeight=H`
|
|
234
|
+
* and return the CDN URL the server hands back. The endpoint is a
|
|
235
|
+
* multipart/form-data POST; we build a FormData with a single `resource`
|
|
236
|
+
* field because that matches the bundle analysis for the console's
|
|
237
|
+
* uploader, which pairs a `fileName` string field with a `resource`
|
|
238
|
+
* Blob (see VALIDATION-RULES.md → iconUri). Dog-food #23 may reveal that
|
|
239
|
+
* the field name is actually `file` — if so, swap it in one place here.
|
|
240
|
+
*/
|
|
241
|
+
async function uploadMiniAppResource(params, opts = {}) {
|
|
242
|
+
const url = new URL(`${BASE$2}/resource/${params.workspaceId}/upload`);
|
|
243
|
+
url.searchParams.set("validWidth", String(params.validWidth));
|
|
244
|
+
url.searchParams.set("validHeight", String(params.validHeight));
|
|
245
|
+
const form = new FormData();
|
|
246
|
+
const view = new Uint8Array(params.file.buffer.buffer, params.file.buffer.byteOffset, params.file.buffer.byteLength);
|
|
247
|
+
const blob = new Blob([view], { type: params.file.contentType });
|
|
248
|
+
form.append("resource", blob, params.file.fileName);
|
|
249
|
+
form.append("fileName", params.file.fileName);
|
|
250
|
+
const cookieHeader = cookieHeaderFor(url, params.cookies);
|
|
251
|
+
const headers = { Accept: "application/json, text/plain, */*" };
|
|
252
|
+
if (cookieHeader) headers.Cookie = cookieHeader;
|
|
253
|
+
const imageUrl = await executeAndUnwrap(url, {
|
|
254
|
+
method: "POST",
|
|
255
|
+
headers,
|
|
256
|
+
body: form
|
|
257
|
+
}, opts.fetchImpl);
|
|
258
|
+
if (typeof imageUrl !== "string") throw new MalformedResponseError(url.toString(), 200, `expected string imageUrl, got ${typeof imageUrl}`);
|
|
259
|
+
return imageUrl;
|
|
260
|
+
}
|
|
190
261
|
//#endregion
|
|
191
262
|
//#region src/exit.ts
|
|
192
263
|
const ExitCode = {
|
|
@@ -376,14 +447,48 @@ function emitNetworkError(json, message) {
|
|
|
376
447
|
});
|
|
377
448
|
else process.stderr.write(`Network error reaching the console API: ${message}.\n`);
|
|
378
449
|
}
|
|
379
|
-
function emitApiError(json, message) {
|
|
450
|
+
function emitApiError(json, message, details) {
|
|
380
451
|
if (json) emitJson({
|
|
381
452
|
ok: false,
|
|
382
453
|
reason: "api-error",
|
|
454
|
+
...details?.status !== void 0 ? { status: details.status } : {},
|
|
455
|
+
...details?.errorCode !== void 0 ? { errorCode: details.errorCode } : {},
|
|
383
456
|
message
|
|
384
457
|
});
|
|
385
458
|
else process.stderr.write(`Unexpected error: ${message}\n`);
|
|
386
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* Shared auth/network/api dispatch. Every session-scoped command's
|
|
462
|
+
* `catch (err)` block boils down to the same sequence: TossApiError
|
|
463
|
+
* (auth → exit 10, otherwise → exit 17 with status + errorCode),
|
|
464
|
+
* NetworkError (exit 11), fallback (exit 17 with just a message).
|
|
465
|
+
* Exists so we get a single source of truth for the api-error JSON
|
|
466
|
+
* shape — previously each command duplicated the if/else ladder and
|
|
467
|
+
* `register` diverged (it exposed `status`/`errorCode` that the others
|
|
468
|
+
* didn't) until this extraction lined them up.
|
|
469
|
+
*
|
|
470
|
+
* Returns `Promise<void>` but never returns at runtime: every branch
|
|
471
|
+
* awaits `exitAfterFlush` which calls `process.exit`.
|
|
472
|
+
*/
|
|
473
|
+
async function emitFailureFromError(json, err) {
|
|
474
|
+
if (err instanceof TossApiError && err.isAuthError) {
|
|
475
|
+
emitNotAuthenticated(json, "session-expired");
|
|
476
|
+
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
477
|
+
}
|
|
478
|
+
if (err instanceof TossApiError) {
|
|
479
|
+
emitApiError(json, err.message, {
|
|
480
|
+
status: err.status,
|
|
481
|
+
errorCode: err.errorCode
|
|
482
|
+
});
|
|
483
|
+
return exitAfterFlush(ExitCode.ApiError);
|
|
484
|
+
}
|
|
485
|
+
if (err instanceof NetworkError) {
|
|
486
|
+
emitNetworkError(json, err.message);
|
|
487
|
+
return exitAfterFlush(ExitCode.NetworkError);
|
|
488
|
+
}
|
|
489
|
+
emitApiError(json, err.message);
|
|
490
|
+
return exitAfterFlush(ExitCode.ApiError);
|
|
491
|
+
}
|
|
387
492
|
function parsePositiveInt(raw) {
|
|
388
493
|
if (!/^[1-9]\d*$/.test(raw)) return null;
|
|
389
494
|
const n = Number.parseInt(raw, 10);
|
|
@@ -441,6 +546,472 @@ async function resolveWorkspaceContext(args) {
|
|
|
441
546
|
};
|
|
442
547
|
}
|
|
443
548
|
//#endregion
|
|
549
|
+
//#region src/config/app-manifest.ts
|
|
550
|
+
var ManifestError = class extends Error {
|
|
551
|
+
kind;
|
|
552
|
+
field;
|
|
553
|
+
constructor(kind, message, field) {
|
|
554
|
+
super(message);
|
|
555
|
+
this.name = "ManifestError";
|
|
556
|
+
this.kind = kind;
|
|
557
|
+
this.field = field;
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
const DEFAULT_NAMES = ["aitcc.app.yaml", "aitcc.app.json"];
|
|
561
|
+
async function fileExists(path) {
|
|
562
|
+
try {
|
|
563
|
+
await access(path);
|
|
564
|
+
return true;
|
|
565
|
+
} catch {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Resolve the manifest file path. When `explicit` is provided, we use it
|
|
571
|
+
* verbatim (resolved against `cwd`) and require it to exist. Otherwise we
|
|
572
|
+
* auto-detect `aitcc.app.yaml` then `aitcc.app.json` under `cwd`.
|
|
573
|
+
*/
|
|
574
|
+
async function resolveManifestPath(explicit, cwd) {
|
|
575
|
+
if (explicit) {
|
|
576
|
+
const abs = isAbsolute(explicit) ? explicit : resolve(cwd, explicit);
|
|
577
|
+
if (!await fileExists(abs)) throw new ManifestError("invalid-config", `manifest file not found at ${abs}`);
|
|
578
|
+
return abs;
|
|
579
|
+
}
|
|
580
|
+
for (const name of DEFAULT_NAMES) {
|
|
581
|
+
const abs = resolve(cwd, name);
|
|
582
|
+
if (await fileExists(abs)) return abs;
|
|
583
|
+
}
|
|
584
|
+
throw new ManifestError("invalid-config", `no app manifest found (looked for ${DEFAULT_NAMES.join(", ")} in ${cwd})`);
|
|
585
|
+
}
|
|
586
|
+
async function loadAppManifest(path) {
|
|
587
|
+
return validateManifest(parseManifestFile(path, await readFile(path, "utf8")), dirname(path));
|
|
588
|
+
}
|
|
589
|
+
function parseManifestFile(path, raw) {
|
|
590
|
+
const isJson = path.toLowerCase().endsWith(".json");
|
|
591
|
+
try {
|
|
592
|
+
const out = isJson ? JSON.parse(raw) : parse(raw);
|
|
593
|
+
if (out === null || typeof out !== "object" || Array.isArray(out)) throw new ManifestError("invalid-config", `manifest at ${path} is not a mapping`);
|
|
594
|
+
return out;
|
|
595
|
+
} catch (err) {
|
|
596
|
+
if (err instanceof ManifestError) throw err;
|
|
597
|
+
const msg = err.message;
|
|
598
|
+
throw new ManifestError("invalid-config", `failed to parse manifest at ${path}: ${msg}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function requireString(input, key) {
|
|
602
|
+
const v = input[key];
|
|
603
|
+
if (v === void 0 || v === null) throw new ManifestError("missing-required-field", `${key} is required`, key);
|
|
604
|
+
if (typeof v !== "string") throw new ManifestError("invalid-config", `${key} must be a string`, key);
|
|
605
|
+
if (v.trim().length === 0) throw new ManifestError("missing-required-field", `${key} is required`, key);
|
|
606
|
+
return v;
|
|
607
|
+
}
|
|
608
|
+
function optionalString(input, key) {
|
|
609
|
+
const v = input[key];
|
|
610
|
+
if (v === void 0 || v === null) return void 0;
|
|
611
|
+
if (typeof v !== "string") throw new ManifestError("invalid-config", `${key} must be a string when provided`, key);
|
|
612
|
+
return v;
|
|
613
|
+
}
|
|
614
|
+
function requirePath(input, key, configDir) {
|
|
615
|
+
const rel = requireString(input, key);
|
|
616
|
+
return isAbsolute(rel) ? rel : resolve(configDir, rel);
|
|
617
|
+
}
|
|
618
|
+
function optionalPath(input, key, configDir) {
|
|
619
|
+
const rel = optionalString(input, key);
|
|
620
|
+
if (rel === void 0) return void 0;
|
|
621
|
+
return isAbsolute(rel) ? rel : resolve(configDir, rel);
|
|
622
|
+
}
|
|
623
|
+
function requireNumberArray(input, key, { min }) {
|
|
624
|
+
const v = input[key];
|
|
625
|
+
if (!Array.isArray(v)) throw new ManifestError("invalid-config", `${key} must be an array of numbers`, key);
|
|
626
|
+
if (v.length < min) throw new ManifestError("invalid-config", `${key} must contain at least ${min} item(s)`, key);
|
|
627
|
+
return v.map((item, idx) => {
|
|
628
|
+
if (typeof item !== "number" || !Number.isInteger(item)) throw new ManifestError("invalid-config", `${key}[${idx}] must be an integer`, key);
|
|
629
|
+
return item;
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
function optionalStringArray(input, key, { max } = {}) {
|
|
633
|
+
const v = input[key];
|
|
634
|
+
if (v === void 0 || v === null) return [];
|
|
635
|
+
if (!Array.isArray(v)) throw new ManifestError("invalid-config", `${key} must be an array of strings`, key);
|
|
636
|
+
if (max !== void 0 && v.length > max) throw new ManifestError("invalid-config", `${key} accepts at most ${max} entries (got ${v.length})`, key);
|
|
637
|
+
return v.map((item, idx) => {
|
|
638
|
+
if (typeof item !== "string") throw new ManifestError("invalid-config", `${key}[${idx}] must be a string`, key);
|
|
639
|
+
return item;
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function requirePathArray(input, key, configDir, { min }) {
|
|
643
|
+
const v = input[key];
|
|
644
|
+
if (!Array.isArray(v)) throw new ManifestError("invalid-config", `${key} must be an array of paths`, key);
|
|
645
|
+
if (v.length < min) throw new ManifestError("invalid-config", `${key} must contain at least ${min} item(s)`, key);
|
|
646
|
+
return v.map((item, idx) => {
|
|
647
|
+
if (typeof item !== "string" || item.trim().length === 0) throw new ManifestError("invalid-config", `${key}[${idx}] must be a non-empty string`, key);
|
|
648
|
+
return isAbsolute(item) ? item : resolve(configDir, item);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
function optionalPathArray(input, key, configDir) {
|
|
652
|
+
const v = input[key];
|
|
653
|
+
if (v === void 0 || v === null) return [];
|
|
654
|
+
if (!Array.isArray(v)) throw new ManifestError("invalid-config", `${key} must be an array of paths`, key);
|
|
655
|
+
return v.map((item, idx) => {
|
|
656
|
+
if (typeof item !== "string" || item.trim().length === 0) throw new ManifestError("invalid-config", `${key}[${idx}] must be a non-empty string`, key);
|
|
657
|
+
return isAbsolute(item) ? item : resolve(configDir, item);
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
661
|
+
function isValidEmail(v) {
|
|
662
|
+
return EMAIL_REGEX.test(v.toLowerCase());
|
|
663
|
+
}
|
|
664
|
+
const TITLE_EN_REGEX = /^[A-Za-z0-9 :]+$/;
|
|
665
|
+
const DETAIL_DESCRIPTION_MAX_CODEPOINTS = 500;
|
|
666
|
+
function isValidHttpUrl(v) {
|
|
667
|
+
try {
|
|
668
|
+
const parsed = new URL(v);
|
|
669
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
670
|
+
} catch {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function validateManifest(raw, configDir) {
|
|
675
|
+
const titleKo = requireString(raw, "titleKo");
|
|
676
|
+
const titleEn = requireString(raw, "titleEn");
|
|
677
|
+
if (!TITLE_EN_REGEX.test(titleEn)) throw new ManifestError("invalid-config", `titleEn may only contain English letters, digits, spaces, and colons (got "${titleEn}")`, "titleEn");
|
|
678
|
+
const appName = requireString(raw, "appName");
|
|
679
|
+
const csEmail = requireString(raw, "csEmail");
|
|
680
|
+
if (!isValidEmail(csEmail)) throw new ManifestError("invalid-config", `csEmail is not a valid email address (got ${csEmail})`, "csEmail");
|
|
681
|
+
const subtitle = requireString(raw, "subtitle");
|
|
682
|
+
if (subtitle.length > 20) throw new ManifestError("invalid-config", `subtitle must be 20 characters or fewer (got ${subtitle.length})`, "subtitle");
|
|
683
|
+
const description = requireString(raw, "description");
|
|
684
|
+
const descriptionCodepoints = [...description].length;
|
|
685
|
+
if (descriptionCodepoints > DETAIL_DESCRIPTION_MAX_CODEPOINTS) throw new ManifestError("invalid-config", `description must be ${DETAIL_DESCRIPTION_MAX_CODEPOINTS} characters or fewer (got ${descriptionCodepoints})`, "description");
|
|
686
|
+
const homePageUri = optionalString(raw, "homePageUri");
|
|
687
|
+
if (homePageUri !== void 0 && !isValidHttpUrl(homePageUri)) throw new ManifestError("invalid-config", `homePageUri must be a http(s) URL (got ${homePageUri})`, "homePageUri");
|
|
688
|
+
return {
|
|
689
|
+
titleKo,
|
|
690
|
+
titleEn,
|
|
691
|
+
appName,
|
|
692
|
+
homePageUri,
|
|
693
|
+
csEmail,
|
|
694
|
+
logo: requirePath(raw, "logo", configDir),
|
|
695
|
+
logoDarkMode: optionalPath(raw, "logoDarkMode", configDir),
|
|
696
|
+
horizontalThumbnail: requirePath(raw, "horizontalThumbnail", configDir),
|
|
697
|
+
categoryIds: requireNumberArray(raw, "categoryIds", { min: 1 }),
|
|
698
|
+
subtitle,
|
|
699
|
+
description,
|
|
700
|
+
keywords: optionalStringArray(raw, "keywords", { max: 10 }),
|
|
701
|
+
verticalScreenshots: requirePathArray(raw, "verticalScreenshots", configDir, { min: 3 }),
|
|
702
|
+
horizontalScreenshots: optionalPathArray(raw, "horizontalScreenshots", configDir)
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
//#endregion
|
|
706
|
+
//#region src/config/image-validator.ts
|
|
707
|
+
var ImageDimensionError = class extends Error {
|
|
708
|
+
path;
|
|
709
|
+
expected;
|
|
710
|
+
actual;
|
|
711
|
+
reason;
|
|
712
|
+
constructor(args) {
|
|
713
|
+
super(args.message);
|
|
714
|
+
this.name = "ImageDimensionError";
|
|
715
|
+
this.path = args.path;
|
|
716
|
+
this.expected = args.expected;
|
|
717
|
+
this.actual = args.actual;
|
|
718
|
+
this.reason = args.reason;
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
function format(dim) {
|
|
722
|
+
return `${dim.width}x${dim.height}`;
|
|
723
|
+
}
|
|
724
|
+
async function validateImageDimensions(path, expected) {
|
|
725
|
+
let buffer;
|
|
726
|
+
try {
|
|
727
|
+
buffer = await readFile(path);
|
|
728
|
+
} catch (err) {
|
|
729
|
+
throw new ImageDimensionError({
|
|
730
|
+
path,
|
|
731
|
+
expected: format(expected),
|
|
732
|
+
actual: void 0,
|
|
733
|
+
reason: "unreadable",
|
|
734
|
+
message: `could not read image at ${path}: ${err.message}`
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
let dims;
|
|
738
|
+
try {
|
|
739
|
+
dims = imageSize(buffer);
|
|
740
|
+
} catch (err) {
|
|
741
|
+
throw new ImageDimensionError({
|
|
742
|
+
path,
|
|
743
|
+
expected: format(expected),
|
|
744
|
+
actual: void 0,
|
|
745
|
+
reason: "unreadable",
|
|
746
|
+
message: `could not decode image header at ${path}: ${err.message}`
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
if (typeof dims.width !== "number" || typeof dims.height !== "number") throw new ImageDimensionError({
|
|
750
|
+
path,
|
|
751
|
+
expected: format(expected),
|
|
752
|
+
actual: void 0,
|
|
753
|
+
reason: "unreadable",
|
|
754
|
+
message: `image header at ${path} did not expose width/height`
|
|
755
|
+
});
|
|
756
|
+
if (dims.width !== expected.width || dims.height !== expected.height) {
|
|
757
|
+
const actual = `${dims.width}x${dims.height}`;
|
|
758
|
+
throw new ImageDimensionError({
|
|
759
|
+
path,
|
|
760
|
+
expected: format(expected),
|
|
761
|
+
actual,
|
|
762
|
+
reason: "mismatch",
|
|
763
|
+
message: `image ${path} has dimensions ${actual}; expected ${format(expected)}`
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const DIMENSIONS = {
|
|
768
|
+
logo: {
|
|
769
|
+
width: 600,
|
|
770
|
+
height: 600
|
|
771
|
+
},
|
|
772
|
+
horizontalThumbnail: {
|
|
773
|
+
width: 1932,
|
|
774
|
+
height: 828
|
|
775
|
+
},
|
|
776
|
+
verticalScreenshot: {
|
|
777
|
+
width: 636,
|
|
778
|
+
height: 1048
|
|
779
|
+
},
|
|
780
|
+
horizontalScreenshot: {
|
|
781
|
+
width: 1504,
|
|
782
|
+
height: 741
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
//#endregion
|
|
786
|
+
//#region src/commands/register-payload.ts
|
|
787
|
+
function buildSubmitPayload(manifest, urls) {
|
|
788
|
+
const images = [
|
|
789
|
+
{
|
|
790
|
+
imageUrl: urls.horizontalThumbnail,
|
|
791
|
+
imageType: "THUMBNAIL",
|
|
792
|
+
orientation: "HORIZONTAL"
|
|
793
|
+
},
|
|
794
|
+
...urls.verticalScreenshots.map((u) => ({
|
|
795
|
+
imageUrl: u,
|
|
796
|
+
imageType: "PREVIEW",
|
|
797
|
+
orientation: "VERTICAL"
|
|
798
|
+
})),
|
|
799
|
+
...urls.horizontalScreenshots.map((u) => ({
|
|
800
|
+
imageUrl: u,
|
|
801
|
+
imageType: "PREVIEW",
|
|
802
|
+
orientation: "HORIZONTAL"
|
|
803
|
+
}))
|
|
804
|
+
];
|
|
805
|
+
return {
|
|
806
|
+
title: manifest.titleKo,
|
|
807
|
+
titleEn: manifest.titleEn,
|
|
808
|
+
appName: manifest.appName,
|
|
809
|
+
iconUri: urls.logo,
|
|
810
|
+
status: "PREPARE",
|
|
811
|
+
csEmail: manifest.csEmail,
|
|
812
|
+
description: manifest.subtitle,
|
|
813
|
+
detailDescription: manifest.description,
|
|
814
|
+
images,
|
|
815
|
+
impression: {
|
|
816
|
+
keywordList: manifest.keywords,
|
|
817
|
+
categoryList: manifest.categoryIds.map((id) => ({ id }))
|
|
818
|
+
},
|
|
819
|
+
...urls.logoDarkMode !== void 0 ? { darkModeIconUri: urls.logoDarkMode } : {},
|
|
820
|
+
...manifest.homePageUri !== void 0 ? { homePageUri: manifest.homePageUri } : {}
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
//#endregion
|
|
824
|
+
//#region src/commands/register.ts
|
|
825
|
+
async function runRegister(args, deps = {}) {
|
|
826
|
+
const ctx = await resolveWorkspaceContext({
|
|
827
|
+
json: args.json,
|
|
828
|
+
...args.workspace !== void 0 ? { workspace: args.workspace } : {}
|
|
829
|
+
});
|
|
830
|
+
if (!ctx) return;
|
|
831
|
+
const { session, workspaceId } = ctx;
|
|
832
|
+
const manifest = await loadAndValidateManifest(args, deps);
|
|
833
|
+
if (!manifest) return;
|
|
834
|
+
if (!args.dryRun && !args.acceptTerms) {
|
|
835
|
+
emitTermsNotAccepted(args.json);
|
|
836
|
+
await exitAfterFlush(ExitCode.Usage);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
if (args.dryRun) {
|
|
841
|
+
const payload = buildSubmitPayload(manifest, {
|
|
842
|
+
logo: "<dry-run:logo>",
|
|
843
|
+
logoDarkMode: manifest.logoDarkMode !== void 0 ? "<dry-run:logoDarkMode>" : void 0,
|
|
844
|
+
horizontalThumbnail: "<dry-run:horizontalThumbnail>",
|
|
845
|
+
verticalScreenshots: manifest.verticalScreenshots.map((_, i) => `<dry-run:verticalScreenshots[${i}]>`),
|
|
846
|
+
horizontalScreenshots: manifest.horizontalScreenshots.map((_, i) => `<dry-run:horizontalScreenshots[${i}]>`)
|
|
847
|
+
});
|
|
848
|
+
emitDryRun(args.json, workspaceId, payload);
|
|
849
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
850
|
+
}
|
|
851
|
+
const payload = buildSubmitPayload(manifest, await uploadAllImages(workspaceId, manifest, session.cookies, deps));
|
|
852
|
+
const result = await (deps.submitImpl ?? ((wid, p, c) => createMiniApp(wid, p, c)))(workspaceId, payload, session.cookies);
|
|
853
|
+
emitSuccess(args.json, workspaceId, result);
|
|
854
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
855
|
+
} catch (err) {
|
|
856
|
+
return emitFailureAndExit(args.json, err);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
async function loadAndValidateManifest(args, deps) {
|
|
860
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
861
|
+
let manifest;
|
|
862
|
+
try {
|
|
863
|
+
manifest = await loadAppManifest(await resolveManifestPath(args.config, cwd));
|
|
864
|
+
} catch (err) {
|
|
865
|
+
if (err instanceof ManifestError) {
|
|
866
|
+
emitManifestError(args.json, err);
|
|
867
|
+
await exitAfterFlush(ExitCode.Usage);
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
throw err;
|
|
871
|
+
}
|
|
872
|
+
try {
|
|
873
|
+
await validateImageDimensions(manifest.logo, DIMENSIONS.logo);
|
|
874
|
+
if (manifest.logoDarkMode !== void 0) await validateImageDimensions(manifest.logoDarkMode, DIMENSIONS.logo);
|
|
875
|
+
await validateImageDimensions(manifest.horizontalThumbnail, DIMENSIONS.horizontalThumbnail);
|
|
876
|
+
for (const p of manifest.verticalScreenshots) await validateImageDimensions(p, DIMENSIONS.verticalScreenshot);
|
|
877
|
+
for (const p of manifest.horizontalScreenshots) await validateImageDimensions(p, DIMENSIONS.horizontalScreenshot);
|
|
878
|
+
} catch (err) {
|
|
879
|
+
if (err instanceof ImageDimensionError) {
|
|
880
|
+
emitImageDimensionError(args.json, err);
|
|
881
|
+
await exitAfterFlush(ExitCode.Usage);
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
throw err;
|
|
885
|
+
}
|
|
886
|
+
return manifest;
|
|
887
|
+
}
|
|
888
|
+
async function uploadAllImages(workspaceId, manifest, cookies, deps) {
|
|
889
|
+
const uploadImpl = deps.uploadImpl ?? ((p) => uploadMiniAppResource(p));
|
|
890
|
+
const logo = await uploadOne(uploadImpl, {
|
|
891
|
+
workspaceId,
|
|
892
|
+
validWidth: DIMENSIONS.logo.width,
|
|
893
|
+
validHeight: DIMENSIONS.logo.height,
|
|
894
|
+
cookies,
|
|
895
|
+
path: manifest.logo
|
|
896
|
+
});
|
|
897
|
+
const logoDarkMode = manifest.logoDarkMode !== void 0 ? await uploadOne(uploadImpl, {
|
|
898
|
+
workspaceId,
|
|
899
|
+
validWidth: DIMENSIONS.logo.width,
|
|
900
|
+
validHeight: DIMENSIONS.logo.height,
|
|
901
|
+
cookies,
|
|
902
|
+
path: manifest.logoDarkMode
|
|
903
|
+
}) : void 0;
|
|
904
|
+
const horizontalThumbnail = await uploadOne(uploadImpl, {
|
|
905
|
+
workspaceId,
|
|
906
|
+
validWidth: DIMENSIONS.horizontalThumbnail.width,
|
|
907
|
+
validHeight: DIMENSIONS.horizontalThumbnail.height,
|
|
908
|
+
cookies,
|
|
909
|
+
path: manifest.horizontalThumbnail
|
|
910
|
+
});
|
|
911
|
+
const verticalScreenshots = [];
|
|
912
|
+
for (const p of manifest.verticalScreenshots) verticalScreenshots.push(await uploadOne(uploadImpl, {
|
|
913
|
+
workspaceId,
|
|
914
|
+
validWidth: DIMENSIONS.verticalScreenshot.width,
|
|
915
|
+
validHeight: DIMENSIONS.verticalScreenshot.height,
|
|
916
|
+
cookies,
|
|
917
|
+
path: p
|
|
918
|
+
}));
|
|
919
|
+
const horizontalScreenshots = [];
|
|
920
|
+
for (const p of manifest.horizontalScreenshots) horizontalScreenshots.push(await uploadOne(uploadImpl, {
|
|
921
|
+
workspaceId,
|
|
922
|
+
validWidth: DIMENSIONS.horizontalScreenshot.width,
|
|
923
|
+
validHeight: DIMENSIONS.horizontalScreenshot.height,
|
|
924
|
+
cookies,
|
|
925
|
+
path: p
|
|
926
|
+
}));
|
|
927
|
+
return {
|
|
928
|
+
logo,
|
|
929
|
+
logoDarkMode,
|
|
930
|
+
horizontalThumbnail,
|
|
931
|
+
verticalScreenshots,
|
|
932
|
+
horizontalScreenshots
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
async function uploadOne(uploadImpl, input) {
|
|
936
|
+
const buffer = await readFile(input.path);
|
|
937
|
+
return uploadImpl({
|
|
938
|
+
workspaceId: input.workspaceId,
|
|
939
|
+
validWidth: input.validWidth,
|
|
940
|
+
validHeight: input.validHeight,
|
|
941
|
+
cookies: input.cookies,
|
|
942
|
+
file: {
|
|
943
|
+
buffer,
|
|
944
|
+
fileName: basename(input.path),
|
|
945
|
+
contentType: "image/png"
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
function emitManifestError(json, err) {
|
|
950
|
+
if (json) if (err.kind === "missing-required-field") emitJson({
|
|
951
|
+
ok: false,
|
|
952
|
+
reason: "missing-required-field",
|
|
953
|
+
field: err.field ?? null,
|
|
954
|
+
message: err.message
|
|
955
|
+
});
|
|
956
|
+
else emitJson({
|
|
957
|
+
ok: false,
|
|
958
|
+
reason: "invalid-config",
|
|
959
|
+
message: err.message
|
|
960
|
+
});
|
|
961
|
+
else process.stderr.write(`${err.message}\n`);
|
|
962
|
+
}
|
|
963
|
+
function emitImageDimensionError(json, err) {
|
|
964
|
+
if (json) if (err.reason === "mismatch") emitJson({
|
|
965
|
+
ok: false,
|
|
966
|
+
reason: "image-dimension-mismatch",
|
|
967
|
+
path: err.path,
|
|
968
|
+
expected: err.expected,
|
|
969
|
+
actual: err.actual ?? null,
|
|
970
|
+
message: err.message
|
|
971
|
+
});
|
|
972
|
+
else emitJson({
|
|
973
|
+
ok: false,
|
|
974
|
+
reason: "image-unreadable",
|
|
975
|
+
path: err.path,
|
|
976
|
+
message: err.message
|
|
977
|
+
});
|
|
978
|
+
else process.stderr.write(`${err.message}\n`);
|
|
979
|
+
}
|
|
980
|
+
function emitTermsNotAccepted(json) {
|
|
981
|
+
const message = "The console requires several legal-agreement checkboxes before submitting a mini-app for review. Re-run with --accept-terms to attest that you have read and agree to each of them (see VALIDATION-RULES.md or the console UI), or use --dry-run to preview the payload without submitting.";
|
|
982
|
+
if (json) emitJson({
|
|
983
|
+
ok: false,
|
|
984
|
+
reason: "terms-not-accepted",
|
|
985
|
+
message
|
|
986
|
+
});
|
|
987
|
+
else process.stderr.write(`${message}\n`);
|
|
988
|
+
}
|
|
989
|
+
function emitDryRun(json, workspaceId, payload) {
|
|
990
|
+
if (json) emitJson({
|
|
991
|
+
ok: true,
|
|
992
|
+
dryRun: true,
|
|
993
|
+
workspaceId,
|
|
994
|
+
payload
|
|
995
|
+
});
|
|
996
|
+
else {
|
|
997
|
+
process.stdout.write("[dry-run] Would POST to ");
|
|
998
|
+
process.stdout.write(`https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole/workspaces/${workspaceId}/mini-app/review\n`);
|
|
999
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
function emitSuccess(json, workspaceId, result) {
|
|
1003
|
+
if (json) emitJson({
|
|
1004
|
+
ok: true,
|
|
1005
|
+
workspaceId,
|
|
1006
|
+
appId: result.miniAppId ?? null,
|
|
1007
|
+
reviewState: result.reviewState ?? null
|
|
1008
|
+
});
|
|
1009
|
+
else process.stdout.write(`Registered mini-app ${result.miniAppId ?? "(id unknown)"} in workspace ${workspaceId} (reviewState=${result.reviewState ?? "unknown"}).\n`);
|
|
1010
|
+
}
|
|
1011
|
+
async function emitFailureAndExit(json, err) {
|
|
1012
|
+
return emitFailureFromError(json, err);
|
|
1013
|
+
}
|
|
1014
|
+
//#endregion
|
|
444
1015
|
//#region src/commands/app.ts
|
|
445
1016
|
function findReviewEntry(reviewEntries, appId) {
|
|
446
1017
|
const target = String(appId);
|
|
@@ -460,72 +1031,105 @@ const appCommand = defineCommand({
|
|
|
460
1031
|
name: "app",
|
|
461
1032
|
description: "Inspect mini-apps in a workspace."
|
|
462
1033
|
},
|
|
463
|
-
subCommands: {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
args: {
|
|
469
|
-
workspace: {
|
|
470
|
-
type: "string",
|
|
471
|
-
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
1034
|
+
subCommands: {
|
|
1035
|
+
ls: defineCommand({
|
|
1036
|
+
meta: {
|
|
1037
|
+
name: "ls",
|
|
1038
|
+
description: "List mini-apps in the selected workspace."
|
|
472
1039
|
},
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const { session, workspaceId } = ctx;
|
|
483
|
-
try {
|
|
484
|
-
const [apps, review] = await Promise.all([fetchMiniApps(workspaceId, session.cookies), fetchReviewStatus(workspaceId, session.cookies)]);
|
|
485
|
-
if (args.json) {
|
|
486
|
-
const joined = apps.map((app) => {
|
|
487
|
-
const reviewState = reviewStateFor(findReviewEntry(review.miniApps, app.id));
|
|
488
|
-
return {
|
|
489
|
-
id: app.id,
|
|
490
|
-
name: app.name ?? null,
|
|
491
|
-
...reviewState !== void 0 ? { reviewState } : {},
|
|
492
|
-
extra: app.extra
|
|
493
|
-
};
|
|
494
|
-
});
|
|
495
|
-
emitJson({
|
|
496
|
-
ok: true,
|
|
497
|
-
workspaceId,
|
|
498
|
-
hasPolicyViolation: review.hasPolicyViolation,
|
|
499
|
-
apps: joined
|
|
500
|
-
});
|
|
501
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
1040
|
+
args: {
|
|
1041
|
+
workspace: {
|
|
1042
|
+
type: "string",
|
|
1043
|
+
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
1044
|
+
},
|
|
1045
|
+
json: {
|
|
1046
|
+
type: "boolean",
|
|
1047
|
+
description: "Emit machine-readable JSON to stdout.",
|
|
1048
|
+
default: false
|
|
502
1049
|
}
|
|
503
|
-
|
|
504
|
-
|
|
1050
|
+
},
|
|
1051
|
+
async run({ args }) {
|
|
1052
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1053
|
+
if (!ctx) return;
|
|
1054
|
+
const { session, workspaceId } = ctx;
|
|
1055
|
+
try {
|
|
1056
|
+
const [apps, review] = await Promise.all([fetchMiniApps(workspaceId, session.cookies), fetchReviewStatus(workspaceId, session.cookies)]);
|
|
1057
|
+
if (args.json) {
|
|
1058
|
+
const joined = apps.map((app) => {
|
|
1059
|
+
const reviewState = reviewStateFor(findReviewEntry(review.miniApps, app.id));
|
|
1060
|
+
return {
|
|
1061
|
+
id: app.id,
|
|
1062
|
+
name: app.name ?? null,
|
|
1063
|
+
...reviewState !== void 0 ? { reviewState } : {},
|
|
1064
|
+
extra: app.extra
|
|
1065
|
+
};
|
|
1066
|
+
});
|
|
1067
|
+
emitJson({
|
|
1068
|
+
ok: true,
|
|
1069
|
+
workspaceId,
|
|
1070
|
+
hasPolicyViolation: review.hasPolicyViolation,
|
|
1071
|
+
apps: joined
|
|
1072
|
+
});
|
|
1073
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1074
|
+
}
|
|
1075
|
+
if (apps.length === 0) {
|
|
1076
|
+
process.stdout.write(`No apps in workspace ${workspaceId}.\n`);
|
|
1077
|
+
if (review.hasPolicyViolation) process.stderr.write("Note: workspace-wide policy violation flag is set.\n");
|
|
1078
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1079
|
+
}
|
|
1080
|
+
for (const app of apps) {
|
|
1081
|
+
const reviewState = reviewStateFor(findReviewEntry(review.miniApps, app.id)) ?? "-";
|
|
1082
|
+
const name = app.name ?? "(unnamed)";
|
|
1083
|
+
process.stdout.write(`${app.id}\t${name}\t${reviewState}\n`);
|
|
1084
|
+
}
|
|
505
1085
|
if (review.hasPolicyViolation) process.stderr.write("Note: workspace-wide policy violation flag is set.\n");
|
|
506
1086
|
return exitAfterFlush(ExitCode.Ok);
|
|
1087
|
+
} catch (err) {
|
|
1088
|
+
return emitFailureFromError(args.json, err);
|
|
507
1089
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
1090
|
+
}
|
|
1091
|
+
}),
|
|
1092
|
+
register: defineCommand({
|
|
1093
|
+
meta: {
|
|
1094
|
+
name: "register",
|
|
1095
|
+
description: "Register a mini-app in the selected workspace from a YAML/JSON manifest. Uploads logo/thumbnail/screenshots, then submits the create payload."
|
|
1096
|
+
},
|
|
1097
|
+
args: {
|
|
1098
|
+
workspace: {
|
|
1099
|
+
type: "string",
|
|
1100
|
+
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
1101
|
+
},
|
|
1102
|
+
config: {
|
|
1103
|
+
type: "string",
|
|
1104
|
+
description: "Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.json`."
|
|
1105
|
+
},
|
|
1106
|
+
"dry-run": {
|
|
1107
|
+
type: "boolean",
|
|
1108
|
+
description: "Validate manifest + images and print the inferred submit payload; no uploads.",
|
|
1109
|
+
default: false
|
|
1110
|
+
},
|
|
1111
|
+
"accept-terms": {
|
|
1112
|
+
type: "boolean",
|
|
1113
|
+
description: "Attest to the required console legal-agreement checkboxes (see VALIDATION-RULES.md). Required for real submits.",
|
|
1114
|
+
default: false
|
|
1115
|
+
},
|
|
1116
|
+
json: {
|
|
1117
|
+
type: "boolean",
|
|
1118
|
+
description: "Emit machine-readable JSON to stdout.",
|
|
1119
|
+
default: false
|
|
523
1120
|
}
|
|
524
|
-
|
|
525
|
-
|
|
1121
|
+
},
|
|
1122
|
+
async run({ args }) {
|
|
1123
|
+
await runRegister({
|
|
1124
|
+
json: args.json,
|
|
1125
|
+
dryRun: args["dry-run"],
|
|
1126
|
+
acceptTerms: args["accept-terms"],
|
|
1127
|
+
...args.workspace !== void 0 ? { workspace: args.workspace } : {},
|
|
1128
|
+
...args.config !== void 0 ? { config: args.config } : {}
|
|
1129
|
+
});
|
|
526
1130
|
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
1131
|
+
})
|
|
1132
|
+
}
|
|
529
1133
|
});
|
|
530
1134
|
//#endregion
|
|
531
1135
|
//#region src/api/api-keys.ts
|
|
@@ -605,16 +1209,7 @@ const keysCommand = defineCommand({
|
|
|
605
1209
|
}
|
|
606
1210
|
return exitAfterFlush(ExitCode.Ok);
|
|
607
1211
|
} catch (err) {
|
|
608
|
-
|
|
609
|
-
emitNotAuthenticated(args.json, "session-expired");
|
|
610
|
-
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
611
|
-
}
|
|
612
|
-
if (err instanceof NetworkError) {
|
|
613
|
-
emitNetworkError(args.json, err.message);
|
|
614
|
-
return exitAfterFlush(ExitCode.NetworkError);
|
|
615
|
-
}
|
|
616
|
-
emitApiError(args.json, err.message);
|
|
617
|
-
return exitAfterFlush(ExitCode.ApiError);
|
|
1212
|
+
return emitFailureFromError(args.json, err);
|
|
618
1213
|
}
|
|
619
1214
|
}
|
|
620
1215
|
}) }
|
|
@@ -1367,16 +1962,7 @@ const membersCommand = defineCommand({
|
|
|
1367
1962
|
for (const m of members) process.stdout.write(`${m.bizUserNo}\t${m.name}\t${m.email}\t${m.role}\t${m.status}\n`);
|
|
1368
1963
|
return exitAfterFlush(ExitCode.Ok);
|
|
1369
1964
|
} catch (err) {
|
|
1370
|
-
|
|
1371
|
-
emitNotAuthenticated(args.json, "session-expired");
|
|
1372
|
-
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
1373
|
-
}
|
|
1374
|
-
if (err instanceof NetworkError) {
|
|
1375
|
-
emitNetworkError(args.json, err.message);
|
|
1376
|
-
return exitAfterFlush(ExitCode.NetworkError);
|
|
1377
|
-
}
|
|
1378
|
-
emitApiError(args.json, err.message);
|
|
1379
|
-
return exitAfterFlush(ExitCode.ApiError);
|
|
1965
|
+
return emitFailureFromError(args.json, err);
|
|
1380
1966
|
}
|
|
1381
1967
|
}
|
|
1382
1968
|
}) }
|
|
@@ -1494,7 +2080,7 @@ function resolveVersion() {
|
|
|
1494
2080
|
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
1495
2081
|
} catch {}
|
|
1496
2082
|
try {
|
|
1497
|
-
return "0.1.
|
|
2083
|
+
return "0.1.7";
|
|
1498
2084
|
} catch {}
|
|
1499
2085
|
return "0.0.0-dev";
|
|
1500
2086
|
}
|
|
@@ -1928,16 +2514,7 @@ const workspaceCommand = defineCommand({
|
|
|
1928
2514
|
if (current === void 0) process.stderr.write("No workspace selected. Run `aitcc workspace use <id>`.\n");
|
|
1929
2515
|
return exitAfterFlush(ExitCode.Ok);
|
|
1930
2516
|
} catch (err) {
|
|
1931
|
-
|
|
1932
|
-
emitNotAuthenticated(args.json, "session-expired");
|
|
1933
|
-
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
1934
|
-
}
|
|
1935
|
-
if (err instanceof NetworkError) {
|
|
1936
|
-
emitNetworkError(args.json, err.message);
|
|
1937
|
-
return exitAfterFlush(ExitCode.NetworkError);
|
|
1938
|
-
}
|
|
1939
|
-
emitApiError(args.json, err.message);
|
|
1940
|
-
return exitAfterFlush(ExitCode.ApiError);
|
|
2517
|
+
return emitFailureFromError(args.json, err);
|
|
1941
2518
|
}
|
|
1942
2519
|
}
|
|
1943
2520
|
}),
|
|
@@ -1999,16 +2576,7 @@ const workspaceCommand = defineCommand({
|
|
|
1999
2576
|
else process.stdout.write(`Using workspace ${match.workspaceId} (${match.workspaceName}).\n`);
|
|
2000
2577
|
return exitAfterFlush(ExitCode.Ok);
|
|
2001
2578
|
} catch (err) {
|
|
2002
|
-
|
|
2003
|
-
emitNotAuthenticated(args.json, "session-expired");
|
|
2004
|
-
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
2005
|
-
}
|
|
2006
|
-
if (err instanceof NetworkError) {
|
|
2007
|
-
emitNetworkError(args.json, err.message);
|
|
2008
|
-
return exitAfterFlush(ExitCode.NetworkError);
|
|
2009
|
-
}
|
|
2010
|
-
emitApiError(args.json, err.message);
|
|
2011
|
-
return exitAfterFlush(ExitCode.ApiError);
|
|
2579
|
+
return emitFailureFromError(args.json, err);
|
|
2012
2580
|
}
|
|
2013
2581
|
}
|
|
2014
2582
|
}),
|
|
@@ -2073,16 +2641,7 @@ const workspaceCommand = defineCommand({
|
|
|
2073
2641
|
if (detail.extra) for (const [k, v] of Object.entries(detail.extra)) process.stdout.write(` ${k}: ${formatScalar(v)}\n`);
|
|
2074
2642
|
return exitAfterFlush(ExitCode.Ok);
|
|
2075
2643
|
} catch (err) {
|
|
2076
|
-
|
|
2077
|
-
emitNotAuthenticated(args.json, "session-expired");
|
|
2078
|
-
return exitAfterFlush(ExitCode.NotAuthenticated);
|
|
2079
|
-
}
|
|
2080
|
-
if (err instanceof NetworkError) {
|
|
2081
|
-
emitNetworkError(args.json, err.message);
|
|
2082
|
-
return exitAfterFlush(ExitCode.NetworkError);
|
|
2083
|
-
}
|
|
2084
|
-
emitApiError(args.json, err.message);
|
|
2085
|
-
return exitAfterFlush(ExitCode.ApiError);
|
|
2644
|
+
return emitFailureFromError(args.json, err);
|
|
2086
2645
|
}
|
|
2087
2646
|
}
|
|
2088
2647
|
})
|