@ait-co/console-cli 0.1.10 → 0.1.11
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 +1348 -188
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -161,10 +161,10 @@ async function executeAndUnwrap(url, init, fetchImpl) {
|
|
|
161
161
|
}
|
|
162
162
|
//#endregion
|
|
163
163
|
//#region src/api/mini-apps.ts
|
|
164
|
-
const BASE$
|
|
164
|
+
const BASE$3 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
165
165
|
async function fetchMiniApps(workspaceId, cookies, opts = {}) {
|
|
166
166
|
const raw = await requestConsoleApi({
|
|
167
|
-
url: `${BASE$
|
|
167
|
+
url: `${BASE$3}/workspaces/${workspaceId}/mini-app`,
|
|
168
168
|
cookies,
|
|
169
169
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
170
170
|
});
|
|
@@ -187,7 +187,7 @@ function normalizeMiniApp(item, workspaceId, index) {
|
|
|
187
187
|
}
|
|
188
188
|
async function fetchReviewStatus(workspaceId, cookies, opts = {}) {
|
|
189
189
|
const raw = await requestConsoleApi({
|
|
190
|
-
url: `${BASE$
|
|
190
|
+
url: `${BASE$3}/workspaces/${workspaceId}/mini-apps/review-status`,
|
|
191
191
|
cookies,
|
|
192
192
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
193
193
|
});
|
|
@@ -206,7 +206,7 @@ async function fetchReviewStatus(workspaceId, cookies, opts = {}) {
|
|
|
206
206
|
}
|
|
207
207
|
async function fetchMiniAppWithDraft(workspaceId, miniAppId, cookies, opts = {}) {
|
|
208
208
|
const raw = await requestConsoleApi({
|
|
209
|
-
url: `${BASE$
|
|
209
|
+
url: `${BASE$3}/workspaces/${workspaceId}/mini-app/${miniAppId}/with-draft`,
|
|
210
210
|
cookies,
|
|
211
211
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
212
212
|
});
|
|
@@ -222,9 +222,148 @@ async function fetchMiniAppWithDraft(workspaceId, miniAppId, cookies, opts = {})
|
|
|
222
222
|
function isRecordOrNull(v) {
|
|
223
223
|
return v === null || typeof v === "object" && !Array.isArray(v);
|
|
224
224
|
}
|
|
225
|
+
async function fetchMiniAppRatings(params, cookies, opts = {}) {
|
|
226
|
+
const page = params.page ?? 0;
|
|
227
|
+
const size = params.size ?? 20;
|
|
228
|
+
const sortField = params.sortField ?? "CREATED_AT";
|
|
229
|
+
const sortDirection = params.sortDirection ?? "DESC";
|
|
230
|
+
const raw = await requestConsoleApi({
|
|
231
|
+
url: `${BASE$3}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/app-ratings?page=${page}&size=${size}&sortField=${sortField}&sortDirection=${sortDirection}`,
|
|
232
|
+
cookies,
|
|
233
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
234
|
+
});
|
|
235
|
+
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected ratings shape for app=${params.miniAppId}`);
|
|
236
|
+
const rec = raw;
|
|
237
|
+
const ratingsRaw = rec.ratings;
|
|
238
|
+
if (!Array.isArray(ratingsRaw)) throw new Error(`Unexpected ratings shape: ratings is not an array (app=${params.miniAppId})`);
|
|
239
|
+
const ratings = ratingsRaw.map((r) => {
|
|
240
|
+
if (r === null || typeof r !== "object") return {};
|
|
241
|
+
return r;
|
|
242
|
+
});
|
|
243
|
+
const pagingRaw = rec.paging;
|
|
244
|
+
if (pagingRaw === null || typeof pagingRaw !== "object") throw new Error(`Unexpected ratings shape: paging missing (app=${params.miniAppId})`);
|
|
245
|
+
const p = pagingRaw;
|
|
246
|
+
return {
|
|
247
|
+
ratings,
|
|
248
|
+
paging: {
|
|
249
|
+
pageNumber: typeof p.pageNumber === "number" ? p.pageNumber : page,
|
|
250
|
+
pageSize: typeof p.pageSize === "number" ? p.pageSize : size,
|
|
251
|
+
hasNext: Boolean(p.hasNext),
|
|
252
|
+
totalCount: typeof p.totalCount === "number" ? p.totalCount : 0
|
|
253
|
+
},
|
|
254
|
+
averageRating: typeof rec.averageRating === "number" ? rec.averageRating : 0,
|
|
255
|
+
totalReviewCount: typeof rec.totalReviewCount === "number" ? rec.totalReviewCount : 0
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
async function fetchUserReports(params, cookies, opts = {}) {
|
|
259
|
+
const pageSize = params.pageSize ?? 20;
|
|
260
|
+
const qs = new URLSearchParams();
|
|
261
|
+
qs.set("pageSize", String(pageSize));
|
|
262
|
+
if (params.cursor !== void 0) qs.set("cursor", params.cursor);
|
|
263
|
+
const raw = await requestConsoleApi({
|
|
264
|
+
url: `${BASE$3}/workspaces/${params.workspaceId}/mini-apps/${params.miniAppId}/user-reports?` + qs.toString(),
|
|
265
|
+
cookies,
|
|
266
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
267
|
+
});
|
|
268
|
+
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected user-reports shape for app=${params.miniAppId}`);
|
|
269
|
+
const rec = raw;
|
|
270
|
+
const reportsRaw = rec.reports;
|
|
271
|
+
if (!Array.isArray(reportsRaw)) throw new Error(`Unexpected user-reports shape: reports is not an array (app=${params.miniAppId})`);
|
|
272
|
+
return {
|
|
273
|
+
reports: reportsRaw.map((r) => {
|
|
274
|
+
if (r === null || typeof r !== "object") return {};
|
|
275
|
+
return r;
|
|
276
|
+
}),
|
|
277
|
+
nextCursor: typeof rec.nextCursor === "string" ? rec.nextCursor : null,
|
|
278
|
+
hasMore: Boolean(rec.hasMore)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async function fetchBundles(params, cookies, opts = {}) {
|
|
282
|
+
const qs = new URLSearchParams();
|
|
283
|
+
if (params.page !== void 0) qs.set("page", String(params.page));
|
|
284
|
+
if (params.tested !== void 0) qs.set("tested", String(params.tested));
|
|
285
|
+
if (params.deployStatus !== void 0) qs.set("deployStatus", params.deployStatus);
|
|
286
|
+
const query = qs.toString();
|
|
287
|
+
const raw = await requestConsoleApi({
|
|
288
|
+
url: `${BASE$3}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles` + (query ? `?${query}` : ""),
|
|
289
|
+
cookies,
|
|
290
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
291
|
+
});
|
|
292
|
+
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected bundles shape for app=${params.miniAppId}`);
|
|
293
|
+
const rec = raw;
|
|
294
|
+
const contentsRaw = rec.contents;
|
|
295
|
+
if (!Array.isArray(contentsRaw)) throw new Error(`Unexpected bundles shape: contents is not an array (app=${params.miniAppId})`);
|
|
296
|
+
return {
|
|
297
|
+
contents: contentsRaw.map((b) => {
|
|
298
|
+
if (b === null || typeof b !== "object") return {};
|
|
299
|
+
return b;
|
|
300
|
+
}),
|
|
301
|
+
totalPage: typeof rec.totalPage === "number" ? rec.totalPage : 0,
|
|
302
|
+
currentPage: typeof rec.currentPage === "number" ? rec.currentPage : 0
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
async function fetchConversionMetrics(params, cookies, opts = {}) {
|
|
306
|
+
const qs = new URLSearchParams();
|
|
307
|
+
qs.set("refresh", String(params.refresh ?? false));
|
|
308
|
+
qs.set("timeUnitType", params.timeUnitType);
|
|
309
|
+
qs.set("startDate", params.startDate);
|
|
310
|
+
qs.set("endDate", params.endDate);
|
|
311
|
+
const raw = await requestConsoleApi({
|
|
312
|
+
url: `${BASE$3}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/conversion-metrics?${qs.toString()}`,
|
|
313
|
+
cookies,
|
|
314
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
315
|
+
});
|
|
316
|
+
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected metrics shape for app=${params.miniAppId}`);
|
|
317
|
+
const rec = raw;
|
|
318
|
+
const metricsRaw = rec.metrics;
|
|
319
|
+
if (!Array.isArray(metricsRaw)) throw new Error(`Unexpected metrics shape: metrics is not an array (app=${params.miniAppId})`);
|
|
320
|
+
return {
|
|
321
|
+
metrics: metricsRaw.map((m) => {
|
|
322
|
+
if (m === null || typeof m !== "object") return {};
|
|
323
|
+
return m;
|
|
324
|
+
}),
|
|
325
|
+
cacheTime: typeof rec.cacheTime === "string" ? rec.cacheTime : void 0
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
async function fetchCerts(workspaceId, miniAppId, cookies, opts = {}) {
|
|
329
|
+
const raw = await requestConsoleApi({
|
|
330
|
+
url: `${BASE$3}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs`,
|
|
331
|
+
cookies,
|
|
332
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
333
|
+
});
|
|
334
|
+
if (!Array.isArray(raw)) throw new Error(`Unexpected certs shape for app=${miniAppId}: not an array`);
|
|
335
|
+
return raw.map((c) => {
|
|
336
|
+
if (c === null || typeof c !== "object") return {};
|
|
337
|
+
return c;
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
async function fetchShareRewards(params, cookies, opts = {}) {
|
|
341
|
+
const qs = new URLSearchParams();
|
|
342
|
+
qs.set("search", params.search ?? "");
|
|
343
|
+
const raw = await requestConsoleApi({
|
|
344
|
+
url: `${BASE$3}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/share-rewards?${qs.toString()}`,
|
|
345
|
+
cookies,
|
|
346
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
347
|
+
});
|
|
348
|
+
if (!Array.isArray(raw)) throw new Error(`Unexpected share-rewards shape for app=${params.miniAppId}: not an array`);
|
|
349
|
+
return raw.map((r) => {
|
|
350
|
+
if (r === null || typeof r !== "object") return {};
|
|
351
|
+
return r;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
async function fetchDeployedBundle(workspaceId, miniAppId, cookies, opts = {}) {
|
|
355
|
+
const raw = await requestConsoleApi({
|
|
356
|
+
url: `${BASE$3}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/deployed`,
|
|
357
|
+
cookies,
|
|
358
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
359
|
+
});
|
|
360
|
+
if (raw === null) return null;
|
|
361
|
+
if (typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected deployed-bundle shape for app=${miniAppId}`);
|
|
362
|
+
return raw;
|
|
363
|
+
}
|
|
225
364
|
async function createMiniApp(workspaceId, payload, cookies, opts = {}) {
|
|
226
365
|
return normalizeCreateResult(await requestConsoleApi({
|
|
227
|
-
url: `${BASE$
|
|
366
|
+
url: `${BASE$3}/workspaces/${workspaceId}/mini-app/review`,
|
|
228
367
|
method: "POST",
|
|
229
368
|
cookies,
|
|
230
369
|
body: payload,
|
|
@@ -257,7 +396,7 @@ function normalizeCreateResult(raw) {
|
|
|
257
396
|
* the field name is actually `file` — if so, swap it in one place here.
|
|
258
397
|
*/
|
|
259
398
|
async function uploadMiniAppResource(params, opts = {}) {
|
|
260
|
-
const url = new URL(`${BASE$
|
|
399
|
+
const url = new URL(`${BASE$3}/resource/${params.workspaceId}/upload`);
|
|
261
400
|
url.searchParams.set("validWidth", String(params.validWidth));
|
|
262
401
|
url.searchParams.set("validHeight", String(params.validHeight));
|
|
263
402
|
const form = new FormData();
|
|
@@ -507,7 +646,7 @@ async function emitFailureFromError(json, err) {
|
|
|
507
646
|
emitApiError(json, err.message);
|
|
508
647
|
return exitAfterFlush(ExitCode.ApiError);
|
|
509
648
|
}
|
|
510
|
-
function parsePositiveInt(raw) {
|
|
649
|
+
function parsePositiveInt$1(raw) {
|
|
511
650
|
if (!/^[1-9]\d*$/.test(raw)) return null;
|
|
512
651
|
const n = Number.parseInt(raw, 10);
|
|
513
652
|
return Number.isSafeInteger(n) ? n : null;
|
|
@@ -535,7 +674,7 @@ async function resolveWorkspaceContext(args) {
|
|
|
535
674
|
let workspaceId;
|
|
536
675
|
if (args.workspace) {
|
|
537
676
|
const raw = String(args.workspace);
|
|
538
|
-
const parsed = parsePositiveInt(raw);
|
|
677
|
+
const parsed = parsePositiveInt$1(raw);
|
|
539
678
|
if (parsed === null) {
|
|
540
679
|
const message = `--workspace must be a positive integer (got ${raw})`;
|
|
541
680
|
if (args.json) emitJson({
|
|
@@ -563,6 +702,21 @@ async function resolveWorkspaceContext(args) {
|
|
|
563
702
|
workspaceId
|
|
564
703
|
};
|
|
565
704
|
}
|
|
705
|
+
/**
|
|
706
|
+
* Session-only sibling of `resolveWorkspaceContext` for commands that
|
|
707
|
+
* don't need a workspace id (notices come from a shared Toss workspace,
|
|
708
|
+
* whoami is self-scoped). Same "exits on miss, returns null to force
|
|
709
|
+
* `if (!session) return`" pattern.
|
|
710
|
+
*/
|
|
711
|
+
async function requireSession(json) {
|
|
712
|
+
const session = await readSession();
|
|
713
|
+
if (!session) {
|
|
714
|
+
emitNotAuthenticated(json);
|
|
715
|
+
await exitAfterFlush(ExitCode.NotAuthenticated);
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
return session;
|
|
719
|
+
}
|
|
566
720
|
//#endregion
|
|
567
721
|
//#region src/config/app-manifest.ts
|
|
568
722
|
var ManifestError = class extends Error {
|
|
@@ -1046,7 +1200,7 @@ function reviewStateFor(entry) {
|
|
|
1046
1200
|
const raw = entry.reviewState ?? entry.status;
|
|
1047
1201
|
return typeof raw === "string" ? raw : void 0;
|
|
1048
1202
|
}
|
|
1049
|
-
const lsCommand$
|
|
1203
|
+
const lsCommand$4 = defineCommand({
|
|
1050
1204
|
meta: {
|
|
1051
1205
|
name: "ls",
|
|
1052
1206
|
description: "List mini-apps in the selected workspace."
|
|
@@ -1126,7 +1280,7 @@ function parseAppId(raw) {
|
|
|
1126
1280
|
if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return null;
|
|
1127
1281
|
return n;
|
|
1128
1282
|
}
|
|
1129
|
-
const showCommand$
|
|
1283
|
+
const showCommand$2 = defineCommand({
|
|
1130
1284
|
meta: {
|
|
1131
1285
|
name: "show",
|
|
1132
1286
|
description: "Show full details of a mini-app, including fields only visible in the draft view."
|
|
@@ -1261,188 +1415,942 @@ function deriveReviewState(env) {
|
|
|
1261
1415
|
}
|
|
1262
1416
|
const POLL_MIN_INTERVAL_SEC = 30;
|
|
1263
1417
|
const POLL_MAX_INTERVAL_SEC = 3600;
|
|
1264
|
-
const
|
|
1418
|
+
const statusCommand = defineCommand({
|
|
1265
1419
|
meta: {
|
|
1266
|
-
name: "
|
|
1267
|
-
description: "
|
|
1420
|
+
name: "status",
|
|
1421
|
+
description: "Show the derived review state of a mini-app (under-review / rejected / approved)."
|
|
1268
1422
|
},
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
};
|
|
1338
|
-
try {
|
|
1339
|
-
const once = async () => {
|
|
1340
|
-
return deriveReviewState(await fetchMiniAppWithDraft(workspaceId, appId, session.cookies));
|
|
1341
|
-
};
|
|
1342
|
-
if (!args.watch) {
|
|
1343
|
-
emit(await once());
|
|
1344
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
1345
|
-
}
|
|
1346
|
-
let lastState = null;
|
|
1347
|
-
while (true) {
|
|
1348
|
-
const status = await once();
|
|
1349
|
-
if (args.json) emit(status);
|
|
1350
|
-
else if (status.state !== lastState) emit(status);
|
|
1351
|
-
lastState = status.state;
|
|
1352
|
-
if (status.state !== "under-review") return exitAfterFlush(ExitCode.Ok);
|
|
1353
|
-
await new Promise((resolve) => setTimeout(resolve, intervalSec * 1e3));
|
|
1354
|
-
}
|
|
1355
|
-
} catch (err) {
|
|
1356
|
-
return emitFailureFromError(args.json, err);
|
|
1357
|
-
}
|
|
1423
|
+
args: {
|
|
1424
|
+
id: {
|
|
1425
|
+
type: "positional",
|
|
1426
|
+
description: "Mini-app ID.",
|
|
1427
|
+
required: true
|
|
1428
|
+
},
|
|
1429
|
+
workspace: {
|
|
1430
|
+
type: "string",
|
|
1431
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1432
|
+
},
|
|
1433
|
+
watch: {
|
|
1434
|
+
type: "boolean",
|
|
1435
|
+
description: "Poll until the review state flips off `under-review` (rejected or approved). Combine with `--interval <seconds>`.",
|
|
1436
|
+
default: false
|
|
1437
|
+
},
|
|
1438
|
+
interval: {
|
|
1439
|
+
type: "string",
|
|
1440
|
+
description: "Polling interval in seconds when --watch is set. Clamped to [30, 3600].",
|
|
1441
|
+
default: "60"
|
|
1442
|
+
},
|
|
1443
|
+
json: {
|
|
1444
|
+
type: "boolean",
|
|
1445
|
+
description: "Emit machine-readable JSON.",
|
|
1446
|
+
default: false
|
|
1447
|
+
}
|
|
1448
|
+
},
|
|
1449
|
+
async run({ args }) {
|
|
1450
|
+
const appId = parseAppId(args.id);
|
|
1451
|
+
if (appId === null) {
|
|
1452
|
+
if (args.json) emitJson({
|
|
1453
|
+
ok: false,
|
|
1454
|
+
reason: "invalid-id",
|
|
1455
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
1456
|
+
});
|
|
1457
|
+
else process.stderr.write(`app status: invalid id ${JSON.stringify(args.id)}\n`);
|
|
1458
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1459
|
+
}
|
|
1460
|
+
const intervalRaw = Number(args.interval);
|
|
1461
|
+
if (!Number.isFinite(intervalRaw) || intervalRaw <= 0) {
|
|
1462
|
+
if (args.json) emitJson({
|
|
1463
|
+
ok: false,
|
|
1464
|
+
reason: "invalid-config",
|
|
1465
|
+
field: "interval",
|
|
1466
|
+
message: `--interval must be a positive number (got ${JSON.stringify(args.interval)})`
|
|
1467
|
+
});
|
|
1468
|
+
else process.stderr.write(`app status: invalid --interval ${JSON.stringify(args.interval)}\n`);
|
|
1469
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1470
|
+
}
|
|
1471
|
+
const intervalSec = Math.max(POLL_MIN_INTERVAL_SEC, Math.min(POLL_MAX_INTERVAL_SEC, Math.floor(intervalRaw)));
|
|
1472
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1473
|
+
if (!ctx) return;
|
|
1474
|
+
const { session, workspaceId } = ctx;
|
|
1475
|
+
const emit = (status) => {
|
|
1476
|
+
if (args.json) emitJson({
|
|
1477
|
+
ok: true,
|
|
1478
|
+
workspaceId,
|
|
1479
|
+
appId,
|
|
1480
|
+
...status
|
|
1481
|
+
});
|
|
1482
|
+
else process.stdout.write(`App ${appId} (ws ${workspaceId}): ${status.state}` + (status.rejectedMessage ? `\n reason: ${status.rejectedMessage}` : "") + "\n");
|
|
1483
|
+
};
|
|
1484
|
+
try {
|
|
1485
|
+
const once = async () => {
|
|
1486
|
+
return deriveReviewState(await fetchMiniAppWithDraft(workspaceId, appId, session.cookies));
|
|
1487
|
+
};
|
|
1488
|
+
if (!args.watch) {
|
|
1489
|
+
emit(await once());
|
|
1490
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1358
1491
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
type: "string",
|
|
1368
|
-
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
1369
|
-
},
|
|
1370
|
-
config: {
|
|
1371
|
-
type: "string",
|
|
1372
|
-
description: "Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.json`."
|
|
1373
|
-
},
|
|
1374
|
-
"dry-run": {
|
|
1375
|
-
type: "boolean",
|
|
1376
|
-
description: "Validate manifest + images and print the inferred submit payload; no uploads.",
|
|
1377
|
-
default: false
|
|
1378
|
-
},
|
|
1379
|
-
"accept-terms": {
|
|
1380
|
-
type: "boolean",
|
|
1381
|
-
description: "Attest to the required console legal-agreement checkboxes (see VALIDATION-RULES.md). Required for real submits.",
|
|
1382
|
-
default: false
|
|
1383
|
-
},
|
|
1384
|
-
json: {
|
|
1385
|
-
type: "boolean",
|
|
1386
|
-
description: "Emit machine-readable JSON to stdout.",
|
|
1387
|
-
default: false
|
|
1388
|
-
}
|
|
1389
|
-
},
|
|
1390
|
-
async run({ args }) {
|
|
1391
|
-
await runRegister({
|
|
1392
|
-
json: args.json,
|
|
1393
|
-
dryRun: args["dry-run"],
|
|
1394
|
-
acceptTerms: args["accept-terms"],
|
|
1395
|
-
...args.workspace !== void 0 ? { workspace: args.workspace } : {},
|
|
1396
|
-
...args.config !== void 0 ? { config: args.config } : {}
|
|
1397
|
-
});
|
|
1492
|
+
let lastState = null;
|
|
1493
|
+
while (true) {
|
|
1494
|
+
const status = await once();
|
|
1495
|
+
if (args.json) emit(status);
|
|
1496
|
+
else if (status.state !== lastState) emit(status);
|
|
1497
|
+
lastState = status.state;
|
|
1498
|
+
if (status.state !== "under-review") return exitAfterFlush(ExitCode.Ok);
|
|
1499
|
+
await new Promise((resolve) => setTimeout(resolve, intervalSec * 1e3));
|
|
1398
1500
|
}
|
|
1399
|
-
})
|
|
1501
|
+
} catch (err) {
|
|
1502
|
+
return emitFailureFromError(args.json, err);
|
|
1503
|
+
}
|
|
1400
1504
|
}
|
|
1401
1505
|
});
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
cookies,
|
|
1409
|
-
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
1410
|
-
});
|
|
1411
|
-
if (!Array.isArray(raw)) throw new Error(`Unexpected api-keys shape for workspace=${workspaceId}: not an array`);
|
|
1412
|
-
return raw.map((entry, index) => normalizeKey(entry, workspaceId, index));
|
|
1413
|
-
}
|
|
1414
|
-
function normalizeKey(raw, workspaceId, index) {
|
|
1415
|
-
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: not an object`);
|
|
1416
|
-
const rec = raw;
|
|
1417
|
-
const rawId = rec.id ?? rec.apiKeyId ?? rec.keyId;
|
|
1418
|
-
if (typeof rawId !== "string" && typeof rawId !== "number") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: missing id`);
|
|
1419
|
-
const rawName = rec.name ?? rec.apiKeyName ?? rec.keyName ?? rec.description;
|
|
1420
|
-
const name = typeof rawName === "string" ? rawName : void 0;
|
|
1421
|
-
const { id: _id, apiKeyId: _aid, keyId: _kid, name: _n, apiKeyName: _an, keyName: _kn, description: _d, ...extra } = rec;
|
|
1422
|
-
return {
|
|
1423
|
-
id: rawId,
|
|
1424
|
-
name,
|
|
1425
|
-
extra
|
|
1426
|
-
};
|
|
1506
|
+
const VALID_SORT_FIELDS = ["CREATED_AT", "SCORE"];
|
|
1507
|
+
const VALID_SORT_DIRECTIONS = ["ASC", "DESC"];
|
|
1508
|
+
function parseNonNegativeInt(raw, field) {
|
|
1509
|
+
const n = Number(raw);
|
|
1510
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) return { error: `--${field} must be a non-negative integer (got ${JSON.stringify(raw)})` };
|
|
1511
|
+
return { value: n };
|
|
1427
1512
|
}
|
|
1428
|
-
const
|
|
1513
|
+
const ratingsCommand = defineCommand({
|
|
1429
1514
|
meta: {
|
|
1430
|
-
name: "
|
|
1431
|
-
description: "
|
|
1515
|
+
name: "ratings",
|
|
1516
|
+
description: "List user ratings and reviews left for a mini-app."
|
|
1432
1517
|
},
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
description: "
|
|
1518
|
+
args: {
|
|
1519
|
+
id: {
|
|
1520
|
+
type: "positional",
|
|
1521
|
+
description: "Mini-app ID.",
|
|
1522
|
+
required: true
|
|
1437
1523
|
},
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1524
|
+
workspace: {
|
|
1525
|
+
type: "string",
|
|
1526
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1527
|
+
},
|
|
1528
|
+
page: {
|
|
1529
|
+
type: "string",
|
|
1530
|
+
description: "Page number (0-indexed).",
|
|
1531
|
+
default: "0"
|
|
1532
|
+
},
|
|
1533
|
+
size: {
|
|
1534
|
+
type: "string",
|
|
1535
|
+
description: "Page size.",
|
|
1536
|
+
default: "20"
|
|
1537
|
+
},
|
|
1538
|
+
"sort-field": {
|
|
1539
|
+
type: "string",
|
|
1540
|
+
description: "Sort field: CREATED_AT (default) or SCORE.",
|
|
1541
|
+
default: "CREATED_AT"
|
|
1542
|
+
},
|
|
1543
|
+
"sort-direction": {
|
|
1544
|
+
type: "string",
|
|
1545
|
+
description: "Sort direction: ASC or DESC (default).",
|
|
1546
|
+
default: "DESC"
|
|
1547
|
+
},
|
|
1548
|
+
json: {
|
|
1549
|
+
type: "boolean",
|
|
1550
|
+
description: "Emit machine-readable JSON.",
|
|
1551
|
+
default: false
|
|
1552
|
+
}
|
|
1553
|
+
},
|
|
1554
|
+
async run({ args }) {
|
|
1555
|
+
const appId = parseAppId(args.id);
|
|
1556
|
+
if (appId === null) {
|
|
1557
|
+
if (args.json) emitJson({
|
|
1558
|
+
ok: false,
|
|
1559
|
+
reason: "invalid-id",
|
|
1560
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
1561
|
+
});
|
|
1562
|
+
else process.stderr.write(`app ratings: invalid id ${JSON.stringify(args.id)}\n`);
|
|
1563
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1564
|
+
}
|
|
1565
|
+
const pageResult = parseNonNegativeInt(args.page, "page");
|
|
1566
|
+
if ("error" in pageResult) {
|
|
1567
|
+
if (args.json) emitJson({
|
|
1568
|
+
ok: false,
|
|
1569
|
+
reason: "invalid-config",
|
|
1570
|
+
field: "page",
|
|
1571
|
+
message: pageResult.error
|
|
1572
|
+
});
|
|
1573
|
+
else process.stderr.write(`app ratings: ${pageResult.error}\n`);
|
|
1574
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1575
|
+
}
|
|
1576
|
+
const sizeResult = parseNonNegativeInt(args.size, "size");
|
|
1577
|
+
if ("error" in sizeResult) {
|
|
1578
|
+
if (args.json) emitJson({
|
|
1579
|
+
ok: false,
|
|
1580
|
+
reason: "invalid-config",
|
|
1581
|
+
field: "size",
|
|
1582
|
+
message: sizeResult.error
|
|
1583
|
+
});
|
|
1584
|
+
else process.stderr.write(`app ratings: ${sizeResult.error}\n`);
|
|
1585
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1586
|
+
}
|
|
1587
|
+
if (sizeResult.value === 0) {
|
|
1588
|
+
if (args.json) emitJson({
|
|
1589
|
+
ok: false,
|
|
1590
|
+
reason: "invalid-config",
|
|
1591
|
+
field: "size",
|
|
1592
|
+
message: "--size must be at least 1"
|
|
1593
|
+
});
|
|
1594
|
+
else process.stderr.write("app ratings: --size must be at least 1\n");
|
|
1595
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1596
|
+
}
|
|
1597
|
+
const sortField = args["sort-field"];
|
|
1598
|
+
if (!VALID_SORT_FIELDS.includes(sortField)) {
|
|
1599
|
+
if (args.json) emitJson({
|
|
1600
|
+
ok: false,
|
|
1601
|
+
reason: "invalid-config",
|
|
1602
|
+
field: "sort-field",
|
|
1603
|
+
message: `--sort-field must be one of ${VALID_SORT_FIELDS.join("|")} (got ${JSON.stringify(sortField)})`
|
|
1604
|
+
});
|
|
1605
|
+
else process.stderr.write(`app ratings: invalid --sort-field ${JSON.stringify(sortField)}\n`);
|
|
1606
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1607
|
+
}
|
|
1608
|
+
const sortDirection = args["sort-direction"];
|
|
1609
|
+
if (!VALID_SORT_DIRECTIONS.includes(sortDirection)) {
|
|
1610
|
+
if (args.json) emitJson({
|
|
1611
|
+
ok: false,
|
|
1612
|
+
reason: "invalid-config",
|
|
1613
|
+
field: "sort-direction",
|
|
1614
|
+
message: `--sort-direction must be one of ${VALID_SORT_DIRECTIONS.join("|")} (got ${JSON.stringify(sortDirection)})`
|
|
1615
|
+
});
|
|
1616
|
+
else process.stderr.write(`app ratings: invalid --sort-direction ${JSON.stringify(sortDirection)}\n`);
|
|
1617
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1618
|
+
}
|
|
1619
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1620
|
+
if (!ctx) return;
|
|
1621
|
+
const { session, workspaceId } = ctx;
|
|
1622
|
+
try {
|
|
1623
|
+
const result = await fetchMiniAppRatings({
|
|
1624
|
+
workspaceId,
|
|
1625
|
+
miniAppId: appId,
|
|
1626
|
+
page: pageResult.value,
|
|
1627
|
+
size: sizeResult.value,
|
|
1628
|
+
sortField,
|
|
1629
|
+
sortDirection
|
|
1630
|
+
}, session.cookies);
|
|
1631
|
+
if (args.json) {
|
|
1632
|
+
emitJson({
|
|
1633
|
+
ok: true,
|
|
1634
|
+
workspaceId,
|
|
1635
|
+
appId,
|
|
1636
|
+
page: pageResult.value,
|
|
1637
|
+
size: sizeResult.value,
|
|
1638
|
+
sortField,
|
|
1639
|
+
sortDirection,
|
|
1640
|
+
averageRating: result.averageRating,
|
|
1641
|
+
totalReviewCount: result.totalReviewCount,
|
|
1642
|
+
paging: result.paging,
|
|
1643
|
+
ratings: result.ratings
|
|
1644
|
+
});
|
|
1645
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1646
|
+
}
|
|
1647
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): ${result.totalReviewCount} review(s), avg ${result.averageRating.toFixed(2)}\n`);
|
|
1648
|
+
if (result.ratings.length === 0) {
|
|
1649
|
+
process.stdout.write("No ratings on this page.\n");
|
|
1650
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1651
|
+
}
|
|
1652
|
+
for (const r of result.ratings) {
|
|
1653
|
+
const score = typeof r.score === "number" ? r.score : r.rating ?? "-";
|
|
1654
|
+
const author = typeof r.nickname === "string" ? r.nickname : typeof r.userName === "string" ? r.userName : "(anon)";
|
|
1655
|
+
const text = typeof r.content === "string" ? r.content : typeof r.reviewContent === "string" ? r.reviewContent : "";
|
|
1656
|
+
const createdAt = typeof r.createdAt === "string" ? r.createdAt : typeof r.reviewedAt === "string" ? r.reviewedAt : "";
|
|
1657
|
+
process.stdout.write(`${score}\t${createdAt}\t${author}\t${text}\n`);
|
|
1658
|
+
}
|
|
1659
|
+
if (result.paging.hasNext) process.stdout.write(`(more: --page ${pageResult.value + 1} for next ${sizeResult.value})\n`);
|
|
1660
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1661
|
+
} catch (err) {
|
|
1662
|
+
return emitFailureFromError(args.json, err);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1666
|
+
const reportsCommand = defineCommand({
|
|
1667
|
+
meta: {
|
|
1668
|
+
name: "reports",
|
|
1669
|
+
description: "List user-submitted reports (신고 내역) for a mini-app."
|
|
1670
|
+
},
|
|
1671
|
+
args: {
|
|
1672
|
+
id: {
|
|
1673
|
+
type: "positional",
|
|
1674
|
+
description: "Mini-app ID.",
|
|
1675
|
+
required: true
|
|
1676
|
+
},
|
|
1677
|
+
workspace: {
|
|
1678
|
+
type: "string",
|
|
1679
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1680
|
+
},
|
|
1681
|
+
"page-size": {
|
|
1682
|
+
type: "string",
|
|
1683
|
+
description: "Page size (default 20).",
|
|
1684
|
+
default: "20"
|
|
1685
|
+
},
|
|
1686
|
+
cursor: {
|
|
1687
|
+
type: "string",
|
|
1688
|
+
description: "Opaque cursor from a previous response `nextCursor`."
|
|
1689
|
+
},
|
|
1690
|
+
json: {
|
|
1691
|
+
type: "boolean",
|
|
1692
|
+
description: "Emit machine-readable JSON.",
|
|
1693
|
+
default: false
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
async run({ args }) {
|
|
1697
|
+
const appId = parseAppId(args.id);
|
|
1698
|
+
if (appId === null) {
|
|
1699
|
+
if (args.json) emitJson({
|
|
1700
|
+
ok: false,
|
|
1701
|
+
reason: "invalid-id",
|
|
1702
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
1703
|
+
});
|
|
1704
|
+
else process.stderr.write(`app reports: invalid id ${JSON.stringify(args.id)}\n`);
|
|
1705
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1706
|
+
}
|
|
1707
|
+
const pageSizeResult = parseNonNegativeInt(args["page-size"], "page-size");
|
|
1708
|
+
if ("error" in pageSizeResult) {
|
|
1709
|
+
if (args.json) emitJson({
|
|
1710
|
+
ok: false,
|
|
1711
|
+
reason: "invalid-config",
|
|
1712
|
+
field: "page-size",
|
|
1713
|
+
message: pageSizeResult.error
|
|
1714
|
+
});
|
|
1715
|
+
else process.stderr.write(`app reports: ${pageSizeResult.error}\n`);
|
|
1716
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1717
|
+
}
|
|
1718
|
+
if (pageSizeResult.value === 0) {
|
|
1719
|
+
if (args.json) emitJson({
|
|
1720
|
+
ok: false,
|
|
1721
|
+
reason: "invalid-config",
|
|
1722
|
+
field: "page-size",
|
|
1723
|
+
message: "--page-size must be at least 1"
|
|
1724
|
+
});
|
|
1725
|
+
else process.stderr.write("app reports: --page-size must be at least 1\n");
|
|
1726
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1727
|
+
}
|
|
1728
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1729
|
+
if (!ctx) return;
|
|
1730
|
+
const { session, workspaceId } = ctx;
|
|
1731
|
+
try {
|
|
1732
|
+
const result = await fetchUserReports({
|
|
1733
|
+
workspaceId,
|
|
1734
|
+
miniAppId: appId,
|
|
1735
|
+
pageSize: pageSizeResult.value,
|
|
1736
|
+
...typeof args.cursor === "string" && args.cursor.length > 0 ? { cursor: args.cursor } : {}
|
|
1737
|
+
}, session.cookies);
|
|
1738
|
+
if (args.json) {
|
|
1739
|
+
emitJson({
|
|
1740
|
+
ok: true,
|
|
1741
|
+
workspaceId,
|
|
1742
|
+
appId,
|
|
1743
|
+
pageSize: pageSizeResult.value,
|
|
1744
|
+
cursor: args.cursor ?? null,
|
|
1745
|
+
nextCursor: result.nextCursor,
|
|
1746
|
+
hasMore: result.hasMore,
|
|
1747
|
+
reports: result.reports
|
|
1748
|
+
});
|
|
1749
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1750
|
+
}
|
|
1751
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): ${result.reports.length} report(s) on this page\n`);
|
|
1752
|
+
if (result.reports.length === 0) {
|
|
1753
|
+
process.stdout.write("No reports.\n");
|
|
1754
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1755
|
+
}
|
|
1756
|
+
for (const r of result.reports) {
|
|
1757
|
+
const id = typeof r.id === "string" || typeof r.id === "number" ? r.id : "-";
|
|
1758
|
+
const reason = typeof r.reason === "string" ? r.reason : r.reportType ?? "-";
|
|
1759
|
+
const text = typeof r.content === "string" ? r.content : typeof r.detail === "string" ? r.detail : "";
|
|
1760
|
+
const createdAt = typeof r.createdAt === "string" ? r.createdAt : typeof r.reportedAt === "string" ? r.reportedAt : "";
|
|
1761
|
+
process.stdout.write(`${id}\t${createdAt}\t${reason}\t${text}\n`);
|
|
1762
|
+
}
|
|
1763
|
+
if (result.hasMore && result.nextCursor) process.stdout.write(`(more: --cursor ${result.nextCursor})\n`);
|
|
1764
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1765
|
+
} catch (err) {
|
|
1766
|
+
return emitFailureFromError(args.json, err);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
});
|
|
1770
|
+
const bundlesCommand = defineCommand({
|
|
1771
|
+
meta: {
|
|
1772
|
+
name: "bundles",
|
|
1773
|
+
description: "Inspect upload bundles for a mini-app."
|
|
1774
|
+
},
|
|
1775
|
+
subCommands: {
|
|
1776
|
+
ls: defineCommand({
|
|
1777
|
+
meta: {
|
|
1778
|
+
name: "ls",
|
|
1779
|
+
description: "List upload bundles for a mini-app (page-based pagination)."
|
|
1780
|
+
},
|
|
1781
|
+
args: {
|
|
1782
|
+
id: {
|
|
1783
|
+
type: "positional",
|
|
1784
|
+
description: "Mini-app ID.",
|
|
1785
|
+
required: true
|
|
1786
|
+
},
|
|
1787
|
+
workspace: {
|
|
1788
|
+
type: "string",
|
|
1789
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1790
|
+
},
|
|
1791
|
+
page: {
|
|
1792
|
+
type: "string",
|
|
1793
|
+
description: "Page number (0-indexed).",
|
|
1794
|
+
default: "0"
|
|
1795
|
+
},
|
|
1796
|
+
tested: {
|
|
1797
|
+
type: "string",
|
|
1798
|
+
description: "Filter: `true` to show only tested bundles (or `false`)."
|
|
1799
|
+
},
|
|
1800
|
+
"deploy-status": {
|
|
1801
|
+
type: "string",
|
|
1802
|
+
description: "Filter by deploy status (e.g. DEPLOYED)."
|
|
1803
|
+
},
|
|
1804
|
+
json: {
|
|
1805
|
+
type: "boolean",
|
|
1806
|
+
description: "Emit machine-readable JSON.",
|
|
1807
|
+
default: false
|
|
1808
|
+
}
|
|
1809
|
+
},
|
|
1810
|
+
async run({ args }) {
|
|
1811
|
+
const appId = parseAppId(args.id);
|
|
1812
|
+
if (appId === null) {
|
|
1813
|
+
if (args.json) emitJson({
|
|
1814
|
+
ok: false,
|
|
1815
|
+
reason: "invalid-id",
|
|
1816
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
1817
|
+
});
|
|
1818
|
+
else process.stderr.write(`app bundles ls: invalid id ${JSON.stringify(args.id)}\n`);
|
|
1819
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1820
|
+
}
|
|
1821
|
+
const pageResult = parseNonNegativeInt(args.page, "page");
|
|
1822
|
+
if ("error" in pageResult) {
|
|
1823
|
+
if (args.json) emitJson({
|
|
1824
|
+
ok: false,
|
|
1825
|
+
reason: "invalid-config",
|
|
1826
|
+
field: "page",
|
|
1827
|
+
message: pageResult.error
|
|
1828
|
+
});
|
|
1829
|
+
else process.stderr.write(`app bundles ls: ${pageResult.error}\n`);
|
|
1830
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1831
|
+
}
|
|
1832
|
+
let tested;
|
|
1833
|
+
if (args.tested !== void 0) if (args.tested === "true") tested = true;
|
|
1834
|
+
else if (args.tested === "false") tested = false;
|
|
1835
|
+
else {
|
|
1836
|
+
if (args.json) emitJson({
|
|
1837
|
+
ok: false,
|
|
1838
|
+
reason: "invalid-config",
|
|
1839
|
+
field: "tested",
|
|
1840
|
+
message: `--tested must be "true" or "false" (got ${JSON.stringify(args.tested)})`
|
|
1841
|
+
});
|
|
1842
|
+
else process.stderr.write(`app bundles ls: invalid --tested ${JSON.stringify(args.tested)}\n`);
|
|
1843
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1844
|
+
}
|
|
1845
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1846
|
+
if (!ctx) return;
|
|
1847
|
+
const { session, workspaceId } = ctx;
|
|
1848
|
+
try {
|
|
1849
|
+
const result = await fetchBundles({
|
|
1850
|
+
workspaceId,
|
|
1851
|
+
miniAppId: appId,
|
|
1852
|
+
page: pageResult.value,
|
|
1853
|
+
...tested !== void 0 ? { tested } : {},
|
|
1854
|
+
...typeof args["deploy-status"] === "string" && args["deploy-status"].length > 0 ? { deployStatus: args["deploy-status"] } : {}
|
|
1855
|
+
}, session.cookies);
|
|
1856
|
+
if (args.json) {
|
|
1857
|
+
emitJson({
|
|
1858
|
+
ok: true,
|
|
1859
|
+
workspaceId,
|
|
1860
|
+
appId,
|
|
1861
|
+
page: pageResult.value,
|
|
1862
|
+
totalPage: result.totalPage,
|
|
1863
|
+
currentPage: result.currentPage,
|
|
1864
|
+
bundles: result.contents
|
|
1865
|
+
});
|
|
1866
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1867
|
+
}
|
|
1868
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): page ${result.currentPage + 1}/${Math.max(result.totalPage, 1)}, ${result.contents.length} bundle(s)\n`);
|
|
1869
|
+
if (result.contents.length === 0) {
|
|
1870
|
+
process.stdout.write("No bundles on this page.\n");
|
|
1871
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1872
|
+
}
|
|
1873
|
+
for (const b of result.contents) {
|
|
1874
|
+
const id = typeof b.id === "string" || typeof b.id === "number" ? b.id : "-";
|
|
1875
|
+
const version = typeof b.version === "string" ? b.version : "-";
|
|
1876
|
+
const status = typeof b.deployStatus === "string" ? b.deployStatus : "-";
|
|
1877
|
+
const createdAt = typeof b.createdAt === "string" ? b.createdAt : "";
|
|
1878
|
+
process.stdout.write(`${id}\t${version}\t${status}\t${createdAt}\n`);
|
|
1879
|
+
}
|
|
1880
|
+
if (result.currentPage + 1 < result.totalPage) process.stdout.write(`(more: --page ${result.currentPage + 1})\n`);
|
|
1881
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
return emitFailureFromError(args.json, err);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}),
|
|
1887
|
+
deployed: defineCommand({
|
|
1888
|
+
meta: {
|
|
1889
|
+
name: "deployed",
|
|
1890
|
+
description: "Show the currently deployed bundle for a mini-app (or null if none)."
|
|
1891
|
+
},
|
|
1892
|
+
args: {
|
|
1893
|
+
id: {
|
|
1894
|
+
type: "positional",
|
|
1895
|
+
description: "Mini-app ID.",
|
|
1896
|
+
required: true
|
|
1897
|
+
},
|
|
1898
|
+
workspace: {
|
|
1899
|
+
type: "string",
|
|
1900
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1901
|
+
},
|
|
1902
|
+
json: {
|
|
1903
|
+
type: "boolean",
|
|
1904
|
+
description: "Emit machine-readable JSON.",
|
|
1905
|
+
default: false
|
|
1906
|
+
}
|
|
1907
|
+
},
|
|
1908
|
+
async run({ args }) {
|
|
1909
|
+
const appId = parseAppId(args.id);
|
|
1910
|
+
if (appId === null) {
|
|
1911
|
+
if (args.json) emitJson({
|
|
1912
|
+
ok: false,
|
|
1913
|
+
reason: "invalid-id",
|
|
1914
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
1915
|
+
});
|
|
1916
|
+
else process.stderr.write(`app bundles deployed: invalid id ${JSON.stringify(args.id)}\n`);
|
|
1917
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1918
|
+
}
|
|
1919
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1920
|
+
if (!ctx) return;
|
|
1921
|
+
const { session, workspaceId } = ctx;
|
|
1922
|
+
try {
|
|
1923
|
+
const bundle = await fetchDeployedBundle(workspaceId, appId, session.cookies);
|
|
1924
|
+
if (args.json) {
|
|
1925
|
+
emitJson({
|
|
1926
|
+
ok: true,
|
|
1927
|
+
workspaceId,
|
|
1928
|
+
appId,
|
|
1929
|
+
bundle
|
|
1930
|
+
});
|
|
1931
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1932
|
+
}
|
|
1933
|
+
if (bundle === null) {
|
|
1934
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): no deployed bundle\n`);
|
|
1935
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1936
|
+
}
|
|
1937
|
+
const id = typeof bundle.id === "string" || typeof bundle.id === "number" ? bundle.id : "-";
|
|
1938
|
+
const version = typeof bundle.version === "string" ? bundle.version : "-";
|
|
1939
|
+
const status = typeof bundle.deployStatus === "string" ? bundle.deployStatus : "-";
|
|
1940
|
+
const deployedAt = typeof bundle.deployedAt === "string" ? bundle.deployedAt : "";
|
|
1941
|
+
process.stdout.write(`App ${appId} deployed bundle:\n`);
|
|
1942
|
+
process.stdout.write(` id ${id}\n`);
|
|
1943
|
+
process.stdout.write(` version ${version}\n`);
|
|
1944
|
+
process.stdout.write(` status ${status}\n`);
|
|
1945
|
+
process.stdout.write(` deployedAt ${deployedAt}\n`);
|
|
1946
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1947
|
+
} catch (err) {
|
|
1948
|
+
return emitFailureFromError(args.json, err);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
})
|
|
1952
|
+
}
|
|
1953
|
+
});
|
|
1954
|
+
const certsCommand = defineCommand({
|
|
1955
|
+
meta: {
|
|
1956
|
+
name: "certs",
|
|
1957
|
+
description: "Inspect mTLS certificates for a mini-app."
|
|
1958
|
+
},
|
|
1959
|
+
subCommands: { ls: defineCommand({
|
|
1960
|
+
meta: {
|
|
1961
|
+
name: "ls",
|
|
1962
|
+
description: "List mTLS certificates issued for a mini-app."
|
|
1963
|
+
},
|
|
1964
|
+
args: {
|
|
1965
|
+
id: {
|
|
1966
|
+
type: "positional",
|
|
1967
|
+
description: "Mini-app ID.",
|
|
1968
|
+
required: true
|
|
1969
|
+
},
|
|
1970
|
+
workspace: {
|
|
1971
|
+
type: "string",
|
|
1972
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1973
|
+
},
|
|
1974
|
+
json: {
|
|
1975
|
+
type: "boolean",
|
|
1976
|
+
description: "Emit machine-readable JSON.",
|
|
1977
|
+
default: false
|
|
1978
|
+
}
|
|
1979
|
+
},
|
|
1980
|
+
async run({ args }) {
|
|
1981
|
+
const appId = parseAppId(args.id);
|
|
1982
|
+
if (appId === null) {
|
|
1983
|
+
if (args.json) emitJson({
|
|
1984
|
+
ok: false,
|
|
1985
|
+
reason: "invalid-id",
|
|
1986
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
1987
|
+
});
|
|
1988
|
+
else process.stderr.write(`app certs ls: invalid id ${JSON.stringify(args.id)}\n`);
|
|
1989
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1990
|
+
}
|
|
1991
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1992
|
+
if (!ctx) return;
|
|
1993
|
+
const { session, workspaceId } = ctx;
|
|
1994
|
+
try {
|
|
1995
|
+
const certs = await fetchCerts(workspaceId, appId, session.cookies);
|
|
1996
|
+
if (args.json) {
|
|
1997
|
+
emitJson({
|
|
1998
|
+
ok: true,
|
|
1999
|
+
workspaceId,
|
|
2000
|
+
appId,
|
|
2001
|
+
certs
|
|
2002
|
+
});
|
|
2003
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2004
|
+
}
|
|
2005
|
+
if (certs.length === 0) {
|
|
2006
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): no mTLS certs\n`);
|
|
2007
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2008
|
+
}
|
|
2009
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): ${certs.length} cert(s)\n`);
|
|
2010
|
+
for (const c of certs) {
|
|
2011
|
+
const id = typeof c.id === "string" || typeof c.id === "number" ? c.id : typeof c.certId === "string" || typeof c.certId === "number" ? c.certId : "-";
|
|
2012
|
+
const cn = typeof c.commonName === "string" ? c.commonName : "-";
|
|
2013
|
+
const createdAt = typeof c.createdAt === "string" ? c.createdAt : "";
|
|
2014
|
+
const expiresAt = typeof c.expiresAt === "string" ? c.expiresAt : typeof c.validUntil === "string" ? c.validUntil : "";
|
|
2015
|
+
process.stdout.write(`${id}\t${cn}\t${createdAt}\t${expiresAt}\n`);
|
|
2016
|
+
}
|
|
2017
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2018
|
+
} catch (err) {
|
|
2019
|
+
return emitFailureFromError(args.json, err);
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
}) }
|
|
2023
|
+
});
|
|
2024
|
+
const VALID_TIME_UNITS = [
|
|
2025
|
+
"DAY",
|
|
2026
|
+
"WEEK",
|
|
2027
|
+
"MONTH"
|
|
2028
|
+
];
|
|
2029
|
+
function parseIsoDate(raw, field) {
|
|
2030
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(raw)) return { error: `--${field} must be YYYY-MM-DD (got ${JSON.stringify(raw)})` };
|
|
2031
|
+
const d = /* @__PURE__ */ new Date(`${raw}T00:00:00Z`);
|
|
2032
|
+
if (Number.isNaN(d.getTime())) return { error: `--${field} is not a valid date (got ${JSON.stringify(raw)})` };
|
|
2033
|
+
return { value: raw };
|
|
2034
|
+
}
|
|
2035
|
+
function todayLocalIso() {
|
|
2036
|
+
const d = /* @__PURE__ */ new Date();
|
|
2037
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
2038
|
+
}
|
|
2039
|
+
function daysAgoLocalIso(days) {
|
|
2040
|
+
const d = /* @__PURE__ */ new Date();
|
|
2041
|
+
d.setDate(d.getDate() - days);
|
|
2042
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
2043
|
+
}
|
|
2044
|
+
const appCommand = defineCommand({
|
|
2045
|
+
meta: {
|
|
2046
|
+
name: "app",
|
|
2047
|
+
description: "Inspect mini-apps in a workspace."
|
|
2048
|
+
},
|
|
2049
|
+
subCommands: {
|
|
2050
|
+
ls: lsCommand$4,
|
|
2051
|
+
show: showCommand$2,
|
|
2052
|
+
status: statusCommand,
|
|
2053
|
+
ratings: ratingsCommand,
|
|
2054
|
+
reports: reportsCommand,
|
|
2055
|
+
bundles: bundlesCommand,
|
|
2056
|
+
certs: certsCommand,
|
|
2057
|
+
metrics: defineCommand({
|
|
2058
|
+
meta: {
|
|
2059
|
+
name: "metrics",
|
|
2060
|
+
description: "Show conversion metrics for a mini-app over a date range."
|
|
2061
|
+
},
|
|
2062
|
+
args: {
|
|
2063
|
+
id: {
|
|
2064
|
+
type: "positional",
|
|
2065
|
+
description: "Mini-app ID.",
|
|
2066
|
+
required: true
|
|
2067
|
+
},
|
|
2068
|
+
workspace: {
|
|
2069
|
+
type: "string",
|
|
2070
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
2071
|
+
},
|
|
2072
|
+
"time-unit": {
|
|
2073
|
+
type: "string",
|
|
2074
|
+
description: "Bucket size: DAY | WEEK | MONTH.",
|
|
2075
|
+
default: "DAY"
|
|
2076
|
+
},
|
|
2077
|
+
start: {
|
|
2078
|
+
type: "string",
|
|
2079
|
+
description: "Start date (YYYY-MM-DD). Defaults to 30 days before --end."
|
|
2080
|
+
},
|
|
2081
|
+
end: {
|
|
2082
|
+
type: "string",
|
|
2083
|
+
description: "End date (YYYY-MM-DD). Defaults to today (host local)."
|
|
2084
|
+
},
|
|
2085
|
+
refresh: {
|
|
2086
|
+
type: "boolean",
|
|
2087
|
+
description: "Bypass server-side cache.",
|
|
2088
|
+
default: false
|
|
2089
|
+
},
|
|
2090
|
+
json: {
|
|
2091
|
+
type: "boolean",
|
|
2092
|
+
description: "Emit machine-readable JSON.",
|
|
2093
|
+
default: false
|
|
2094
|
+
}
|
|
2095
|
+
},
|
|
2096
|
+
async run({ args }) {
|
|
2097
|
+
const appId = parseAppId(args.id);
|
|
2098
|
+
if (appId === null) {
|
|
2099
|
+
if (args.json) emitJson({
|
|
2100
|
+
ok: false,
|
|
2101
|
+
reason: "invalid-id",
|
|
2102
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
2103
|
+
});
|
|
2104
|
+
else process.stderr.write(`app metrics: invalid id ${JSON.stringify(args.id)}\n`);
|
|
2105
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
2106
|
+
}
|
|
2107
|
+
const timeUnit = String(args["time-unit"]).toUpperCase();
|
|
2108
|
+
if (!VALID_TIME_UNITS.includes(timeUnit)) {
|
|
2109
|
+
const message = `--time-unit must be one of ${VALID_TIME_UNITS.join("|")} (got ${JSON.stringify(args["time-unit"])})`;
|
|
2110
|
+
if (args.json) emitJson({
|
|
2111
|
+
ok: false,
|
|
2112
|
+
reason: "invalid-time-unit",
|
|
2113
|
+
message
|
|
2114
|
+
});
|
|
2115
|
+
else process.stderr.write(`${message}\n`);
|
|
2116
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
2117
|
+
}
|
|
2118
|
+
const endResult = parseIsoDate(args.end ? String(args.end) : todayLocalIso(), "end");
|
|
2119
|
+
if ("error" in endResult) {
|
|
2120
|
+
if (args.json) emitJson({
|
|
2121
|
+
ok: false,
|
|
2122
|
+
reason: "invalid-date",
|
|
2123
|
+
message: endResult.error
|
|
2124
|
+
});
|
|
2125
|
+
else process.stderr.write(`${endResult.error}\n`);
|
|
2126
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
2127
|
+
}
|
|
2128
|
+
const startResult = parseIsoDate(args.start ? String(args.start) : daysAgoLocalIso(30), "start");
|
|
2129
|
+
if ("error" in startResult) {
|
|
2130
|
+
if (args.json) emitJson({
|
|
2131
|
+
ok: false,
|
|
2132
|
+
reason: "invalid-date",
|
|
2133
|
+
message: startResult.error
|
|
2134
|
+
});
|
|
2135
|
+
else process.stderr.write(`${startResult.error}\n`);
|
|
2136
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
2137
|
+
}
|
|
2138
|
+
if (startResult.value > endResult.value) {
|
|
2139
|
+
const message = `--start (${startResult.value}) must be on or before --end (${endResult.value})`;
|
|
2140
|
+
if (args.json) emitJson({
|
|
2141
|
+
ok: false,
|
|
2142
|
+
reason: "invalid-date",
|
|
2143
|
+
message
|
|
2144
|
+
});
|
|
2145
|
+
else process.stderr.write(`${message}\n`);
|
|
2146
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
2147
|
+
}
|
|
2148
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
2149
|
+
if (!ctx) return;
|
|
2150
|
+
const { session, workspaceId } = ctx;
|
|
2151
|
+
try {
|
|
2152
|
+
const result = await fetchConversionMetrics({
|
|
2153
|
+
workspaceId,
|
|
2154
|
+
miniAppId: appId,
|
|
2155
|
+
timeUnitType: timeUnit,
|
|
2156
|
+
startDate: startResult.value,
|
|
2157
|
+
endDate: endResult.value,
|
|
2158
|
+
refresh: args.refresh
|
|
2159
|
+
}, session.cookies);
|
|
2160
|
+
if (args.json) {
|
|
2161
|
+
emitJson({
|
|
2162
|
+
ok: true,
|
|
2163
|
+
workspaceId,
|
|
2164
|
+
appId,
|
|
2165
|
+
timeUnitType: timeUnit,
|
|
2166
|
+
startDate: startResult.value,
|
|
2167
|
+
endDate: endResult.value,
|
|
2168
|
+
...result.cacheTime !== void 0 ? { cacheTime: result.cacheTime } : {},
|
|
2169
|
+
metrics: result.metrics
|
|
2170
|
+
});
|
|
2171
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2172
|
+
}
|
|
2173
|
+
const header = `App ${appId} (ws ${workspaceId}) · ${timeUnit} · ${startResult.value} → ${endResult.value}`;
|
|
2174
|
+
if (result.metrics.length === 0) {
|
|
2175
|
+
process.stdout.write(`${header}: no metrics\n`);
|
|
2176
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2177
|
+
}
|
|
2178
|
+
process.stdout.write(`${header}: ${result.metrics.length} bucket(s)\n`);
|
|
2179
|
+
for (const m of result.metrics) {
|
|
2180
|
+
const date = typeof m.date === "string" ? m.date : typeof m.bucketDate === "string" ? m.bucketDate : "";
|
|
2181
|
+
const impressions = typeof m.impressions === "number" ? m.impressions : typeof m.impressionCount === "number" ? m.impressionCount : "";
|
|
2182
|
+
const clicks = typeof m.clicks === "number" ? m.clicks : typeof m.clickCount === "number" ? m.clickCount : "";
|
|
2183
|
+
process.stdout.write(`${date}\t${impressions}\t${clicks}\n`);
|
|
2184
|
+
}
|
|
2185
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2186
|
+
} catch (err) {
|
|
2187
|
+
return emitFailureFromError(args.json, err);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
}),
|
|
2191
|
+
"share-rewards": defineCommand({
|
|
2192
|
+
meta: {
|
|
2193
|
+
name: "share-rewards",
|
|
2194
|
+
description: "Inspect share-reward promotions for a mini-app."
|
|
2195
|
+
},
|
|
2196
|
+
subCommands: { ls: defineCommand({
|
|
2197
|
+
meta: {
|
|
2198
|
+
name: "ls",
|
|
2199
|
+
description: "List share-reward promotions configured for a mini-app."
|
|
2200
|
+
},
|
|
2201
|
+
args: {
|
|
2202
|
+
id: {
|
|
2203
|
+
type: "positional",
|
|
2204
|
+
description: "Mini-app ID.",
|
|
2205
|
+
required: true
|
|
2206
|
+
},
|
|
2207
|
+
workspace: {
|
|
2208
|
+
type: "string",
|
|
2209
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
2210
|
+
},
|
|
2211
|
+
search: {
|
|
2212
|
+
type: "string",
|
|
2213
|
+
description: "Filter by title (server-side title-contains match). Empty matches everything."
|
|
2214
|
+
},
|
|
2215
|
+
json: {
|
|
2216
|
+
type: "boolean",
|
|
2217
|
+
description: "Emit machine-readable JSON.",
|
|
2218
|
+
default: false
|
|
2219
|
+
}
|
|
2220
|
+
},
|
|
2221
|
+
async run({ args }) {
|
|
2222
|
+
const appId = parseAppId(args.id);
|
|
2223
|
+
if (appId === null) {
|
|
2224
|
+
if (args.json) emitJson({
|
|
2225
|
+
ok: false,
|
|
2226
|
+
reason: "invalid-id",
|
|
2227
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
2228
|
+
});
|
|
2229
|
+
else process.stderr.write(`app share-rewards ls: invalid id ${JSON.stringify(args.id)}\n`);
|
|
2230
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
2231
|
+
}
|
|
2232
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
2233
|
+
if (!ctx) return;
|
|
2234
|
+
const { session, workspaceId } = ctx;
|
|
2235
|
+
try {
|
|
2236
|
+
const rewards = await fetchShareRewards({
|
|
2237
|
+
workspaceId,
|
|
2238
|
+
miniAppId: appId,
|
|
2239
|
+
...args.search !== void 0 ? { search: String(args.search) } : {}
|
|
2240
|
+
}, session.cookies);
|
|
2241
|
+
if (args.json) {
|
|
2242
|
+
emitJson({
|
|
2243
|
+
ok: true,
|
|
2244
|
+
workspaceId,
|
|
2245
|
+
appId,
|
|
2246
|
+
rewards
|
|
2247
|
+
});
|
|
2248
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2249
|
+
}
|
|
2250
|
+
if (rewards.length === 0) {
|
|
2251
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): no share-reward promotions\n`);
|
|
2252
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2253
|
+
}
|
|
2254
|
+
process.stdout.write(`App ${appId} (ws ${workspaceId}): ${rewards.length} share-reward(s)\n`);
|
|
2255
|
+
for (const r of rewards) {
|
|
2256
|
+
const id = typeof r.id === "string" || typeof r.id === "number" ? r.id : typeof r.rewardId === "string" || typeof r.rewardId === "number" ? r.rewardId : "-";
|
|
2257
|
+
const title = typeof r.title === "string" ? r.title : typeof r.name === "string" ? r.name : "-";
|
|
2258
|
+
const status = typeof r.status === "string" ? r.status : "-";
|
|
2259
|
+
process.stdout.write(`${id}\t${title}\t${status}\n`);
|
|
2260
|
+
}
|
|
2261
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
2262
|
+
} catch (err) {
|
|
2263
|
+
return emitFailureFromError(args.json, err);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
}) }
|
|
2267
|
+
}),
|
|
2268
|
+
register: defineCommand({
|
|
2269
|
+
meta: {
|
|
2270
|
+
name: "register",
|
|
2271
|
+
description: "Register a mini-app in the selected workspace from a YAML/JSON manifest. Uploads logo/thumbnail/screenshots, then submits the create payload."
|
|
2272
|
+
},
|
|
2273
|
+
args: {
|
|
2274
|
+
workspace: {
|
|
2275
|
+
type: "string",
|
|
2276
|
+
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
2277
|
+
},
|
|
2278
|
+
config: {
|
|
2279
|
+
type: "string",
|
|
2280
|
+
description: "Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.json`."
|
|
2281
|
+
},
|
|
2282
|
+
"dry-run": {
|
|
2283
|
+
type: "boolean",
|
|
2284
|
+
description: "Validate manifest + images and print the inferred submit payload; no uploads.",
|
|
2285
|
+
default: false
|
|
2286
|
+
},
|
|
2287
|
+
"accept-terms": {
|
|
2288
|
+
type: "boolean",
|
|
2289
|
+
description: "Attest to the required console legal-agreement checkboxes (see VALIDATION-RULES.md). Required for real submits.",
|
|
2290
|
+
default: false
|
|
2291
|
+
},
|
|
2292
|
+
json: {
|
|
2293
|
+
type: "boolean",
|
|
2294
|
+
description: "Emit machine-readable JSON to stdout.",
|
|
2295
|
+
default: false
|
|
2296
|
+
}
|
|
2297
|
+
},
|
|
2298
|
+
async run({ args }) {
|
|
2299
|
+
await runRegister({
|
|
2300
|
+
json: args.json,
|
|
2301
|
+
dryRun: args["dry-run"],
|
|
2302
|
+
acceptTerms: args["accept-terms"],
|
|
2303
|
+
...args.workspace !== void 0 ? { workspace: args.workspace } : {},
|
|
2304
|
+
...args.config !== void 0 ? { config: args.config } : {}
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2307
|
+
})
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
//#endregion
|
|
2311
|
+
//#region src/api/api-keys.ts
|
|
2312
|
+
const BASE$2 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
2313
|
+
async function fetchApiKeys(workspaceId, cookies, opts = {}) {
|
|
2314
|
+
const raw = await requestConsoleApi({
|
|
2315
|
+
url: `${BASE$2}/workspaces/${workspaceId}/api-keys`,
|
|
2316
|
+
cookies,
|
|
2317
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
2318
|
+
});
|
|
2319
|
+
if (!Array.isArray(raw)) throw new Error(`Unexpected api-keys shape for workspace=${workspaceId}: not an array`);
|
|
2320
|
+
return raw.map((entry, index) => normalizeKey(entry, workspaceId, index));
|
|
2321
|
+
}
|
|
2322
|
+
function normalizeKey(raw, workspaceId, index) {
|
|
2323
|
+
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: not an object`);
|
|
2324
|
+
const rec = raw;
|
|
2325
|
+
const rawId = rec.id ?? rec.apiKeyId ?? rec.keyId;
|
|
2326
|
+
if (typeof rawId !== "string" && typeof rawId !== "number") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: missing id`);
|
|
2327
|
+
const rawName = rec.name ?? rec.apiKeyName ?? rec.keyName ?? rec.description;
|
|
2328
|
+
const name = typeof rawName === "string" ? rawName : void 0;
|
|
2329
|
+
const { id: _id, apiKeyId: _aid, keyId: _kid, name: _n, apiKeyName: _an, keyName: _kn, description: _d, ...extra } = rec;
|
|
2330
|
+
return {
|
|
2331
|
+
id: rawId,
|
|
2332
|
+
name,
|
|
2333
|
+
extra
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
const keysCommand = defineCommand({
|
|
2337
|
+
meta: {
|
|
2338
|
+
name: "keys",
|
|
2339
|
+
description: "Inspect console API keys used for deploy automation."
|
|
2340
|
+
},
|
|
2341
|
+
subCommands: { ls: defineCommand({
|
|
2342
|
+
meta: {
|
|
2343
|
+
name: "ls",
|
|
2344
|
+
description: "List console API keys in the selected workspace."
|
|
2345
|
+
},
|
|
2346
|
+
args: {
|
|
2347
|
+
workspace: {
|
|
2348
|
+
type: "string",
|
|
2349
|
+
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
2350
|
+
},
|
|
2351
|
+
json: {
|
|
2352
|
+
type: "boolean",
|
|
2353
|
+
description: "Emit machine-readable JSON to stdout.",
|
|
1446
2354
|
default: false
|
|
1447
2355
|
}
|
|
1448
2356
|
},
|
|
@@ -2147,10 +3055,10 @@ const logoutCommand = defineCommand({
|
|
|
2147
3055
|
});
|
|
2148
3056
|
//#endregion
|
|
2149
3057
|
//#region src/api/members.ts
|
|
2150
|
-
const BASE = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
3058
|
+
const BASE$1 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
|
|
2151
3059
|
async function fetchWorkspaceMembers(workspaceId, cookies, opts = {}) {
|
|
2152
3060
|
const raw = await requestConsoleApi({
|
|
2153
|
-
url: `${BASE}/workspaces/${workspaceId}/members`,
|
|
3061
|
+
url: `${BASE$1}/workspaces/${workspaceId}/members`,
|
|
2154
3062
|
cookies,
|
|
2155
3063
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
2156
3064
|
});
|
|
@@ -2235,6 +3143,257 @@ const membersCommand = defineCommand({
|
|
|
2235
3143
|
}
|
|
2236
3144
|
}) }
|
|
2237
3145
|
});
|
|
3146
|
+
const BASE = "https://api-public.toss.im/api-public/v3/ipd-thor/api/v1";
|
|
3147
|
+
async function fetchNotices(params, cookies, opts = {}) {
|
|
3148
|
+
const qs = new URLSearchParams();
|
|
3149
|
+
qs.set("page", String(params.page ?? 0));
|
|
3150
|
+
qs.set("size", String(params.size ?? 20));
|
|
3151
|
+
if (params.titleContains !== void 0) qs.set("title__icontains", params.titleContains);
|
|
3152
|
+
const raw = await requestConsoleApi({
|
|
3153
|
+
url: `${BASE}/workspaces/129/posts?${qs.toString()}`,
|
|
3154
|
+
cookies,
|
|
3155
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
3156
|
+
});
|
|
3157
|
+
if (raw === null || typeof raw !== "object") throw new Error("Unexpected notices shape: not an object");
|
|
3158
|
+
const rec = raw;
|
|
3159
|
+
const resultsRaw = rec.results;
|
|
3160
|
+
if (!Array.isArray(resultsRaw)) throw new Error("Unexpected notices shape: results is not an array");
|
|
3161
|
+
const results = resultsRaw.map((r) => {
|
|
3162
|
+
if (r === null || typeof r !== "object") return {};
|
|
3163
|
+
return r;
|
|
3164
|
+
});
|
|
3165
|
+
return {
|
|
3166
|
+
page: typeof rec.page === "number" ? rec.page : 1,
|
|
3167
|
+
pageSize: typeof rec.pageSize === "number" ? rec.pageSize : params.size ?? 20,
|
|
3168
|
+
count: typeof rec.count === "number" ? rec.count : results.length,
|
|
3169
|
+
next: typeof rec.next === "string" ? rec.next : null,
|
|
3170
|
+
previous: typeof rec.previous === "string" ? rec.previous : null,
|
|
3171
|
+
results
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
async function fetchNoticeCategories(cookies, opts = {}) {
|
|
3175
|
+
const raw = await requestConsoleApi({
|
|
3176
|
+
url: `${BASE}/workspaces/129/categories`,
|
|
3177
|
+
cookies,
|
|
3178
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
3179
|
+
});
|
|
3180
|
+
if (!Array.isArray(raw)) throw new Error("Unexpected categories shape: not an array");
|
|
3181
|
+
return raw.map((c) => {
|
|
3182
|
+
if (c === null || typeof c !== "object") return {};
|
|
3183
|
+
return c;
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
async function fetchNoticePost(postId, cookies, opts = {}) {
|
|
3187
|
+
const raw = await requestConsoleApi({
|
|
3188
|
+
url: `${BASE}/workspaces/129/posts/${postId}`,
|
|
3189
|
+
cookies,
|
|
3190
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
3191
|
+
});
|
|
3192
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected notice-post shape for id=${postId}`);
|
|
3193
|
+
return raw;
|
|
3194
|
+
}
|
|
3195
|
+
//#endregion
|
|
3196
|
+
//#region src/commands/notices.ts
|
|
3197
|
+
function parsePositiveInt(raw, field) {
|
|
3198
|
+
const n = Number(raw);
|
|
3199
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) return { error: `--${field} must be a non-negative integer (got ${JSON.stringify(raw)})` };
|
|
3200
|
+
return { value: n };
|
|
3201
|
+
}
|
|
3202
|
+
const noticesCommand = defineCommand({
|
|
3203
|
+
meta: {
|
|
3204
|
+
name: "notices",
|
|
3205
|
+
description: "Read Apps in Toss notices (공지사항). Shared across all users."
|
|
3206
|
+
},
|
|
3207
|
+
subCommands: {
|
|
3208
|
+
ls: defineCommand({
|
|
3209
|
+
meta: {
|
|
3210
|
+
name: "ls",
|
|
3211
|
+
description: "List notices (공지사항) from Apps in Toss."
|
|
3212
|
+
},
|
|
3213
|
+
args: {
|
|
3214
|
+
page: {
|
|
3215
|
+
type: "string",
|
|
3216
|
+
description: "Page number (0-indexed).",
|
|
3217
|
+
default: "0"
|
|
3218
|
+
},
|
|
3219
|
+
size: {
|
|
3220
|
+
type: "string",
|
|
3221
|
+
description: "Page size.",
|
|
3222
|
+
default: "20"
|
|
3223
|
+
},
|
|
3224
|
+
search: {
|
|
3225
|
+
type: "string",
|
|
3226
|
+
description: "Filter by title substring (case-insensitive)."
|
|
3227
|
+
},
|
|
3228
|
+
json: {
|
|
3229
|
+
type: "boolean",
|
|
3230
|
+
description: "Emit machine-readable JSON.",
|
|
3231
|
+
default: false
|
|
3232
|
+
}
|
|
3233
|
+
},
|
|
3234
|
+
async run({ args }) {
|
|
3235
|
+
const session = await requireSession(args.json);
|
|
3236
|
+
if (!session) return;
|
|
3237
|
+
const pageResult = parsePositiveInt(args.page, "page");
|
|
3238
|
+
if ("error" in pageResult) {
|
|
3239
|
+
if (args.json) emitJson({
|
|
3240
|
+
ok: false,
|
|
3241
|
+
reason: "invalid-config",
|
|
3242
|
+
field: "page",
|
|
3243
|
+
message: pageResult.error
|
|
3244
|
+
});
|
|
3245
|
+
else process.stderr.write(`notices ls: ${pageResult.error}\n`);
|
|
3246
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
3247
|
+
}
|
|
3248
|
+
const sizeResult = parsePositiveInt(args.size, "size");
|
|
3249
|
+
if ("error" in sizeResult) {
|
|
3250
|
+
if (args.json) emitJson({
|
|
3251
|
+
ok: false,
|
|
3252
|
+
reason: "invalid-config",
|
|
3253
|
+
field: "size",
|
|
3254
|
+
message: sizeResult.error
|
|
3255
|
+
});
|
|
3256
|
+
else process.stderr.write(`notices ls: ${sizeResult.error}\n`);
|
|
3257
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
3258
|
+
}
|
|
3259
|
+
if (sizeResult.value === 0) {
|
|
3260
|
+
if (args.json) emitJson({
|
|
3261
|
+
ok: false,
|
|
3262
|
+
reason: "invalid-config",
|
|
3263
|
+
field: "size",
|
|
3264
|
+
message: "--size must be at least 1"
|
|
3265
|
+
});
|
|
3266
|
+
else process.stderr.write("notices ls: --size must be at least 1\n");
|
|
3267
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
3268
|
+
}
|
|
3269
|
+
try {
|
|
3270
|
+
const result = await fetchNotices({
|
|
3271
|
+
page: pageResult.value,
|
|
3272
|
+
size: sizeResult.value,
|
|
3273
|
+
...typeof args.search === "string" && args.search.length > 0 ? { titleContains: args.search } : {}
|
|
3274
|
+
}, session.cookies);
|
|
3275
|
+
if (args.json) {
|
|
3276
|
+
emitJson({
|
|
3277
|
+
ok: true,
|
|
3278
|
+
page: result.page,
|
|
3279
|
+
pageSize: result.pageSize,
|
|
3280
|
+
count: result.count,
|
|
3281
|
+
hasNext: result.next !== null,
|
|
3282
|
+
notices: result.results
|
|
3283
|
+
});
|
|
3284
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
3285
|
+
}
|
|
3286
|
+
process.stdout.write(`Notices: page ${result.page}, ${result.results.length}/${result.count} shown\n`);
|
|
3287
|
+
if (result.results.length === 0) {
|
|
3288
|
+
process.stdout.write("No notices on this page.\n");
|
|
3289
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
3290
|
+
}
|
|
3291
|
+
for (const n of result.results) {
|
|
3292
|
+
const id = typeof n.id === "string" || typeof n.id === "number" ? n.id : "-";
|
|
3293
|
+
const category = typeof n.category === "string" ? n.category : "-";
|
|
3294
|
+
const title = typeof n.title === "string" ? n.title : "";
|
|
3295
|
+
const publishedTime = typeof n.publishedTime === "string" ? n.publishedTime : "";
|
|
3296
|
+
process.stdout.write(`${id}\t${publishedTime}\t[${category}]\t${title}\n`);
|
|
3297
|
+
}
|
|
3298
|
+
if (result.next !== null) process.stdout.write(`(more: --page ${result.page + 1})\n`);
|
|
3299
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
3300
|
+
} catch (err) {
|
|
3301
|
+
return emitFailureFromError(args.json, err);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
}),
|
|
3305
|
+
show: defineCommand({
|
|
3306
|
+
meta: {
|
|
3307
|
+
name: "show",
|
|
3308
|
+
description: "Show a single notice post by ID."
|
|
3309
|
+
},
|
|
3310
|
+
args: {
|
|
3311
|
+
id: {
|
|
3312
|
+
type: "positional",
|
|
3313
|
+
description: "Notice post ID.",
|
|
3314
|
+
required: true
|
|
3315
|
+
},
|
|
3316
|
+
json: {
|
|
3317
|
+
type: "boolean",
|
|
3318
|
+
description: "Emit machine-readable JSON.",
|
|
3319
|
+
default: false
|
|
3320
|
+
}
|
|
3321
|
+
},
|
|
3322
|
+
async run({ args }) {
|
|
3323
|
+
const postId = Number(args.id);
|
|
3324
|
+
if (!Number.isFinite(postId) || !Number.isInteger(postId) || postId <= 0) {
|
|
3325
|
+
if (args.json) emitJson({
|
|
3326
|
+
ok: false,
|
|
3327
|
+
reason: "invalid-id",
|
|
3328
|
+
message: `notice id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
3329
|
+
});
|
|
3330
|
+
else process.stderr.write(`notices show: invalid id ${JSON.stringify(args.id)}\n`);
|
|
3331
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
3332
|
+
}
|
|
3333
|
+
const session = await requireSession(args.json);
|
|
3334
|
+
if (!session) return;
|
|
3335
|
+
try {
|
|
3336
|
+
const notice = await fetchNoticePost(postId, session.cookies);
|
|
3337
|
+
if (args.json) {
|
|
3338
|
+
emitJson({
|
|
3339
|
+
ok: true,
|
|
3340
|
+
id: postId,
|
|
3341
|
+
notice
|
|
3342
|
+
});
|
|
3343
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
3344
|
+
}
|
|
3345
|
+
const title = typeof notice.title === "string" ? notice.title : "";
|
|
3346
|
+
const subtitle = typeof notice.subtitle === "string" ? notice.subtitle : "";
|
|
3347
|
+
const category = typeof notice.category === "string" ? notice.category : "";
|
|
3348
|
+
const publishedTime = typeof notice.publishedTime === "string" ? notice.publishedTime : "";
|
|
3349
|
+
const body = typeof notice.fullDescription === "string" ? notice.fullDescription : typeof notice.shortDescription === "string" ? notice.shortDescription : "";
|
|
3350
|
+
process.stdout.write(`# ${title}\n`);
|
|
3351
|
+
if (subtitle) process.stdout.write(`${subtitle}\n`);
|
|
3352
|
+
process.stdout.write(`\n[${category}] ${publishedTime}\n\n`);
|
|
3353
|
+
process.stdout.write(body);
|
|
3354
|
+
if (!body.endsWith("\n")) process.stdout.write("\n");
|
|
3355
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
3356
|
+
} catch (err) {
|
|
3357
|
+
return emitFailureFromError(args.json, err);
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
}),
|
|
3361
|
+
categories: defineCommand({
|
|
3362
|
+
meta: {
|
|
3363
|
+
name: "categories",
|
|
3364
|
+
description: "List notice categories and their post counts."
|
|
3365
|
+
},
|
|
3366
|
+
args: { json: {
|
|
3367
|
+
type: "boolean",
|
|
3368
|
+
description: "Emit machine-readable JSON.",
|
|
3369
|
+
default: false
|
|
3370
|
+
} },
|
|
3371
|
+
async run({ args }) {
|
|
3372
|
+
const session = await requireSession(args.json);
|
|
3373
|
+
if (!session) return;
|
|
3374
|
+
try {
|
|
3375
|
+
const categories = await fetchNoticeCategories(session.cookies);
|
|
3376
|
+
if (args.json) {
|
|
3377
|
+
emitJson({
|
|
3378
|
+
ok: true,
|
|
3379
|
+
categories
|
|
3380
|
+
});
|
|
3381
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
3382
|
+
}
|
|
3383
|
+
for (const c of categories) {
|
|
3384
|
+
const id = typeof c.id === "string" || typeof c.id === "number" ? c.id : "-";
|
|
3385
|
+
const name = typeof c.name === "string" ? c.name : "-";
|
|
3386
|
+
const postCount = typeof c.postCount === "number" ? c.postCount : 0;
|
|
3387
|
+
process.stdout.write(`${id}\t${postCount}\t${name}\n`);
|
|
3388
|
+
}
|
|
3389
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
3390
|
+
} catch (err) {
|
|
3391
|
+
return emitFailureFromError(args.json, err);
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
})
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
2238
3397
|
//#endregion
|
|
2239
3398
|
//#region src/github.ts
|
|
2240
3399
|
const REPO_OWNER = "apps-in-toss-community";
|
|
@@ -2348,7 +3507,7 @@ function resolveVersion() {
|
|
|
2348
3507
|
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
2349
3508
|
} catch {}
|
|
2350
3509
|
try {
|
|
2351
|
-
return "0.1.
|
|
3510
|
+
return "0.1.11";
|
|
2352
3511
|
} catch {}
|
|
2353
3512
|
return "0.0.0-dev";
|
|
2354
3513
|
}
|
|
@@ -2805,7 +3964,7 @@ const workspaceCommand = defineCommand({
|
|
|
2805
3964
|
},
|
|
2806
3965
|
async run({ args }) {
|
|
2807
3966
|
const raw = String(args.id);
|
|
2808
|
-
const parsed = parsePositiveInt(raw);
|
|
3967
|
+
const parsed = parsePositiveInt$1(raw);
|
|
2809
3968
|
if (parsed === null) {
|
|
2810
3969
|
const message = `workspace id must be a positive integer (got ${raw})`;
|
|
2811
3970
|
if (args.json) emitJson({
|
|
@@ -2873,7 +4032,7 @@ const workspaceCommand = defineCommand({
|
|
|
2873
4032
|
let workspaceId;
|
|
2874
4033
|
if (args.workspace) {
|
|
2875
4034
|
const raw = String(args.workspace);
|
|
2876
|
-
const parsed = parsePositiveInt(raw);
|
|
4035
|
+
const parsed = parsePositiveInt$1(raw);
|
|
2877
4036
|
if (parsed === null) {
|
|
2878
4037
|
const message = `--workspace must be a positive integer (got ${raw})`;
|
|
2879
4038
|
if (args.json) emitJson({
|
|
@@ -2931,7 +4090,8 @@ runMain(defineCommand({
|
|
|
2931
4090
|
workspace: workspaceCommand,
|
|
2932
4091
|
app: appCommand,
|
|
2933
4092
|
members: membersCommand,
|
|
2934
|
-
keys: keysCommand
|
|
4093
|
+
keys: keysCommand,
|
|
4094
|
+
notices: noticesCommand
|
|
2935
4095
|
}
|
|
2936
4096
|
}));
|
|
2937
4097
|
//#endregion
|