@ait-co/console-cli 0.1.9 → 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 +1490 -217
- 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
|
});
|
|
@@ -214,15 +214,156 @@ async function fetchMiniAppWithDraft(workspaceId, miniAppId, cookies, opts = {})
|
|
|
214
214
|
const rec = raw;
|
|
215
215
|
return {
|
|
216
216
|
current: isRecordOrNull(rec.current) ? rec.current : null,
|
|
217
|
-
draft: isRecordOrNull(rec.draft) ? rec.draft : null
|
|
217
|
+
draft: isRecordOrNull(rec.draft) ? rec.draft : null,
|
|
218
|
+
approvalType: typeof rec.approvalType === "string" ? rec.approvalType : null,
|
|
219
|
+
rejectedMessage: typeof rec.rejectedMessage === "string" ? rec.rejectedMessage : null
|
|
218
220
|
};
|
|
219
221
|
}
|
|
220
222
|
function isRecordOrNull(v) {
|
|
221
223
|
return v === null || typeof v === "object" && !Array.isArray(v);
|
|
222
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
|
+
}
|
|
223
364
|
async function createMiniApp(workspaceId, payload, cookies, opts = {}) {
|
|
224
365
|
return normalizeCreateResult(await requestConsoleApi({
|
|
225
|
-
url: `${BASE$
|
|
366
|
+
url: `${BASE$3}/workspaces/${workspaceId}/mini-app/review`,
|
|
226
367
|
method: "POST",
|
|
227
368
|
cookies,
|
|
228
369
|
body: payload,
|
|
@@ -255,7 +396,7 @@ function normalizeCreateResult(raw) {
|
|
|
255
396
|
* the field name is actually `file` — if so, swap it in one place here.
|
|
256
397
|
*/
|
|
257
398
|
async function uploadMiniAppResource(params, opts = {}) {
|
|
258
|
-
const url = new URL(`${BASE$
|
|
399
|
+
const url = new URL(`${BASE$3}/resource/${params.workspaceId}/upload`);
|
|
259
400
|
url.searchParams.set("validWidth", String(params.validWidth));
|
|
260
401
|
url.searchParams.set("validHeight", String(params.validHeight));
|
|
261
402
|
const form = new FormData();
|
|
@@ -505,7 +646,7 @@ async function emitFailureFromError(json, err) {
|
|
|
505
646
|
emitApiError(json, err.message);
|
|
506
647
|
return exitAfterFlush(ExitCode.ApiError);
|
|
507
648
|
}
|
|
508
|
-
function parsePositiveInt(raw) {
|
|
649
|
+
function parsePositiveInt$1(raw) {
|
|
509
650
|
if (!/^[1-9]\d*$/.test(raw)) return null;
|
|
510
651
|
const n = Number.parseInt(raw, 10);
|
|
511
652
|
return Number.isSafeInteger(n) ? n : null;
|
|
@@ -533,7 +674,7 @@ async function resolveWorkspaceContext(args) {
|
|
|
533
674
|
let workspaceId;
|
|
534
675
|
if (args.workspace) {
|
|
535
676
|
const raw = String(args.workspace);
|
|
536
|
-
const parsed = parsePositiveInt(raw);
|
|
677
|
+
const parsed = parsePositiveInt$1(raw);
|
|
537
678
|
if (parsed === null) {
|
|
538
679
|
const message = `--workspace must be a positive integer (got ${raw})`;
|
|
539
680
|
if (args.json) emitJson({
|
|
@@ -561,6 +702,21 @@ async function resolveWorkspaceContext(args) {
|
|
|
561
702
|
workspaceId
|
|
562
703
|
};
|
|
563
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
|
+
}
|
|
564
720
|
//#endregion
|
|
565
721
|
//#region src/config/app-manifest.ts
|
|
566
722
|
var ManifestError = class extends Error {
|
|
@@ -1044,7 +1200,7 @@ function reviewStateFor(entry) {
|
|
|
1044
1200
|
const raw = entry.reviewState ?? entry.status;
|
|
1045
1201
|
return typeof raw === "string" ? raw : void 0;
|
|
1046
1202
|
}
|
|
1047
|
-
const lsCommand$
|
|
1203
|
+
const lsCommand$4 = defineCommand({
|
|
1048
1204
|
meta: {
|
|
1049
1205
|
name: "ls",
|
|
1050
1206
|
description: "List mini-apps in the selected workspace."
|
|
@@ -1124,220 +1280,1085 @@ function parseAppId(raw) {
|
|
|
1124
1280
|
if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return null;
|
|
1125
1281
|
return n;
|
|
1126
1282
|
}
|
|
1127
|
-
const
|
|
1283
|
+
const showCommand$2 = defineCommand({
|
|
1128
1284
|
meta: {
|
|
1129
|
-
name: "
|
|
1130
|
-
description: "
|
|
1285
|
+
name: "show",
|
|
1286
|
+
description: "Show full details of a mini-app, including fields only visible in the draft view."
|
|
1131
1287
|
},
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
if (args.json) {
|
|
1189
|
-
emitJson({
|
|
1190
|
-
ok: true,
|
|
1191
|
-
workspaceId,
|
|
1192
|
-
appId,
|
|
1193
|
-
view,
|
|
1194
|
-
miniApp
|
|
1195
|
-
});
|
|
1196
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
1197
|
-
}
|
|
1198
|
-
if (miniApp === null) {
|
|
1199
|
-
if (view === "current" && envelope.draft !== null) process.stdout.write(`App ${appId} has no \`current\` view yet (not reviewed). Try --view draft.\n`);
|
|
1200
|
-
else process.stdout.write(`App ${appId} has no data for view=${view}.\n`);
|
|
1201
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
1202
|
-
}
|
|
1203
|
-
const pick = (k) => {
|
|
1204
|
-
const v = miniApp[k];
|
|
1205
|
-
return v === null || v === void 0 ? "-" : String(v);
|
|
1206
|
-
};
|
|
1207
|
-
const images = Array.isArray(miniApp.images) ? miniApp.images : [];
|
|
1208
|
-
const impression = miniApp.impression !== null && typeof miniApp.impression === "object" ? miniApp.impression : {};
|
|
1209
|
-
const keywords = Array.isArray(impression.keywordList) ? impression.keywordList : [];
|
|
1210
|
-
const categoryPaths = Array.isArray(impression.categoryPaths) ? impression.categoryPaths : [];
|
|
1211
|
-
process.stdout.write(`# App ${appId} (view=${view})\n\n`);
|
|
1212
|
-
process.stdout.write(`Name (ko) ${pick("title")}\n`);
|
|
1213
|
-
process.stdout.write(`Name (en) ${pick("titleEn")}\n`);
|
|
1214
|
-
process.stdout.write(`App slug ${pick("appName")}\n`);
|
|
1215
|
-
process.stdout.write(`Status ${pick("status")}\n`);
|
|
1216
|
-
process.stdout.write(`Home page ${pick("homePageUri")}\n`);
|
|
1217
|
-
process.stdout.write(`CS email ${pick("csEmail")}\n`);
|
|
1218
|
-
process.stdout.write(`Logo ${pick("iconUri")}\n`);
|
|
1219
|
-
process.stdout.write(`Subtitle ${pick("description")}\n`);
|
|
1220
|
-
const detail = typeof miniApp.detailDescription === "string" ? `${[...miniApp.detailDescription].length} chars` : "-";
|
|
1221
|
-
process.stdout.write(`Detail desc ${detail}\n`);
|
|
1222
|
-
process.stdout.write(`Images ${images.length}\n`);
|
|
1223
|
-
process.stdout.write(`Keywords ${keywords.length} (${keywords.join(", ")})\n`);
|
|
1224
|
-
const firstPath = categoryPaths[0];
|
|
1225
|
-
if (firstPath && typeof firstPath === "object") {
|
|
1226
|
-
const fp = firstPath;
|
|
1227
|
-
const parts = [];
|
|
1228
|
-
for (const key of [
|
|
1229
|
-
"group",
|
|
1230
|
-
"category",
|
|
1231
|
-
"subCategory"
|
|
1232
|
-
]) {
|
|
1233
|
-
const node = fp[key];
|
|
1234
|
-
if (node !== null && typeof node === "object") {
|
|
1235
|
-
const nm = node.name;
|
|
1236
|
-
if (typeof nm === "string") parts.push(nm);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
process.stdout.write(`Category ${parts.join(" > ") || "-"}\n`);
|
|
1240
|
-
} else process.stdout.write(`Category -\n`);
|
|
1241
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
1242
|
-
} catch (err) {
|
|
1243
|
-
return emitFailureFromError(args.json, err);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
}),
|
|
1247
|
-
register: defineCommand({
|
|
1248
|
-
meta: {
|
|
1249
|
-
name: "register",
|
|
1250
|
-
description: "Register a mini-app in the selected workspace from a YAML/JSON manifest. Uploads logo/thumbnail/screenshots, then submits the create payload."
|
|
1251
|
-
},
|
|
1252
|
-
args: {
|
|
1253
|
-
workspace: {
|
|
1254
|
-
type: "string",
|
|
1255
|
-
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
1256
|
-
},
|
|
1257
|
-
config: {
|
|
1258
|
-
type: "string",
|
|
1259
|
-
description: "Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.json`."
|
|
1260
|
-
},
|
|
1261
|
-
"dry-run": {
|
|
1262
|
-
type: "boolean",
|
|
1263
|
-
description: "Validate manifest + images and print the inferred submit payload; no uploads.",
|
|
1264
|
-
default: false
|
|
1265
|
-
},
|
|
1266
|
-
"accept-terms": {
|
|
1267
|
-
type: "boolean",
|
|
1268
|
-
description: "Attest to the required console legal-agreement checkboxes (see VALIDATION-RULES.md). Required for real submits.",
|
|
1269
|
-
default: false
|
|
1270
|
-
},
|
|
1271
|
-
json: {
|
|
1272
|
-
type: "boolean",
|
|
1273
|
-
description: "Emit machine-readable JSON to stdout.",
|
|
1274
|
-
default: false
|
|
1275
|
-
}
|
|
1276
|
-
},
|
|
1277
|
-
async run({ args }) {
|
|
1278
|
-
await runRegister({
|
|
1279
|
-
json: args.json,
|
|
1280
|
-
dryRun: args["dry-run"],
|
|
1281
|
-
acceptTerms: args["accept-terms"],
|
|
1282
|
-
...args.workspace !== void 0 ? { workspace: args.workspace } : {},
|
|
1283
|
-
...args.config !== void 0 ? { config: args.config } : {}
|
|
1288
|
+
args: {
|
|
1289
|
+
id: {
|
|
1290
|
+
type: "positional",
|
|
1291
|
+
description: "Mini-app ID (the numeric `appId` from `app ls` or `app register`).",
|
|
1292
|
+
required: true
|
|
1293
|
+
},
|
|
1294
|
+
workspace: {
|
|
1295
|
+
type: "string",
|
|
1296
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1297
|
+
},
|
|
1298
|
+
view: {
|
|
1299
|
+
type: "string",
|
|
1300
|
+
description: "Which view to render: `draft` (default), `current`, or `merged`.",
|
|
1301
|
+
default: "draft"
|
|
1302
|
+
},
|
|
1303
|
+
json: {
|
|
1304
|
+
type: "boolean",
|
|
1305
|
+
description: "Emit machine-readable JSON to stdout.",
|
|
1306
|
+
default: false
|
|
1307
|
+
}
|
|
1308
|
+
},
|
|
1309
|
+
async run({ args }) {
|
|
1310
|
+
const appId = parseAppId(args.id);
|
|
1311
|
+
if (appId === null) {
|
|
1312
|
+
if (args.json) emitJson({
|
|
1313
|
+
ok: false,
|
|
1314
|
+
reason: "invalid-id",
|
|
1315
|
+
message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
|
|
1316
|
+
});
|
|
1317
|
+
else process.stderr.write(`app show: invalid id ${JSON.stringify(args.id)}\n`);
|
|
1318
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1319
|
+
}
|
|
1320
|
+
const view = args.view;
|
|
1321
|
+
if (view !== "draft" && view !== "current" && view !== "merged") {
|
|
1322
|
+
if (args.json) emitJson({
|
|
1323
|
+
ok: false,
|
|
1324
|
+
reason: "invalid-config",
|
|
1325
|
+
field: "view",
|
|
1326
|
+
message: `--view must be one of draft|current|merged (got ${JSON.stringify(view)})`
|
|
1327
|
+
});
|
|
1328
|
+
else process.stderr.write(`app show: invalid --view ${JSON.stringify(view)}\n`);
|
|
1329
|
+
return exitAfterFlush(ExitCode.Usage);
|
|
1330
|
+
}
|
|
1331
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
1332
|
+
if (!ctx) return;
|
|
1333
|
+
const { session, workspaceId } = ctx;
|
|
1334
|
+
try {
|
|
1335
|
+
const envelope = await fetchMiniAppWithDraft(workspaceId, appId, session.cookies);
|
|
1336
|
+
const miniApp = pickMiniAppView(envelope, view);
|
|
1337
|
+
if (args.json) {
|
|
1338
|
+
emitJson({
|
|
1339
|
+
ok: true,
|
|
1340
|
+
workspaceId,
|
|
1341
|
+
appId,
|
|
1342
|
+
view,
|
|
1343
|
+
miniApp
|
|
1284
1344
|
});
|
|
1345
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1285
1346
|
}
|
|
1286
|
-
|
|
1347
|
+
if (miniApp === null) {
|
|
1348
|
+
if (view === "current" && envelope.draft !== null) process.stdout.write(`App ${appId} has no \`current\` view yet (not reviewed). Try --view draft.\n`);
|
|
1349
|
+
else process.stdout.write(`App ${appId} has no data for view=${view}.\n`);
|
|
1350
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1351
|
+
}
|
|
1352
|
+
const pick = (k) => {
|
|
1353
|
+
const v = miniApp[k];
|
|
1354
|
+
return v === null || v === void 0 ? "-" : String(v);
|
|
1355
|
+
};
|
|
1356
|
+
const images = Array.isArray(miniApp.images) ? miniApp.images : [];
|
|
1357
|
+
const impression = miniApp.impression !== null && typeof miniApp.impression === "object" ? miniApp.impression : {};
|
|
1358
|
+
const keywords = Array.isArray(impression.keywordList) ? impression.keywordList : [];
|
|
1359
|
+
const categoryPaths = Array.isArray(impression.categoryPaths) ? impression.categoryPaths : [];
|
|
1360
|
+
process.stdout.write(`# App ${appId} (view=${view})\n\n`);
|
|
1361
|
+
process.stdout.write(`Name (ko) ${pick("title")}\n`);
|
|
1362
|
+
process.stdout.write(`Name (en) ${pick("titleEn")}\n`);
|
|
1363
|
+
process.stdout.write(`App slug ${pick("appName")}\n`);
|
|
1364
|
+
process.stdout.write(`Status ${pick("status")}\n`);
|
|
1365
|
+
process.stdout.write(`Home page ${pick("homePageUri")}\n`);
|
|
1366
|
+
process.stdout.write(`CS email ${pick("csEmail")}\n`);
|
|
1367
|
+
process.stdout.write(`Logo ${pick("iconUri")}\n`);
|
|
1368
|
+
process.stdout.write(`Subtitle ${pick("description")}\n`);
|
|
1369
|
+
const detail = typeof miniApp.detailDescription === "string" ? `${[...miniApp.detailDescription].length} chars` : "-";
|
|
1370
|
+
process.stdout.write(`Detail desc ${detail}\n`);
|
|
1371
|
+
process.stdout.write(`Images ${images.length}\n`);
|
|
1372
|
+
process.stdout.write(`Keywords ${keywords.length} (${keywords.join(", ")})\n`);
|
|
1373
|
+
const firstPath = categoryPaths[0];
|
|
1374
|
+
if (firstPath && typeof firstPath === "object") {
|
|
1375
|
+
const fp = firstPath;
|
|
1376
|
+
const parts = [];
|
|
1377
|
+
for (const key of [
|
|
1378
|
+
"group",
|
|
1379
|
+
"category",
|
|
1380
|
+
"subCategory"
|
|
1381
|
+
]) {
|
|
1382
|
+
const node = fp[key];
|
|
1383
|
+
if (node !== null && typeof node === "object") {
|
|
1384
|
+
const nm = node.name;
|
|
1385
|
+
if (typeof nm === "string") parts.push(nm);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
process.stdout.write(`Category ${parts.join(" > ") || "-"}\n`);
|
|
1389
|
+
} else process.stdout.write(`Category -\n`);
|
|
1390
|
+
return exitAfterFlush(ExitCode.Ok);
|
|
1391
|
+
} catch (err) {
|
|
1392
|
+
return emitFailureFromError(args.json, err);
|
|
1393
|
+
}
|
|
1287
1394
|
}
|
|
1288
1395
|
});
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
if (
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
function normalizeKey(raw, workspaceId, index) {
|
|
1302
|
-
if (raw === null || typeof raw !== "object") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: not an object`);
|
|
1303
|
-
const rec = raw;
|
|
1304
|
-
const rawId = rec.id ?? rec.apiKeyId ?? rec.keyId;
|
|
1305
|
-
if (typeof rawId !== "string" && typeof rawId !== "number") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: missing id`);
|
|
1306
|
-
const rawName = rec.name ?? rec.apiKeyName ?? rec.keyName ?? rec.description;
|
|
1307
|
-
const name = typeof rawName === "string" ? rawName : void 0;
|
|
1308
|
-
const { id: _id, apiKeyId: _aid, keyId: _kid, name: _n, apiKeyName: _an, keyName: _kn, description: _d, ...extra } = rec;
|
|
1396
|
+
function deriveReviewState(env) {
|
|
1397
|
+
const hasCurrent = env.current !== null;
|
|
1398
|
+
const hasDraft = env.draft !== null;
|
|
1399
|
+
const approvalType = env.approvalType;
|
|
1400
|
+
const rejectedMessage = env.rejectedMessage;
|
|
1401
|
+
let state;
|
|
1402
|
+
if (approvalType === null) state = "not-submitted";
|
|
1403
|
+
else if (rejectedMessage !== null) state = "rejected";
|
|
1404
|
+
else if (!hasCurrent) state = "under-review";
|
|
1405
|
+
else if (hasDraft) state = "approved-with-edits";
|
|
1406
|
+
else state = "approved";
|
|
1407
|
+
if (approvalType !== null && approvalType !== "REVIEW" && state === "under-review") state = "unknown";
|
|
1309
1408
|
return {
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1409
|
+
state,
|
|
1410
|
+
approvalType,
|
|
1411
|
+
rejectedMessage,
|
|
1412
|
+
hasCurrent,
|
|
1413
|
+
hasDraft
|
|
1313
1414
|
};
|
|
1314
1415
|
}
|
|
1315
|
-
const
|
|
1416
|
+
const POLL_MIN_INTERVAL_SEC = 30;
|
|
1417
|
+
const POLL_MAX_INTERVAL_SEC = 3600;
|
|
1418
|
+
const statusCommand = defineCommand({
|
|
1316
1419
|
meta: {
|
|
1317
|
-
name: "
|
|
1318
|
-
description: "
|
|
1420
|
+
name: "status",
|
|
1421
|
+
description: "Show the derived review state of a mini-app (under-review / rejected / approved)."
|
|
1319
1422
|
},
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
description: "
|
|
1423
|
+
args: {
|
|
1424
|
+
id: {
|
|
1425
|
+
type: "positional",
|
|
1426
|
+
description: "Mini-app ID.",
|
|
1427
|
+
required: true
|
|
1324
1428
|
},
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
|
|
1329
|
-
},
|
|
1330
|
-
json: {
|
|
1331
|
-
type: "boolean",
|
|
1332
|
-
description: "Emit machine-readable JSON to stdout.",
|
|
1333
|
-
default: false
|
|
1334
|
-
}
|
|
1429
|
+
workspace: {
|
|
1430
|
+
type: "string",
|
|
1431
|
+
description: "Workspace ID. Defaults to the selected workspace."
|
|
1335
1432
|
},
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
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);
|
|
1491
|
+
}
|
|
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));
|
|
1500
|
+
}
|
|
1501
|
+
} catch (err) {
|
|
1502
|
+
return emitFailureFromError(args.json, err);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
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 };
|
|
1512
|
+
}
|
|
1513
|
+
const ratingsCommand = defineCommand({
|
|
1514
|
+
meta: {
|
|
1515
|
+
name: "ratings",
|
|
1516
|
+
description: "List user ratings and reviews left for a mini-app."
|
|
1517
|
+
},
|
|
1518
|
+
args: {
|
|
1519
|
+
id: {
|
|
1520
|
+
type: "positional",
|
|
1521
|
+
description: "Mini-app ID.",
|
|
1522
|
+
required: true
|
|
1523
|
+
},
|
|
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.",
|
|
2354
|
+
default: false
|
|
2355
|
+
}
|
|
2356
|
+
},
|
|
2357
|
+
async run({ args }) {
|
|
2358
|
+
const ctx = await resolveWorkspaceContext(args);
|
|
2359
|
+
if (!ctx) return;
|
|
2360
|
+
const { session, workspaceId } = ctx;
|
|
2361
|
+
try {
|
|
1341
2362
|
const keys = await fetchApiKeys(workspaceId, session.cookies);
|
|
1342
2363
|
if (args.json) {
|
|
1343
2364
|
emitJson({
|
|
@@ -2034,10 +3055,10 @@ const logoutCommand = defineCommand({
|
|
|
2034
3055
|
});
|
|
2035
3056
|
//#endregion
|
|
2036
3057
|
//#region src/api/members.ts
|
|
2037
|
-
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";
|
|
2038
3059
|
async function fetchWorkspaceMembers(workspaceId, cookies, opts = {}) {
|
|
2039
3060
|
const raw = await requestConsoleApi({
|
|
2040
|
-
url: `${BASE}/workspaces/${workspaceId}/members`,
|
|
3061
|
+
url: `${BASE$1}/workspaces/${workspaceId}/members`,
|
|
2041
3062
|
cookies,
|
|
2042
3063
|
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
2043
3064
|
});
|
|
@@ -2122,6 +3143,257 @@ const membersCommand = defineCommand({
|
|
|
2122
3143
|
}
|
|
2123
3144
|
}) }
|
|
2124
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
|
+
});
|
|
2125
3397
|
//#endregion
|
|
2126
3398
|
//#region src/github.ts
|
|
2127
3399
|
const REPO_OWNER = "apps-in-toss-community";
|
|
@@ -2235,7 +3507,7 @@ function resolveVersion() {
|
|
|
2235
3507
|
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
2236
3508
|
} catch {}
|
|
2237
3509
|
try {
|
|
2238
|
-
return "0.1.
|
|
3510
|
+
return "0.1.11";
|
|
2239
3511
|
} catch {}
|
|
2240
3512
|
return "0.0.0-dev";
|
|
2241
3513
|
}
|
|
@@ -2692,7 +3964,7 @@ const workspaceCommand = defineCommand({
|
|
|
2692
3964
|
},
|
|
2693
3965
|
async run({ args }) {
|
|
2694
3966
|
const raw = String(args.id);
|
|
2695
|
-
const parsed = parsePositiveInt(raw);
|
|
3967
|
+
const parsed = parsePositiveInt$1(raw);
|
|
2696
3968
|
if (parsed === null) {
|
|
2697
3969
|
const message = `workspace id must be a positive integer (got ${raw})`;
|
|
2698
3970
|
if (args.json) emitJson({
|
|
@@ -2760,7 +4032,7 @@ const workspaceCommand = defineCommand({
|
|
|
2760
4032
|
let workspaceId;
|
|
2761
4033
|
if (args.workspace) {
|
|
2762
4034
|
const raw = String(args.workspace);
|
|
2763
|
-
const parsed = parsePositiveInt(raw);
|
|
4035
|
+
const parsed = parsePositiveInt$1(raw);
|
|
2764
4036
|
if (parsed === null) {
|
|
2765
4037
|
const message = `--workspace must be a positive integer (got ${raw})`;
|
|
2766
4038
|
if (args.json) emitJson({
|
|
@@ -2818,7 +4090,8 @@ runMain(defineCommand({
|
|
|
2818
4090
|
workspace: workspaceCommand,
|
|
2819
4091
|
app: appCommand,
|
|
2820
4092
|
members: membersCommand,
|
|
2821
|
-
keys: keysCommand
|
|
4093
|
+
keys: keysCommand,
|
|
4094
|
+
notices: noticesCommand
|
|
2822
4095
|
}
|
|
2823
4096
|
}));
|
|
2824
4097
|
//#endregion
|