@ait-co/console-cli 0.1.11 → 0.1.12

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 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$3 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
164
+ const BASE$4 = "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$3}/workspaces/${workspaceId}/mini-app`,
167
+ url: `${BASE$4}/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$3}/workspaces/${workspaceId}/mini-apps/review-status`,
190
+ url: `${BASE$4}/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$3}/workspaces/${workspaceId}/mini-app/${miniAppId}/with-draft`,
209
+ url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/with-draft`,
210
210
  cookies,
211
211
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
212
212
  });
@@ -228,7 +228,7 @@ async function fetchMiniAppRatings(params, cookies, opts = {}) {
228
228
  const sortField = params.sortField ?? "CREATED_AT";
229
229
  const sortDirection = params.sortDirection ?? "DESC";
230
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}`,
231
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/app-ratings?page=${page}&size=${size}&sortField=${sortField}&sortDirection=${sortDirection}`,
232
232
  cookies,
233
233
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
234
234
  });
@@ -261,7 +261,7 @@ async function fetchUserReports(params, cookies, opts = {}) {
261
261
  qs.set("pageSize", String(pageSize));
262
262
  if (params.cursor !== void 0) qs.set("cursor", params.cursor);
263
263
  const raw = await requestConsoleApi({
264
- url: `${BASE$3}/workspaces/${params.workspaceId}/mini-apps/${params.miniAppId}/user-reports?` + qs.toString(),
264
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-apps/${params.miniAppId}/user-reports?` + qs.toString(),
265
265
  cookies,
266
266
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
267
267
  });
@@ -285,7 +285,7 @@ async function fetchBundles(params, cookies, opts = {}) {
285
285
  if (params.deployStatus !== void 0) qs.set("deployStatus", params.deployStatus);
286
286
  const query = qs.toString();
287
287
  const raw = await requestConsoleApi({
288
- url: `${BASE$3}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles` + (query ? `?${query}` : ""),
288
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles` + (query ? `?${query}` : ""),
289
289
  cookies,
290
290
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
291
291
  });
@@ -309,7 +309,7 @@ async function fetchConversionMetrics(params, cookies, opts = {}) {
309
309
  qs.set("startDate", params.startDate);
310
310
  qs.set("endDate", params.endDate);
311
311
  const raw = await requestConsoleApi({
312
- url: `${BASE$3}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/conversion-metrics?${qs.toString()}`,
312
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/conversion-metrics?${qs.toString()}`,
313
313
  cookies,
314
314
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
315
315
  });
@@ -327,7 +327,7 @@ async function fetchConversionMetrics(params, cookies, opts = {}) {
327
327
  }
328
328
  async function fetchCerts(workspaceId, miniAppId, cookies, opts = {}) {
329
329
  const raw = await requestConsoleApi({
330
- url: `${BASE$3}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs`,
330
+ url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs`,
331
331
  cookies,
332
332
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
333
333
  });
@@ -341,7 +341,7 @@ async function fetchShareRewards(params, cookies, opts = {}) {
341
341
  const qs = new URLSearchParams();
342
342
  qs.set("search", params.search ?? "");
343
343
  const raw = await requestConsoleApi({
344
- url: `${BASE$3}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/share-rewards?${qs.toString()}`,
344
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/share-rewards?${qs.toString()}`,
345
345
  cookies,
346
346
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
347
347
  });
@@ -351,9 +351,164 @@ async function fetchShareRewards(params, cookies, opts = {}) {
351
351
  return r;
352
352
  });
353
353
  }
354
+ async function fetchSmartMessageCampaigns(params, cookies, opts = {}) {
355
+ const page = params.page ?? 0;
356
+ const size = params.size ?? 20;
357
+ const qs = new URLSearchParams();
358
+ qs.set("page", String(page));
359
+ qs.set("size", String(size));
360
+ const raw = await requestConsoleApi({
361
+ method: "POST",
362
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/smart-message/campaigns?${qs.toString()}`,
363
+ body: {
364
+ sort: params.sort ?? [{
365
+ field: "regTs",
366
+ direction: "DESC"
367
+ }],
368
+ search: params.search ?? "",
369
+ filters: params.filters ?? {}
370
+ },
371
+ cookies,
372
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
373
+ });
374
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected smart-message campaigns shape for app=${params.miniAppId}`);
375
+ const data = raw;
376
+ const items = Array.isArray(data.items) ? data.items : [];
377
+ const rawPaging = data.paging;
378
+ const paging = {
379
+ pageNumber: typeof rawPaging?.pageNumber === "number" ? rawPaging.pageNumber : page,
380
+ pageSize: typeof rawPaging?.pageSize === "number" ? rawPaging.pageSize : size,
381
+ hasNext: Boolean(rawPaging?.hasNext),
382
+ totalCount: typeof rawPaging?.totalCount === "number" ? rawPaging.totalCount : items.length
383
+ };
384
+ return {
385
+ items: items.map((r) => r && typeof r === "object" ? r : {}),
386
+ paging
387
+ };
388
+ }
389
+ async function fetchAppEventCatalogs(params, cookies, opts = {}) {
390
+ const pageNumber = params.pageNumber ?? 0;
391
+ const pageSize = params.pageSize ?? 20;
392
+ const raw = await requestConsoleApi({
393
+ method: "POST",
394
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/log/catalogs/search`,
395
+ body: {
396
+ isRefresh: params.refresh ?? false,
397
+ pageNumber,
398
+ pageSize,
399
+ search: params.search ?? ""
400
+ },
401
+ cookies,
402
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
403
+ });
404
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected event-catalogs shape for app=${params.miniAppId}`);
405
+ const data = raw;
406
+ const results = Array.isArray(data.results) ? data.results : [];
407
+ const rawPaging = data.paging;
408
+ const paging = {
409
+ pageNumber: typeof rawPaging?.pageNumber === "number" ? rawPaging.pageNumber : pageNumber,
410
+ pageSize: typeof rawPaging?.pageSize === "number" ? rawPaging.pageSize : pageSize,
411
+ hasNext: Boolean(rawPaging?.hasNext),
412
+ totalCount: typeof rawPaging?.totalCount === "number" ? rawPaging.totalCount : results.length,
413
+ totalPages: typeof rawPaging?.totalPages === "number" ? rawPaging.totalPages : 0
414
+ };
415
+ return {
416
+ results: results.map((r) => r && typeof r === "object" ? r : {}),
417
+ cacheTime: typeof data.cacheTime === "string" ? data.cacheTime : void 0,
418
+ paging
419
+ };
420
+ }
421
+ const TEMPLATE_CONTENT_REACH_TYPES = ["FUNCTIONAL", "MARKETING"];
422
+ async function fetchAppTemplates(params, cookies, opts = {}) {
423
+ const page = params.page ?? 0;
424
+ const size = params.size ?? 20;
425
+ const qs = new URLSearchParams();
426
+ qs.set("page", String(page));
427
+ qs.set("size", String(size));
428
+ if (params.contentReachType) qs.set("contentReachType", params.contentReachType);
429
+ if (params.isSmartMessage !== void 0) qs.set("isSmartMessage", String(params.isSmartMessage));
430
+ const raw = await requestConsoleApi({
431
+ url: `${BASE$4}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/templates/search?${qs.toString()}`,
432
+ cookies,
433
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
434
+ });
435
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected templates shape for app=${params.miniAppId}`);
436
+ const data = raw;
437
+ const list = Array.isArray(data.groupSendContextSimpleView) ? data.groupSendContextSimpleView : [];
438
+ const pageMeta = data.page;
439
+ return {
440
+ templates: list.map((r) => r && typeof r === "object" ? r : {}),
441
+ totalPageCount: typeof pageMeta?.totalPageCount === "number" ? pageMeta.totalPageCount : 0
442
+ };
443
+ }
444
+ async function fetchImpressionCategoryList(cookies, opts = {}) {
445
+ const raw = await requestConsoleApi({
446
+ url: `${BASE$4}/impression/category-list`,
447
+ cookies,
448
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
449
+ });
450
+ if (!Array.isArray(raw)) throw new Error("Unexpected impression/category-list shape: not an array");
451
+ return raw.map((entry, i) => {
452
+ if (!entry || typeof entry !== "object") throw new Error(`Unexpected category-list entry at index ${i}`);
453
+ const e = entry;
454
+ const group = e.categoryGroup;
455
+ const list = Array.isArray(e.categoryList) ? e.categoryList : [];
456
+ return {
457
+ categoryGroup: {
458
+ id: typeof group?.id === "number" ? group.id : 0,
459
+ name: typeof group?.name === "string" ? group.name : "",
460
+ isSelectable: Boolean(group?.isSelectable)
461
+ },
462
+ categoryList: list.map((c) => {
463
+ if (!c || typeof c !== "object") return {
464
+ id: 0,
465
+ name: "",
466
+ isSelectable: false,
467
+ subCategoryList: []
468
+ };
469
+ const cr = c;
470
+ const subs = Array.isArray(cr.subCategoryList) ? cr.subCategoryList : [];
471
+ return {
472
+ id: typeof cr.id === "number" ? cr.id : 0,
473
+ name: typeof cr.name === "string" ? cr.name : "",
474
+ isSelectable: Boolean(cr.isSelectable),
475
+ subCategoryList: subs.map((s) => {
476
+ if (!s || typeof s !== "object") return {
477
+ id: 0,
478
+ name: "",
479
+ isSelectable: false
480
+ };
481
+ const sr = s;
482
+ return {
483
+ id: typeof sr.id === "number" ? sr.id : 0,
484
+ name: typeof sr.name === "string" ? sr.name : "",
485
+ isSelectable: Boolean(sr.isSelectable)
486
+ };
487
+ })
488
+ };
489
+ })
490
+ };
491
+ });
492
+ }
493
+ async function fetchAppServiceStatus(workspaceId, miniAppId, cookies, opts = {}) {
494
+ const raw = await requestConsoleApi({
495
+ url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/review-status`,
496
+ cookies,
497
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
498
+ });
499
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected app service-status shape for app=${miniAppId}`);
500
+ const data = raw;
501
+ const serviceStatus = data.serviceStatus;
502
+ if (typeof serviceStatus !== "string") throw new Error(`Unexpected app service-status shape for app=${miniAppId}: missing serviceStatus`);
503
+ return {
504
+ shutdownCandidateStatus: typeof data.shutdownCandidateStatus === "string" ? data.shutdownCandidateStatus : null,
505
+ scheduledShutdownAt: typeof data.scheduledShutdownAt === "string" ? data.scheduledShutdownAt : null,
506
+ serviceStatus
507
+ };
508
+ }
354
509
  async function fetchDeployedBundle(workspaceId, miniAppId, cookies, opts = {}) {
355
510
  const raw = await requestConsoleApi({
356
- url: `${BASE$3}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/deployed`,
511
+ url: `${BASE$4}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/deployed`,
357
512
  cookies,
358
513
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
359
514
  });
@@ -363,7 +518,7 @@ async function fetchDeployedBundle(workspaceId, miniAppId, cookies, opts = {}) {
363
518
  }
364
519
  async function createMiniApp(workspaceId, payload, cookies, opts = {}) {
365
520
  return normalizeCreateResult(await requestConsoleApi({
366
- url: `${BASE$3}/workspaces/${workspaceId}/mini-app/review`,
521
+ url: `${BASE$4}/workspaces/${workspaceId}/mini-app/review`,
367
522
  method: "POST",
368
523
  cookies,
369
524
  body: payload,
@@ -396,7 +551,7 @@ function normalizeCreateResult(raw) {
396
551
  * the field name is actually `file` — if so, swap it in one place here.
397
552
  */
398
553
  async function uploadMiniAppResource(params, opts = {}) {
399
- const url = new URL(`${BASE$3}/resource/${params.workspaceId}/upload`);
554
+ const url = new URL(`${BASE$4}/resource/${params.workspaceId}/upload`);
400
555
  url.searchParams.set("validWidth", String(params.validWidth));
401
556
  url.searchParams.set("validHeight", String(params.validHeight));
402
557
  const form = new FormData();
@@ -2265,73 +2420,559 @@ const appCommand = defineCommand({
2265
2420
  }
2266
2421
  }) }
2267
2422
  }),
2268
- register: defineCommand({
2423
+ messages: defineCommand({
2269
2424
  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."
2425
+ name: "messages",
2426
+ description: "Inspect smart-message (formerly push) campaigns for a mini-app."
2272
2427
  },
2273
- args: {
2274
- workspace: {
2275
- type: "string",
2276
- description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
2428
+ subCommands: { ls: defineCommand({
2429
+ meta: {
2430
+ name: "ls",
2431
+ description: "List smart-message campaigns (formerly \"push\" the 스마트 발송 menu)."
2277
2432
  },
2278
- config: {
2279
- type: "string",
2280
- description: "Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.json`."
2433
+ args: {
2434
+ id: {
2435
+ type: "positional",
2436
+ description: "Mini-app ID.",
2437
+ required: true
2438
+ },
2439
+ workspace: {
2440
+ type: "string",
2441
+ description: "Workspace ID. Defaults to the selected workspace."
2442
+ },
2443
+ page: {
2444
+ type: "string",
2445
+ description: "Page number (0-indexed).",
2446
+ default: "0"
2447
+ },
2448
+ size: {
2449
+ type: "string",
2450
+ description: "Page size.",
2451
+ default: "20"
2452
+ },
2453
+ search: {
2454
+ type: "string",
2455
+ description: "Title-contains filter. Empty matches everything."
2456
+ },
2457
+ json: {
2458
+ type: "boolean",
2459
+ description: "Emit machine-readable JSON.",
2460
+ default: false
2461
+ }
2281
2462
  },
2282
- "dry-run": {
2283
- type: "boolean",
2284
- description: "Validate manifest + images and print the inferred submit payload; no uploads.",
2285
- default: false
2463
+ async run({ args }) {
2464
+ const appId = parseAppId(args.id);
2465
+ if (appId === null) {
2466
+ if (args.json) emitJson({
2467
+ ok: false,
2468
+ reason: "invalid-id",
2469
+ message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
2470
+ });
2471
+ else process.stderr.write(`app messages ls: invalid id ${JSON.stringify(args.id)}\n`);
2472
+ return exitAfterFlush(ExitCode.Usage);
2473
+ }
2474
+ const pageResult = parseNonNegativeInt(String(args.page), "page");
2475
+ if ("error" in pageResult) {
2476
+ if (args.json) emitJson({
2477
+ ok: false,
2478
+ reason: "invalid-page",
2479
+ message: pageResult.error
2480
+ });
2481
+ else process.stderr.write(`${pageResult.error}\n`);
2482
+ return exitAfterFlush(ExitCode.Usage);
2483
+ }
2484
+ const sizeResult = parseNonNegativeInt(String(args.size), "size");
2485
+ if ("error" in sizeResult) {
2486
+ if (args.json) emitJson({
2487
+ ok: false,
2488
+ reason: "invalid-size",
2489
+ message: sizeResult.error
2490
+ });
2491
+ else process.stderr.write(`${sizeResult.error}\n`);
2492
+ return exitAfterFlush(ExitCode.Usage);
2493
+ }
2494
+ const ctx = await resolveWorkspaceContext(args);
2495
+ if (!ctx) return;
2496
+ const { session, workspaceId } = ctx;
2497
+ try {
2498
+ const result = await fetchSmartMessageCampaigns({
2499
+ workspaceId,
2500
+ miniAppId: appId,
2501
+ page: pageResult.value,
2502
+ size: sizeResult.value,
2503
+ ...args.search !== void 0 ? { search: String(args.search) } : {}
2504
+ }, session.cookies);
2505
+ if (args.json) {
2506
+ emitJson({
2507
+ ok: true,
2508
+ workspaceId,
2509
+ appId,
2510
+ campaigns: result.items,
2511
+ paging: result.paging
2512
+ });
2513
+ return exitAfterFlush(ExitCode.Ok);
2514
+ }
2515
+ if (result.items.length === 0) {
2516
+ process.stdout.write(`App ${appId} (ws ${workspaceId}): no smart-message campaigns\n`);
2517
+ return exitAfterFlush(ExitCode.Ok);
2518
+ }
2519
+ process.stdout.write(`App ${appId} (ws ${workspaceId}): ${result.items.length} campaign(s) on page ${result.paging.pageNumber} of ${result.paging.totalCount}\n`);
2520
+ for (const c of result.items) {
2521
+ const id = typeof c.id === "string" || typeof c.id === "number" ? c.id : typeof c.campaignId === "string" || typeof c.campaignId === "number" ? c.campaignId : "-";
2522
+ const title = typeof c.title === "string" ? c.title : typeof c.name === "string" ? c.name : "-";
2523
+ const status = typeof c.status === "string" ? c.status : "-";
2524
+ process.stdout.write(`${id}\t${title}\t${status}\n`);
2525
+ }
2526
+ return exitAfterFlush(ExitCode.Ok);
2527
+ } catch (err) {
2528
+ return emitFailureFromError(args.json, err);
2529
+ }
2530
+ }
2531
+ }) }
2532
+ }),
2533
+ events: defineCommand({
2534
+ meta: {
2535
+ name: "events",
2536
+ description: "Inspect custom event catalogs (log search) for a mini-app."
2537
+ },
2538
+ subCommands: { ls: defineCommand({
2539
+ meta: {
2540
+ name: "ls",
2541
+ description: "List custom event catalogs recorded for a mini-app (the 이벤트 menu)."
2286
2542
  },
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
2543
+ args: {
2544
+ id: {
2545
+ type: "positional",
2546
+ description: "Mini-app ID.",
2547
+ required: true
2548
+ },
2549
+ workspace: {
2550
+ type: "string",
2551
+ description: "Workspace ID. Defaults to the selected workspace."
2552
+ },
2553
+ page: {
2554
+ type: "string",
2555
+ description: "Page number (0-indexed).",
2556
+ default: "0"
2557
+ },
2558
+ size: {
2559
+ type: "string",
2560
+ description: "Page size.",
2561
+ default: "20"
2562
+ },
2563
+ search: {
2564
+ type: "string",
2565
+ description: "Event-name filter. Empty matches everything."
2566
+ },
2567
+ refresh: {
2568
+ type: "boolean",
2569
+ description: "Bypass the server cache and rebuild the catalog list.",
2570
+ default: false
2571
+ },
2572
+ json: {
2573
+ type: "boolean",
2574
+ description: "Emit machine-readable JSON.",
2575
+ default: false
2576
+ }
2291
2577
  },
2292
- json: {
2293
- type: "boolean",
2294
- description: "Emit machine-readable JSON to stdout.",
2295
- default: false
2578
+ async run({ args }) {
2579
+ const appId = parseAppId(args.id);
2580
+ if (appId === null) {
2581
+ if (args.json) emitJson({
2582
+ ok: false,
2583
+ reason: "invalid-id",
2584
+ message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
2585
+ });
2586
+ else process.stderr.write(`app events ls: invalid id ${JSON.stringify(args.id)}\n`);
2587
+ return exitAfterFlush(ExitCode.Usage);
2588
+ }
2589
+ const pageResult = parseNonNegativeInt(String(args.page), "page");
2590
+ if ("error" in pageResult) {
2591
+ if (args.json) emitJson({
2592
+ ok: false,
2593
+ reason: "invalid-page",
2594
+ message: pageResult.error
2595
+ });
2596
+ else process.stderr.write(`${pageResult.error}\n`);
2597
+ return exitAfterFlush(ExitCode.Usage);
2598
+ }
2599
+ const sizeResult = parseNonNegativeInt(String(args.size), "size");
2600
+ if ("error" in sizeResult) {
2601
+ if (args.json) emitJson({
2602
+ ok: false,
2603
+ reason: "invalid-size",
2604
+ message: sizeResult.error
2605
+ });
2606
+ else process.stderr.write(`${sizeResult.error}\n`);
2607
+ return exitAfterFlush(ExitCode.Usage);
2608
+ }
2609
+ const ctx = await resolveWorkspaceContext(args);
2610
+ if (!ctx) return;
2611
+ const { session, workspaceId } = ctx;
2612
+ try {
2613
+ const result = await fetchAppEventCatalogs({
2614
+ workspaceId,
2615
+ miniAppId: appId,
2616
+ pageNumber: pageResult.value,
2617
+ pageSize: sizeResult.value,
2618
+ ...args.search !== void 0 ? { search: String(args.search) } : {},
2619
+ ...args.refresh ? { refresh: true } : {}
2620
+ }, session.cookies);
2621
+ if (args.json) {
2622
+ emitJson({
2623
+ ok: true,
2624
+ workspaceId,
2625
+ appId,
2626
+ events: result.results,
2627
+ cacheTime: result.cacheTime ?? null,
2628
+ paging: result.paging
2629
+ });
2630
+ return exitAfterFlush(ExitCode.Ok);
2631
+ }
2632
+ if (result.results.length === 0) {
2633
+ const ct = result.cacheTime ? ` (cached ${result.cacheTime})` : "";
2634
+ process.stdout.write(`App ${appId} (ws ${workspaceId}): no event catalogs${ct}\n`);
2635
+ return exitAfterFlush(ExitCode.Ok);
2636
+ }
2637
+ process.stdout.write(`App ${appId} (ws ${workspaceId}): ${result.results.length} event(s) on page ${result.paging.pageNumber} of ${result.paging.totalPages}\n`);
2638
+ for (const e of result.results) {
2639
+ const name = typeof e.name === "string" ? e.name : typeof e.eventName === "string" ? e.eventName : "-";
2640
+ const count = typeof e.count === "number" ? String(e.count) : typeof e.totalCount === "number" ? String(e.totalCount) : "-";
2641
+ process.stdout.write(`${name}\t${count}\n`);
2642
+ }
2643
+ return exitAfterFlush(ExitCode.Ok);
2644
+ } catch (err) {
2645
+ return emitFailureFromError(args.json, err);
2646
+ }
2296
2647
  }
2648
+ }) }
2649
+ }),
2650
+ templates: defineCommand({
2651
+ meta: {
2652
+ name: "templates",
2653
+ description: "Inspect smart-message composer templates available for a mini-app."
2297
2654
  },
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
- };
2655
+ subCommands: { ls: defineCommand({
2656
+ meta: {
2657
+ name: "ls",
2658
+ description: "List the smart-message composer templates available for a mini-app (the 템플릿 picker in 스마트 발송)."
2659
+ },
2660
+ args: {
2661
+ id: {
2662
+ type: "positional",
2663
+ description: "Mini-app ID.",
2664
+ required: true
2665
+ },
2666
+ workspace: {
2667
+ type: "string",
2668
+ description: "Workspace ID. Defaults to the selected workspace."
2669
+ },
2670
+ page: {
2671
+ type: "string",
2672
+ description: "Page number (0-indexed).",
2673
+ default: "0"
2674
+ },
2675
+ size: {
2676
+ type: "string",
2677
+ description: "Page size.",
2678
+ default: "20"
2679
+ },
2680
+ "content-reach-type": {
2681
+ type: "string",
2682
+ description: `Template reach bucket: ${TEMPLATE_CONTENT_REACH_TYPES.join(" | ")}. Omit for all.`
2683
+ },
2684
+ "smart-message": {
2685
+ type: "string",
2686
+ description: "Filter to templates compatible with smart-message (\"true\") or legacy push (\"false\"). Omit for all."
2687
+ },
2688
+ json: {
2689
+ type: "boolean",
2690
+ description: "Emit machine-readable JSON.",
2691
+ default: false
2692
+ }
2693
+ },
2694
+ async run({ args }) {
2695
+ const appId = parseAppId(args.id);
2696
+ if (appId === null) {
2697
+ if (args.json) emitJson({
2698
+ ok: false,
2699
+ reason: "invalid-id",
2700
+ message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
2701
+ });
2702
+ else process.stderr.write(`app templates ls: invalid id ${JSON.stringify(args.id)}\n`);
2703
+ return exitAfterFlush(ExitCode.Usage);
2704
+ }
2705
+ const pageResult = parseNonNegativeInt(String(args.page), "page");
2706
+ if ("error" in pageResult) {
2707
+ if (args.json) emitJson({
2708
+ ok: false,
2709
+ reason: "invalid-page",
2710
+ message: pageResult.error
2711
+ });
2712
+ else process.stderr.write(`${pageResult.error}\n`);
2713
+ return exitAfterFlush(ExitCode.Usage);
2714
+ }
2715
+ const sizeResult = parseNonNegativeInt(String(args.size), "size");
2716
+ if ("error" in sizeResult) {
2717
+ if (args.json) emitJson({
2718
+ ok: false,
2719
+ reason: "invalid-size",
2720
+ message: sizeResult.error
2721
+ });
2722
+ else process.stderr.write(`${sizeResult.error}\n`);
2723
+ return exitAfterFlush(ExitCode.Usage);
2724
+ }
2725
+ let contentReachType;
2726
+ if (args["content-reach-type"] !== void 0) {
2727
+ const upper = String(args["content-reach-type"]).toUpperCase();
2728
+ if (TEMPLATE_CONTENT_REACH_TYPES.includes(upper)) contentReachType = upper;
2729
+ else {
2730
+ const message = `--content-reach-type must be one of: ${TEMPLATE_CONTENT_REACH_TYPES.join(", ")}`;
2731
+ if (args.json) emitJson({
2732
+ ok: false,
2733
+ reason: "invalid-content-reach-type",
2734
+ allowed: [...TEMPLATE_CONTENT_REACH_TYPES]
2735
+ });
2736
+ else process.stderr.write(`${message}\n`);
2737
+ return exitAfterFlush(ExitCode.Usage);
2738
+ }
2739
+ }
2740
+ let isSmartMessage;
2741
+ if (args["smart-message"] !== void 0) {
2742
+ const raw = String(args["smart-message"]).toLowerCase();
2743
+ if (raw === "true") isSmartMessage = true;
2744
+ else if (raw === "false") isSmartMessage = false;
2745
+ else {
2746
+ const message = "--smart-message must be \"true\" or \"false\"";
2747
+ if (args.json) emitJson({
2748
+ ok: false,
2749
+ reason: "invalid-smart-message",
2750
+ message
2751
+ });
2752
+ else process.stderr.write(`${message}\n`);
2753
+ return exitAfterFlush(ExitCode.Usage);
2754
+ }
2755
+ }
2756
+ const ctx = await resolveWorkspaceContext(args);
2757
+ if (!ctx) return;
2758
+ const { session, workspaceId } = ctx;
2759
+ try {
2760
+ const result = await fetchAppTemplates({
2761
+ workspaceId,
2762
+ miniAppId: appId,
2763
+ page: pageResult.value,
2764
+ size: sizeResult.value,
2765
+ ...contentReachType !== void 0 ? { contentReachType } : {},
2766
+ ...isSmartMessage !== void 0 ? { isSmartMessage } : {}
2767
+ }, session.cookies);
2768
+ if (args.json) {
2769
+ emitJson({
2770
+ ok: true,
2771
+ workspaceId,
2772
+ appId,
2773
+ templates: result.templates,
2774
+ totalPageCount: result.totalPageCount
2775
+ });
2776
+ return exitAfterFlush(ExitCode.Ok);
2777
+ }
2778
+ if (result.templates.length === 0) {
2779
+ process.stdout.write(`App ${appId} (ws ${workspaceId}): no templates\n`);
2780
+ return exitAfterFlush(ExitCode.Ok);
2781
+ }
2782
+ process.stdout.write(`App ${appId} (ws ${workspaceId}): ${result.templates.length} template(s) of ${result.totalPageCount} page(s)\n`);
2783
+ for (const t of result.templates) {
2784
+ const id = typeof t.id === "string" || typeof t.id === "number" ? t.id : typeof t.templateId === "string" || typeof t.templateId === "number" ? t.templateId : "-";
2785
+ const title = typeof t.title === "string" ? t.title : typeof t.name === "string" ? t.name : "-";
2786
+ const type = typeof t.templateType === "string" ? t.templateType : "-";
2787
+ process.stdout.write(`${id}\t${title}\t${type}\n`);
2788
+ }
2789
+ return exitAfterFlush(ExitCode.Ok);
2790
+ } catch (err) {
2791
+ return emitFailureFromError(args.json, err);
2792
+ }
2793
+ }
2794
+ }) }
2795
+ }),
2796
+ categories: defineCommand({
2797
+ meta: {
2798
+ name: "categories",
2799
+ description: "List the impression category tree used by `app register`'s `categoryIds` field."
2800
+ },
2801
+ args: {
2802
+ selectable: {
2803
+ type: "boolean",
2804
+ description: "Only show categories flagged `isSelectable: true` — the ones you can pick.",
2805
+ default: false
2806
+ },
2807
+ json: {
2808
+ type: "boolean",
2809
+ description: "Emit machine-readable JSON to stdout.",
2810
+ default: false
2811
+ }
2812
+ },
2813
+ async run({ args }) {
2814
+ const session = await readSession();
2815
+ if (!session) {
2816
+ emitNotAuthenticated(args.json);
2817
+ return exitAfterFlush(ExitCode.NotAuthenticated);
2818
+ }
2819
+ try {
2820
+ const tree = await fetchImpressionCategoryList(session.cookies);
2821
+ const filtered = args.selectable ? tree.filter((g) => g.categoryGroup.isSelectable).map((g) => ({
2822
+ ...g,
2823
+ categoryList: g.categoryList.filter((c) => c.isSelectable).map((c) => ({
2824
+ ...c,
2825
+ subCategoryList: c.subCategoryList.filter((s) => s.isSelectable)
2826
+ }))
2827
+ })) : tree;
2828
+ if (args.json) {
2829
+ emitJson({
2830
+ ok: true,
2831
+ categories: filtered
2832
+ });
2833
+ return exitAfterFlush(ExitCode.Ok);
2834
+ }
2835
+ for (const g of filtered) {
2836
+ const mark = g.categoryGroup.isSelectable ? "" : " (not selectable)";
2837
+ process.stdout.write(`[${g.categoryGroup.id}] ${g.categoryGroup.name}${mark}\n`);
2838
+ for (const c of g.categoryList) {
2839
+ const cmark = c.isSelectable ? "" : " (not selectable)";
2840
+ process.stdout.write(` ${c.id}\t${c.name}${cmark}\n`);
2841
+ for (const s of c.subCategoryList) {
2842
+ const smark = s.isSelectable ? "" : " (not selectable)";
2843
+ process.stdout.write(` ${s.id}\t${s.name}${smark}\n`);
2844
+ }
2845
+ }
2846
+ }
2847
+ return exitAfterFlush(ExitCode.Ok);
2848
+ } catch (err) {
2849
+ return emitFailureFromError(args.json, err);
2850
+ }
2851
+ }
2852
+ }),
2853
+ "service-status": defineCommand({
2854
+ meta: {
2855
+ name: "service-status",
2856
+ description: "Show the server-authoritative runtime status of a mini-app (serviceStatus, shutdown schedule)."
2857
+ },
2858
+ args: {
2859
+ id: {
2860
+ type: "positional",
2861
+ description: "Mini-app ID.",
2862
+ required: true
2863
+ },
2864
+ workspace: {
2865
+ type: "string",
2866
+ description: "Workspace ID. Defaults to the selected workspace."
2867
+ },
2868
+ json: {
2869
+ type: "boolean",
2870
+ description: "Emit machine-readable JSON.",
2871
+ default: false
2872
+ }
2873
+ },
2874
+ async run({ args }) {
2875
+ const appId = parseAppId(args.id);
2876
+ if (appId === null) {
2877
+ if (args.json) emitJson({
2878
+ ok: false,
2879
+ reason: "invalid-id",
2880
+ message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`
2881
+ });
2882
+ else process.stderr.write(`app service-status: invalid id ${JSON.stringify(args.id)}\n`);
2883
+ return exitAfterFlush(ExitCode.Usage);
2884
+ }
2885
+ const ctx = await resolveWorkspaceContext(args);
2886
+ if (!ctx) return;
2887
+ const { session, workspaceId } = ctx;
2888
+ try {
2889
+ const st = await fetchAppServiceStatus(workspaceId, appId, session.cookies);
2890
+ if (args.json) {
2891
+ emitJson({
2892
+ ok: true,
2893
+ workspaceId,
2894
+ appId,
2895
+ ...st
2896
+ });
2897
+ return exitAfterFlush(ExitCode.Ok);
2898
+ }
2899
+ process.stdout.write(`App ${appId} (ws ${workspaceId}):\n`);
2900
+ process.stdout.write(` serviceStatus: ${st.serviceStatus}\n`);
2901
+ process.stdout.write(` shutdownCandidateStatus: ${st.shutdownCandidateStatus ?? "null"}\n`);
2902
+ process.stdout.write(` scheduledShutdownAt: ${st.scheduledShutdownAt ?? "null"}\n`);
2903
+ return exitAfterFlush(ExitCode.Ok);
2904
+ } catch (err) {
2905
+ return emitFailureFromError(args.json, err);
2906
+ }
2907
+ }
2908
+ }),
2909
+ register: defineCommand({
2910
+ meta: {
2911
+ name: "register",
2912
+ description: "Register a mini-app in the selected workspace from a YAML/JSON manifest. Uploads logo/thumbnail/screenshots, then submits the create payload."
2913
+ },
2914
+ args: {
2915
+ workspace: {
2916
+ type: "string",
2917
+ description: "Workspace ID. Defaults to the selected workspace (`aitcc workspace use`)."
2918
+ },
2919
+ config: {
2920
+ type: "string",
2921
+ description: "Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.json`."
2922
+ },
2923
+ "dry-run": {
2924
+ type: "boolean",
2925
+ description: "Validate manifest + images and print the inferred submit payload; no uploads.",
2926
+ default: false
2927
+ },
2928
+ "accept-terms": {
2929
+ type: "boolean",
2930
+ description: "Attest to the required console legal-agreement checkboxes (see VALIDATION-RULES.md). Required for real submits.",
2931
+ default: false
2932
+ },
2933
+ json: {
2934
+ type: "boolean",
2935
+ description: "Emit machine-readable JSON to stdout.",
2936
+ default: false
2937
+ }
2938
+ },
2939
+ async run({ args }) {
2940
+ await runRegister({
2941
+ json: args.json,
2942
+ dryRun: args["dry-run"],
2943
+ acceptTerms: args["accept-terms"],
2944
+ ...args.workspace !== void 0 ? { workspace: args.workspace } : {},
2945
+ ...args.config !== void 0 ? { config: args.config } : {}
2946
+ });
2947
+ }
2948
+ })
2949
+ }
2950
+ });
2951
+ //#endregion
2952
+ //#region src/api/api-keys.ts
2953
+ const BASE$3 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
2954
+ async function fetchApiKeys(workspaceId, cookies, opts = {}) {
2955
+ const raw = await requestConsoleApi({
2956
+ url: `${BASE$3}/workspaces/${workspaceId}/api-keys`,
2957
+ cookies,
2958
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
2959
+ });
2960
+ if (!Array.isArray(raw)) throw new Error(`Unexpected api-keys shape for workspace=${workspaceId}: not an array`);
2961
+ return raw.map((entry, index) => normalizeKey(entry, workspaceId, index));
2962
+ }
2963
+ function normalizeKey(raw, workspaceId, index) {
2964
+ if (raw === null || typeof raw !== "object") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: not an object`);
2965
+ const rec = raw;
2966
+ const rawId = rec.id ?? rec.apiKeyId ?? rec.keyId;
2967
+ if (typeof rawId !== "string" && typeof rawId !== "number") throw new Error(`Unexpected api-key entry at index ${index} for workspace=${workspaceId}: missing id`);
2968
+ const rawName = rec.name ?? rec.apiKeyName ?? rec.keyName ?? rec.description;
2969
+ const name = typeof rawName === "string" ? rawName : void 0;
2970
+ const { id: _id, apiKeyId: _aid, keyId: _kid, name: _n, apiKeyName: _an, keyName: _kn, description: _d, ...extra } = rec;
2971
+ return {
2972
+ id: rawId,
2973
+ name,
2974
+ extra
2975
+ };
2335
2976
  }
2336
2977
  const keysCommand = defineCommand({
2337
2978
  meta: {
@@ -2392,7 +3033,8 @@ const keysCommand = defineCommand({
2392
3033
  });
2393
3034
  //#endregion
2394
3035
  //#region src/api/me.ts
2395
- const MEMBER_USER_INFO_URL = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole/members/me/user-info";
3036
+ const BASE$2 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
3037
+ const MEMBER_USER_INFO_URL = `${BASE$2}/members/me/user-info`;
2396
3038
  async function fetchConsoleMemberUserInfo(cookies, opts = {}) {
2397
3039
  return requestConsoleApi({
2398
3040
  url: MEMBER_USER_INFO_URL,
@@ -2400,6 +3042,28 @@ async function fetchConsoleMemberUserInfo(cookies, opts = {}) {
2400
3042
  ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
2401
3043
  });
2402
3044
  }
3045
+ async function fetchUserTerms(cookies, opts = {}) {
3046
+ const raw = await requestConsoleApi({
3047
+ url: `${BASE$2}/console-user-terms/me`,
3048
+ cookies,
3049
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
3050
+ });
3051
+ if (!Array.isArray(raw)) throw new Error("Unexpected user-terms shape: not an array");
3052
+ return raw.map((entry, i) => {
3053
+ if (!entry || typeof entry !== "object") throw new Error(`Unexpected user-terms entry at index ${i}`);
3054
+ const e = entry;
3055
+ return {
3056
+ required: Boolean(e.required),
3057
+ termsId: typeof e.termsId === "number" ? e.termsId : 0,
3058
+ revisionId: typeof e.revisionId === "number" ? e.revisionId : 0,
3059
+ title: typeof e.title === "string" ? e.title : "",
3060
+ contentsUrl: typeof e.contentsUrl === "string" ? e.contentsUrl : "",
3061
+ actionType: typeof e.actionType === "string" ? e.actionType : "",
3062
+ isAgreed: Boolean(e.isAgreed),
3063
+ isOneTimeConsent: Boolean(e.isOneTimeConsent)
3064
+ };
3065
+ });
3066
+ }
2403
3067
  //#endregion
2404
3068
  //#region src/cdp.ts
2405
3069
  function isResponse(m) {
@@ -3054,27 +3718,75 @@ const logoutCommand = defineCommand({
3054
3718
  }
3055
3719
  });
3056
3720
  //#endregion
3057
- //#region src/api/members.ts
3058
- const BASE$1 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
3059
- async function fetchWorkspaceMembers(workspaceId, cookies, opts = {}) {
3060
- const raw = await requestConsoleApi({
3061
- url: `${BASE$1}/workspaces/${workspaceId}/members`,
3062
- cookies,
3063
- ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
3064
- });
3065
- if (!Array.isArray(raw)) throw new Error(`Unexpected members shape for workspace=${workspaceId}: not an array`);
3066
- return raw.map((entry, index) => normalizeMember(entry, workspaceId, index));
3721
+ //#region src/commands/me.ts
3722
+ function formatTermLine(t) {
3723
+ return ` ${t.isAgreed ? "[agreed]" : "[pending]"}${t.required ? " required" : ""} ${t.title}\n ${t.contentsUrl}\n`;
3067
3724
  }
3068
- function normalizeMember(raw, workspaceId, index) {
3069
- if (raw === null || typeof raw !== "object") throw new Error(`Unexpected member entry at index ${index} for workspace=${workspaceId}: not an object`);
3070
- const rec = raw;
3071
- const stringField = (k) => {
3072
- const v = rec[k];
3073
- if (typeof v !== "string") throw new Error(`Unexpected member entry at index ${index} for workspace=${workspaceId}: missing ${k}`);
3074
- return v;
3075
- };
3076
- const numField = (k) => {
3077
- const v = rec[k];
3725
+ const meCommand = defineCommand({
3726
+ meta: {
3727
+ name: "me",
3728
+ description: "Inspect account-level settings for the signed-in user."
3729
+ },
3730
+ subCommands: { terms: defineCommand({
3731
+ meta: {
3732
+ name: "terms",
3733
+ description: "Show the console-level terms of agreement for the signed-in account."
3734
+ },
3735
+ args: { json: {
3736
+ type: "boolean",
3737
+ description: "Emit machine-readable JSON to stdout.",
3738
+ default: false
3739
+ } },
3740
+ async run({ args }) {
3741
+ const session = await readSession();
3742
+ if (!session) {
3743
+ emitNotAuthenticated(args.json);
3744
+ return exitAfterFlush(ExitCode.NotAuthenticated);
3745
+ }
3746
+ try {
3747
+ const terms = await fetchUserTerms(session.cookies);
3748
+ if (args.json) {
3749
+ emitJson({
3750
+ ok: true,
3751
+ terms
3752
+ });
3753
+ return exitAfterFlush(ExitCode.Ok);
3754
+ }
3755
+ if (terms.length === 0) {
3756
+ process.stdout.write("No console-level terms required.\n");
3757
+ return exitAfterFlush(ExitCode.Ok);
3758
+ }
3759
+ process.stdout.write("Console account terms:\n");
3760
+ for (const t of terms) process.stdout.write(formatTermLine(t));
3761
+ return exitAfterFlush(ExitCode.Ok);
3762
+ } catch (err) {
3763
+ return emitFailureFromError(args.json, err);
3764
+ }
3765
+ }
3766
+ }) }
3767
+ });
3768
+ //#endregion
3769
+ //#region src/api/members.ts
3770
+ const BASE$1 = "https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole";
3771
+ async function fetchWorkspaceMembers(workspaceId, cookies, opts = {}) {
3772
+ const raw = await requestConsoleApi({
3773
+ url: `${BASE$1}/workspaces/${workspaceId}/members`,
3774
+ cookies,
3775
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
3776
+ });
3777
+ if (!Array.isArray(raw)) throw new Error(`Unexpected members shape for workspace=${workspaceId}: not an array`);
3778
+ return raw.map((entry, index) => normalizeMember(entry, workspaceId, index));
3779
+ }
3780
+ function normalizeMember(raw, workspaceId, index) {
3781
+ if (raw === null || typeof raw !== "object") throw new Error(`Unexpected member entry at index ${index} for workspace=${workspaceId}: not an object`);
3782
+ const rec = raw;
3783
+ const stringField = (k) => {
3784
+ const v = rec[k];
3785
+ if (typeof v !== "string") throw new Error(`Unexpected member entry at index ${index} for workspace=${workspaceId}: missing ${k}`);
3786
+ return v;
3787
+ };
3788
+ const numField = (k) => {
3789
+ const v = rec[k];
3078
3790
  if (typeof v !== "number" || !Number.isFinite(v)) throw new Error(`Unexpected member entry at index ${index} for workspace=${workspaceId}: missing ${k}`);
3079
3791
  return v;
3080
3792
  };
@@ -3507,7 +4219,7 @@ function resolveVersion() {
3507
4219
  if (typeof injected === "string" && injected.length > 0) return injected;
3508
4220
  } catch {}
3509
4221
  try {
3510
- return "0.1.11";
4222
+ return "0.1.12";
3511
4223
  } catch {}
3512
4224
  return "0.0.0-dev";
3513
4225
  }
@@ -3886,6 +4598,70 @@ async function fetchWorkspaceDetail(workspaceId, cookies, opts = {}) {
3886
4598
  extra
3887
4599
  };
3888
4600
  }
4601
+ async function fetchWorkspacePartner(workspaceId, cookies, opts = {}) {
4602
+ const raw = await requestConsoleApi({
4603
+ url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/partner`,
4604
+ cookies,
4605
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
4606
+ });
4607
+ const registered = raw.registered;
4608
+ if (typeof registered !== "boolean") throw new Error(`Unexpected workspace partner shape for id=${workspaceId}`);
4609
+ return {
4610
+ registered,
4611
+ approvalType: typeof raw.approvalType === "string" ? raw.approvalType : null,
4612
+ rejectMessage: typeof raw.rejectMessage === "string" ? raw.rejectMessage : null,
4613
+ partner: raw.partner && typeof raw.partner === "object" ? raw.partner : null
4614
+ };
4615
+ }
4616
+ const WORKSPACE_TERM_TYPES = [
4617
+ "TOSS_LOGIN",
4618
+ "BIZ_WORKSPACE",
4619
+ "TOSS_PROMOTION_MONEY",
4620
+ "IAA",
4621
+ "IAP"
4622
+ ];
4623
+ const DEFAULT_SEGMENT_CATEGORY = "생성된 세그먼트";
4624
+ async function fetchWorkspaceSegments(params, cookies, opts = {}) {
4625
+ const page = params.page ?? 0;
4626
+ const qs = new URLSearchParams();
4627
+ qs.set("category", params.category ?? DEFAULT_SEGMENT_CATEGORY);
4628
+ qs.set("search", params.search ?? "");
4629
+ qs.set("page", String(page));
4630
+ const raw = await requestConsoleApi({
4631
+ url: `${WORKSPACES_BASE}/workspaces/${params.workspaceId}/segments/list?${qs.toString()}`,
4632
+ cookies,
4633
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
4634
+ });
4635
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error(`Unexpected segments shape for workspace=${params.workspaceId}`);
4636
+ const data = raw;
4637
+ return {
4638
+ contents: (Array.isArray(data.contents) ? data.contents : []).map((c) => c && typeof c === "object" ? c : {}),
4639
+ totalPage: typeof data.totalPage === "number" ? data.totalPage : 0,
4640
+ currentPage: typeof data.currentPage === "number" ? data.currentPage : page
4641
+ };
4642
+ }
4643
+ async function fetchWorkspaceTerms(workspaceId, type, cookies, opts = {}) {
4644
+ const raw = await requestConsoleApi({
4645
+ url: `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms/${type}/skip-permission`,
4646
+ cookies,
4647
+ ...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
4648
+ });
4649
+ if (!Array.isArray(raw)) throw new Error(`Unexpected workspace terms shape for type=${type}`);
4650
+ return raw.map((entry, i) => {
4651
+ if (!entry || typeof entry !== "object") throw new Error(`Unexpected workspace terms entry at index ${i} for type=${type}`);
4652
+ const e = entry;
4653
+ return {
4654
+ required: Boolean(e.required),
4655
+ termsId: typeof e.termsId === "number" ? e.termsId : 0,
4656
+ revisionId: typeof e.revisionId === "number" ? e.revisionId : 0,
4657
+ title: typeof e.title === "string" ? e.title : "",
4658
+ contentsUrl: typeof e.contentsUrl === "string" ? e.contentsUrl : "",
4659
+ actionType: typeof e.actionType === "string" ? e.actionType : "",
4660
+ isAgreed: Boolean(e.isAgreed),
4661
+ isOneTimeConsent: Boolean(e.isOneTimeConsent)
4662
+ };
4663
+ });
4664
+ }
3889
4665
  //#endregion
3890
4666
  //#region src/commands/workspace.ts
3891
4667
  function formatScalar(v) {
@@ -3893,68 +4669,282 @@ function formatScalar(v) {
3893
4669
  if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return String(v);
3894
4670
  return JSON.stringify(v);
3895
4671
  }
4672
+ const lsCommand = defineCommand({
4673
+ meta: {
4674
+ name: "ls",
4675
+ description: "List workspaces the current user has access to."
4676
+ },
4677
+ args: { json: {
4678
+ type: "boolean",
4679
+ description: "Emit machine-readable JSON to stdout.",
4680
+ default: false
4681
+ } },
4682
+ async run({ args }) {
4683
+ const session = await readSession();
4684
+ if (!session) {
4685
+ emitNotAuthenticated(args.json);
4686
+ return exitAfterFlush(ExitCode.NotAuthenticated);
4687
+ }
4688
+ try {
4689
+ const info = await fetchConsoleMemberUserInfo(session.cookies);
4690
+ const current = session.currentWorkspaceId;
4691
+ if (args.json) {
4692
+ emitJson({
4693
+ ok: true,
4694
+ workspaces: info.workspaces.map((w) => ({
4695
+ workspaceId: w.workspaceId,
4696
+ workspaceName: w.workspaceName,
4697
+ role: w.role,
4698
+ current: w.workspaceId === current
4699
+ }))
4700
+ });
4701
+ return exitAfterFlush(ExitCode.Ok);
4702
+ }
4703
+ if (info.workspaces.length === 0) {
4704
+ process.stdout.write("No workspaces.\n");
4705
+ return exitAfterFlush(ExitCode.Ok);
4706
+ }
4707
+ for (const w of info.workspaces) {
4708
+ const marker = w.workspaceId === current ? "* " : " ";
4709
+ process.stdout.write(`${marker}${w.workspaceId} ${w.workspaceName} (${w.role})\n`);
4710
+ }
4711
+ if (current === void 0) process.stderr.write("No workspace selected. Run `aitcc workspace use <id>`.\n");
4712
+ return exitAfterFlush(ExitCode.Ok);
4713
+ } catch (err) {
4714
+ return emitFailureFromError(args.json, err);
4715
+ }
4716
+ }
4717
+ });
4718
+ const useCommand = defineCommand({
4719
+ meta: {
4720
+ name: "use",
4721
+ description: "Select the current workspace by ID. Subsequent commands use this."
4722
+ },
4723
+ args: {
4724
+ id: {
4725
+ type: "positional",
4726
+ description: "Workspace ID",
4727
+ required: true
4728
+ },
4729
+ json: {
4730
+ type: "boolean",
4731
+ description: "Emit machine-readable JSON to stdout.",
4732
+ default: false
4733
+ }
4734
+ },
4735
+ async run({ args }) {
4736
+ const raw = String(args.id);
4737
+ const parsed = parsePositiveInt$1(raw);
4738
+ if (parsed === null) {
4739
+ const message = `workspace id must be a positive integer (got ${raw})`;
4740
+ if (args.json) emitJson({
4741
+ ok: false,
4742
+ reason: "invalid-id",
4743
+ message
4744
+ });
4745
+ else process.stderr.write(`${message}\n`);
4746
+ return exitAfterFlush(ExitCode.Usage);
4747
+ }
4748
+ const session = await readSession();
4749
+ if (!session) {
4750
+ emitNotAuthenticated(args.json);
4751
+ return exitAfterFlush(ExitCode.NotAuthenticated);
4752
+ }
4753
+ try {
4754
+ const match = (await fetchConsoleMemberUserInfo(session.cookies)).workspaces.find((w) => w.workspaceId === parsed);
4755
+ if (!match) {
4756
+ if (args.json) emitJson({
4757
+ ok: false,
4758
+ reason: "not-found",
4759
+ workspaceId: parsed
4760
+ });
4761
+ else process.stderr.write(`Workspace ${parsed} is not accessible from this account. Run \`aitcc workspace ls\` to see available workspaces.\n`);
4762
+ return exitAfterFlush(ExitCode.Usage);
4763
+ }
4764
+ if (await setCurrentWorkspaceId(parsed) === null) {
4765
+ emitNotAuthenticated(args.json);
4766
+ return exitAfterFlush(ExitCode.NotAuthenticated);
4767
+ }
4768
+ if (args.json) emitJson({
4769
+ ok: true,
4770
+ workspaceId: match.workspaceId,
4771
+ workspaceName: match.workspaceName
4772
+ });
4773
+ else process.stdout.write(`Using workspace ${match.workspaceId} (${match.workspaceName}).\n`);
4774
+ return exitAfterFlush(ExitCode.Ok);
4775
+ } catch (err) {
4776
+ return emitFailureFromError(args.json, err);
4777
+ }
4778
+ }
4779
+ });
4780
+ const showCommand = defineCommand({
4781
+ meta: {
4782
+ name: "show",
4783
+ description: "Show details of the selected workspace (or the one passed with --workspace)."
4784
+ },
4785
+ args: {
4786
+ workspace: {
4787
+ type: "string",
4788
+ description: "Workspace ID to inspect. Defaults to the selected workspace."
4789
+ },
4790
+ json: {
4791
+ type: "boolean",
4792
+ description: "Emit machine-readable JSON to stdout.",
4793
+ default: false
4794
+ }
4795
+ },
4796
+ async run({ args }) {
4797
+ const session = await readSession();
4798
+ if (!session) {
4799
+ emitNotAuthenticated(args.json);
4800
+ return exitAfterFlush(ExitCode.NotAuthenticated);
4801
+ }
4802
+ let workspaceId;
4803
+ if (args.workspace) {
4804
+ const raw = String(args.workspace);
4805
+ const parsed = parsePositiveInt$1(raw);
4806
+ if (parsed === null) {
4807
+ const message = `--workspace must be a positive integer (got ${raw})`;
4808
+ if (args.json) emitJson({
4809
+ ok: false,
4810
+ reason: "invalid-id",
4811
+ message
4812
+ });
4813
+ else process.stderr.write(`${message}\n`);
4814
+ return exitAfterFlush(ExitCode.Usage);
4815
+ }
4816
+ workspaceId = parsed;
4817
+ } else workspaceId = session.currentWorkspaceId;
4818
+ if (workspaceId === void 0) {
4819
+ if (args.json) emitJson({
4820
+ ok: false,
4821
+ reason: "no-workspace-selected"
4822
+ });
4823
+ else process.stderr.write("No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\n");
4824
+ return exitAfterFlush(ExitCode.Usage);
4825
+ }
4826
+ try {
4827
+ const detail = await fetchWorkspaceDetail(workspaceId, session.cookies);
4828
+ if (args.json) {
4829
+ emitJson({
4830
+ ok: true,
4831
+ workspaceId: detail.workspaceId,
4832
+ workspaceName: detail.workspaceName,
4833
+ extra: detail.extra ?? {}
4834
+ });
4835
+ return exitAfterFlush(ExitCode.Ok);
4836
+ }
4837
+ process.stdout.write(`Workspace ${detail.workspaceId}: ${detail.workspaceName}\n`);
4838
+ if (detail.extra) for (const [k, v] of Object.entries(detail.extra)) process.stdout.write(` ${k}: ${formatScalar(v)}\n`);
4839
+ return exitAfterFlush(ExitCode.Ok);
4840
+ } catch (err) {
4841
+ return emitFailureFromError(args.json, err);
4842
+ }
4843
+ }
4844
+ });
4845
+ async function resolveWorkspaceArg(args, selected) {
4846
+ if (args.workspace) {
4847
+ const raw = String(args.workspace);
4848
+ const parsed = parsePositiveInt$1(raw);
4849
+ if (parsed === null) {
4850
+ const message = `--workspace must be a positive integer (got ${raw})`;
4851
+ if (args.json) emitJson({
4852
+ ok: false,
4853
+ reason: "invalid-id",
4854
+ message
4855
+ });
4856
+ else process.stderr.write(`${message}\n`);
4857
+ return null;
4858
+ }
4859
+ return parsed;
4860
+ }
4861
+ if (selected === void 0) {
4862
+ if (args.json) emitJson({
4863
+ ok: false,
4864
+ reason: "no-workspace-selected"
4865
+ });
4866
+ else process.stderr.write("No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\n");
4867
+ return null;
4868
+ }
4869
+ return selected;
4870
+ }
4871
+ const partnerCommand = defineCommand({
4872
+ meta: {
4873
+ name: "partner",
4874
+ description: "Show the partner (billing/payout) registration state for the selected workspace."
4875
+ },
4876
+ args: {
4877
+ workspace: {
4878
+ type: "string",
4879
+ description: "Workspace ID to inspect. Defaults to the selected workspace."
4880
+ },
4881
+ json: {
4882
+ type: "boolean",
4883
+ description: "Emit machine-readable JSON to stdout.",
4884
+ default: false
4885
+ }
4886
+ },
4887
+ async run({ args }) {
4888
+ const session = await readSession();
4889
+ if (!session) {
4890
+ emitNotAuthenticated(args.json);
4891
+ return exitAfterFlush(ExitCode.NotAuthenticated);
4892
+ }
4893
+ const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);
4894
+ if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);
4895
+ try {
4896
+ const state = await fetchWorkspacePartner(workspaceId, session.cookies);
4897
+ if (args.json) {
4898
+ emitJson({
4899
+ ok: true,
4900
+ workspaceId,
4901
+ registered: state.registered,
4902
+ approvalType: state.approvalType,
4903
+ rejectMessage: state.rejectMessage,
4904
+ partner: state.partner
4905
+ });
4906
+ return exitAfterFlush(ExitCode.Ok);
4907
+ }
4908
+ process.stdout.write(`Workspace ${workspaceId} partner:\n`);
4909
+ process.stdout.write(` registered: ${state.registered}\n`);
4910
+ process.stdout.write(` approvalType: ${state.approvalType ?? "null"}\n`);
4911
+ if (state.rejectMessage) process.stdout.write(` rejectMessage: ${state.rejectMessage}\n`);
4912
+ if (state.partner) {
4913
+ process.stdout.write(" partner:\n");
4914
+ for (const [k, v] of Object.entries(state.partner)) process.stdout.write(` ${k}: ${formatScalar(v)}\n`);
4915
+ }
4916
+ return exitAfterFlush(ExitCode.Ok);
4917
+ } catch (err) {
4918
+ return emitFailureFromError(args.json, err);
4919
+ }
4920
+ }
4921
+ });
4922
+ function formatTermLines(term) {
4923
+ return ` ${term.isAgreed ? "[agreed]" : "[pending]"}${term.required ? " required" : ""} ${term.title}\n ${term.contentsUrl}\n`;
4924
+ }
3896
4925
  const workspaceCommand = defineCommand({
3897
4926
  meta: {
3898
4927
  name: "workspace",
3899
4928
  description: "Inspect and switch between the workspaces this account can access."
3900
4929
  },
3901
4930
  subCommands: {
3902
- ls: defineCommand({
4931
+ ls: lsCommand,
4932
+ use: useCommand,
4933
+ show: showCommand,
4934
+ partner: partnerCommand,
4935
+ terms: defineCommand({
3903
4936
  meta: {
3904
- name: "ls",
3905
- description: "List workspaces the current user has access to."
3906
- },
3907
- args: { json: {
3908
- type: "boolean",
3909
- description: "Emit machine-readable JSON to stdout.",
3910
- default: false
3911
- } },
3912
- async run({ args }) {
3913
- const session = await readSession();
3914
- if (!session) {
3915
- emitNotAuthenticated(args.json);
3916
- return exitAfterFlush(ExitCode.NotAuthenticated);
3917
- }
3918
- try {
3919
- const info = await fetchConsoleMemberUserInfo(session.cookies);
3920
- const current = session.currentWorkspaceId;
3921
- if (args.json) {
3922
- emitJson({
3923
- ok: true,
3924
- workspaces: info.workspaces.map((w) => ({
3925
- workspaceId: w.workspaceId,
3926
- workspaceName: w.workspaceName,
3927
- role: w.role,
3928
- current: w.workspaceId === current
3929
- }))
3930
- });
3931
- return exitAfterFlush(ExitCode.Ok);
3932
- }
3933
- if (info.workspaces.length === 0) {
3934
- process.stdout.write("No workspaces.\n");
3935
- return exitAfterFlush(ExitCode.Ok);
3936
- }
3937
- for (const w of info.workspaces) {
3938
- const marker = w.workspaceId === current ? "* " : " ";
3939
- process.stdout.write(`${marker}${w.workspaceId} ${w.workspaceName} (${w.role})\n`);
3940
- }
3941
- if (current === void 0) process.stderr.write("No workspace selected. Run `aitcc workspace use <id>`.\n");
3942
- return exitAfterFlush(ExitCode.Ok);
3943
- } catch (err) {
3944
- return emitFailureFromError(args.json, err);
3945
- }
3946
- }
3947
- }),
3948
- use: defineCommand({
3949
- meta: {
3950
- name: "use",
3951
- description: "Select the current workspace by ID. Subsequent commands use this."
4937
+ name: "terms",
4938
+ description: "Show the console terms-of-agreement state that gate workspace-level features (Toss login, IAP, IAA, biz workspace, promotion money)."
3952
4939
  },
3953
4940
  args: {
3954
- id: {
3955
- type: "positional",
3956
- description: "Workspace ID",
3957
- required: true
4941
+ type: {
4942
+ type: "string",
4943
+ description: `Term bucket to inspect: ${WORKSPACE_TERM_TYPES.join(" | ")}. Omit to query every bucket.`
4944
+ },
4945
+ workspace: {
4946
+ type: "string",
4947
+ description: "Workspace ID to inspect. Defaults to the selected workspace."
3958
4948
  },
3959
4949
  json: {
3960
4950
  type: "boolean",
@@ -3963,114 +4953,158 @@ const workspaceCommand = defineCommand({
3963
4953
  }
3964
4954
  },
3965
4955
  async run({ args }) {
3966
- const raw = String(args.id);
3967
- const parsed = parsePositiveInt$1(raw);
3968
- if (parsed === null) {
3969
- const message = `workspace id must be a positive integer (got ${raw})`;
4956
+ const session = await readSession();
4957
+ if (!session) {
4958
+ emitNotAuthenticated(args.json);
4959
+ return exitAfterFlush(ExitCode.NotAuthenticated);
4960
+ }
4961
+ const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);
4962
+ if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);
4963
+ const typesToQuery = (() => {
4964
+ if (!args.type) return WORKSPACE_TERM_TYPES;
4965
+ const raw = String(args.type).toUpperCase();
4966
+ if (WORKSPACE_TERM_TYPES.includes(raw)) return [raw];
4967
+ return [];
4968
+ })();
4969
+ if (typesToQuery.length === 0) {
4970
+ const message = `--type must be one of: ${WORKSPACE_TERM_TYPES.join(", ")}`;
3970
4971
  if (args.json) emitJson({
3971
4972
  ok: false,
3972
- reason: "invalid-id",
3973
- message
4973
+ reason: "invalid-type",
4974
+ allowed: [...WORKSPACE_TERM_TYPES]
3974
4975
  });
3975
4976
  else process.stderr.write(`${message}\n`);
3976
4977
  return exitAfterFlush(ExitCode.Usage);
3977
4978
  }
3978
- const session = await readSession();
3979
- if (!session) {
3980
- emitNotAuthenticated(args.json);
3981
- return exitAfterFlush(ExitCode.NotAuthenticated);
3982
- }
3983
4979
  try {
3984
- const match = (await fetchConsoleMemberUserInfo(session.cookies)).workspaces.find((w) => w.workspaceId === parsed);
3985
- if (!match) {
3986
- if (args.json) emitJson({
3987
- ok: false,
3988
- reason: "not-found",
3989
- workspaceId: parsed
4980
+ const results = await Promise.all(typesToQuery.map(async (t) => [t, await fetchWorkspaceTerms(workspaceId, t, session.cookies)]));
4981
+ if (typesToQuery.length === 1) {
4982
+ const [type, terms] = results[0];
4983
+ if (args.json) {
4984
+ emitJson({
4985
+ ok: true,
4986
+ workspaceId,
4987
+ type,
4988
+ terms
4989
+ });
4990
+ return exitAfterFlush(ExitCode.Ok);
4991
+ }
4992
+ process.stdout.write(`Workspace ${workspaceId} terms (${type}):\n`);
4993
+ if (terms.length === 0) process.stdout.write(" (no terms required)\n");
4994
+ else for (const t of terms) process.stdout.write(formatTermLines(t));
4995
+ return exitAfterFlush(ExitCode.Ok);
4996
+ }
4997
+ const byType = {};
4998
+ for (const [t, terms] of results) byType[t] = terms;
4999
+ if (args.json) {
5000
+ emitJson({
5001
+ ok: true,
5002
+ workspaceId,
5003
+ byType
3990
5004
  });
3991
- else process.stderr.write(`Workspace ${parsed} is not accessible from this account. Run \`aitcc workspace ls\` to see available workspaces.\n`);
3992
- return exitAfterFlush(ExitCode.Usage);
5005
+ return exitAfterFlush(ExitCode.Ok);
3993
5006
  }
3994
- if (await setCurrentWorkspaceId(parsed) === null) {
3995
- emitNotAuthenticated(args.json);
3996
- return exitAfterFlush(ExitCode.NotAuthenticated);
5007
+ for (const [type, terms] of results) {
5008
+ process.stdout.write(`\n[${type}]\n`);
5009
+ if (terms.length === 0) process.stdout.write(" (no terms required)\n");
5010
+ else for (const t of terms) process.stdout.write(formatTermLines(t));
3997
5011
  }
3998
- if (args.json) emitJson({
3999
- ok: true,
4000
- workspaceId: match.workspaceId,
4001
- workspaceName: match.workspaceName
4002
- });
4003
- else process.stdout.write(`Using workspace ${match.workspaceId} (${match.workspaceName}).\n`);
4004
5012
  return exitAfterFlush(ExitCode.Ok);
4005
5013
  } catch (err) {
4006
5014
  return emitFailureFromError(args.json, err);
4007
5015
  }
4008
5016
  }
4009
5017
  }),
4010
- show: defineCommand({
5018
+ segments: defineCommand({
4011
5019
  meta: {
4012
- name: "show",
4013
- description: "Show details of the selected workspace (or the one passed with --workspace)."
5020
+ name: "segments",
5021
+ description: "Inspect user segments defined in a workspace."
4014
5022
  },
4015
- args: {
4016
- workspace: {
4017
- type: "string",
4018
- description: "Workspace ID to inspect. Defaults to the selected workspace."
5023
+ subCommands: { ls: defineCommand({
5024
+ meta: {
5025
+ name: "ls",
5026
+ description: "List user segments in the selected workspace (the 세그먼트 menu)."
4019
5027
  },
4020
- json: {
4021
- type: "boolean",
4022
- description: "Emit machine-readable JSON to stdout.",
4023
- default: false
4024
- }
4025
- },
4026
- async run({ args }) {
4027
- const session = await readSession();
4028
- if (!session) {
4029
- emitNotAuthenticated(args.json);
4030
- return exitAfterFlush(ExitCode.NotAuthenticated);
4031
- }
4032
- let workspaceId;
4033
- if (args.workspace) {
4034
- const raw = String(args.workspace);
4035
- const parsed = parsePositiveInt$1(raw);
4036
- if (parsed === null) {
4037
- const message = `--workspace must be a positive integer (got ${raw})`;
5028
+ args: {
5029
+ workspace: {
5030
+ type: "string",
5031
+ description: "Workspace ID. Defaults to the selected workspace."
5032
+ },
5033
+ category: {
5034
+ type: "string",
5035
+ description: "Category bucket (tab). Defaults to \"생성된 세그먼트\" — the UI's initial tab."
5036
+ },
5037
+ search: {
5038
+ type: "string",
5039
+ description: "Name-contains filter. Empty matches everything."
5040
+ },
5041
+ page: {
5042
+ type: "string",
5043
+ description: "Page number (0-indexed).",
5044
+ default: "0"
5045
+ },
5046
+ json: {
5047
+ type: "boolean",
5048
+ description: "Emit machine-readable JSON to stdout.",
5049
+ default: false
5050
+ }
5051
+ },
5052
+ async run({ args }) {
5053
+ const session = await readSession();
5054
+ if (!session) {
5055
+ emitNotAuthenticated(args.json);
5056
+ return exitAfterFlush(ExitCode.NotAuthenticated);
5057
+ }
5058
+ const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);
5059
+ if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);
5060
+ const pageRaw = String(args.page);
5061
+ const pageNum = Number(pageRaw);
5062
+ if (!Number.isFinite(pageNum) || !Number.isInteger(pageNum) || pageNum < 0) {
5063
+ const message = `--page must be a non-negative integer (got ${JSON.stringify(pageRaw)})`;
4038
5064
  if (args.json) emitJson({
4039
5065
  ok: false,
4040
- reason: "invalid-id",
5066
+ reason: "invalid-page",
4041
5067
  message
4042
5068
  });
4043
5069
  else process.stderr.write(`${message}\n`);
4044
5070
  return exitAfterFlush(ExitCode.Usage);
4045
5071
  }
4046
- workspaceId = parsed;
4047
- } else workspaceId = session.currentWorkspaceId;
4048
- if (workspaceId === void 0) {
4049
- if (args.json) emitJson({
4050
- ok: false,
4051
- reason: "no-workspace-selected"
4052
- });
4053
- else process.stderr.write("No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\n");
4054
- return exitAfterFlush(ExitCode.Usage);
4055
- }
4056
- try {
4057
- const detail = await fetchWorkspaceDetail(workspaceId, session.cookies);
4058
- if (args.json) {
4059
- emitJson({
4060
- ok: true,
4061
- workspaceId: detail.workspaceId,
4062
- workspaceName: detail.workspaceName,
4063
- extra: detail.extra ?? {}
4064
- });
5072
+ try {
5073
+ const page = await fetchWorkspaceSegments({
5074
+ workspaceId,
5075
+ ...args.category !== void 0 ? { category: String(args.category) } : {},
5076
+ ...args.search !== void 0 ? { search: String(args.search) } : {},
5077
+ page: pageNum
5078
+ }, session.cookies);
5079
+ const category = args.category !== void 0 ? String(args.category) : "생성된 세그먼트";
5080
+ if (args.json) {
5081
+ emitJson({
5082
+ ok: true,
5083
+ workspaceId,
5084
+ category,
5085
+ segments: page.contents,
5086
+ totalPage: page.totalPage,
5087
+ currentPage: page.currentPage
5088
+ });
5089
+ return exitAfterFlush(ExitCode.Ok);
5090
+ }
5091
+ if (page.contents.length === 0) {
5092
+ process.stdout.write(`Workspace ${workspaceId} (${category}): no segments on page ${page.currentPage}\n`);
5093
+ return exitAfterFlush(ExitCode.Ok);
5094
+ }
5095
+ process.stdout.write(`Workspace ${workspaceId} (${category}): ${page.contents.length} segment(s), page ${page.currentPage} of ${page.totalPage}\n`);
5096
+ for (const s of page.contents) {
5097
+ const id = typeof s.id === "string" || typeof s.id === "number" ? s.id : typeof s.segmentId === "string" || typeof s.segmentId === "number" ? s.segmentId : "-";
5098
+ const name = typeof s.name === "string" ? s.name : typeof s.title === "string" ? s.title : "-";
5099
+ const userCount = typeof s.userCount === "number" ? String(s.userCount) : typeof s.count === "number" ? String(s.count) : "-";
5100
+ process.stdout.write(`${id}\t${name}\t${userCount}\n`);
5101
+ }
4065
5102
  return exitAfterFlush(ExitCode.Ok);
5103
+ } catch (err) {
5104
+ return emitFailureFromError(args.json, err);
4066
5105
  }
4067
- process.stdout.write(`Workspace ${detail.workspaceId}: ${detail.workspaceName}\n`);
4068
- if (detail.extra) for (const [k, v] of Object.entries(detail.extra)) process.stdout.write(` ${k}: ${formatScalar(v)}\n`);
4069
- return exitAfterFlush(ExitCode.Ok);
4070
- } catch (err) {
4071
- return emitFailureFromError(args.json, err);
4072
5106
  }
4073
- }
5107
+ }) }
4074
5108
  })
4075
5109
  }
4076
5110
  });
@@ -4091,7 +5125,8 @@ runMain(defineCommand({
4091
5125
  app: appCommand,
4092
5126
  members: membersCommand,
4093
5127
  keys: keysCommand,
4094
- notices: noticesCommand
5128
+ notices: noticesCommand,
5129
+ me: meCommand
4095
5130
  }
4096
5131
  }));
4097
5132
  //#endregion