@commissionsight/cli 0.1.0

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.
Files changed (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +248 -0
  3. package/bin/cs.mjs +2 -0
  4. package/dist/commands/admin.d.ts +7 -0
  5. package/dist/commands/admin.js +409 -0
  6. package/dist/commands/auth.d.ts +7 -0
  7. package/dist/commands/auth.js +107 -0
  8. package/dist/commands/batch.d.ts +2 -0
  9. package/dist/commands/batch.js +68 -0
  10. package/dist/commands/billing.d.ts +6 -0
  11. package/dist/commands/billing.js +75 -0
  12. package/dist/commands/carrier.d.ts +6 -0
  13. package/dist/commands/carrier.js +111 -0
  14. package/dist/commands/completion.d.ts +6 -0
  15. package/dist/commands/completion.js +56 -0
  16. package/dist/commands/context.d.ts +6 -0
  17. package/dist/commands/context.js +73 -0
  18. package/dist/commands/file.d.ts +6 -0
  19. package/dist/commands/file.js +97 -0
  20. package/dist/commands/job.d.ts +2 -0
  21. package/dist/commands/job.js +186 -0
  22. package/dist/commands/member.d.ts +5 -0
  23. package/dist/commands/member.js +91 -0
  24. package/dist/commands/meta.d.ts +7 -0
  25. package/dist/commands/meta.js +36 -0
  26. package/dist/commands/rate.d.ts +5 -0
  27. package/dist/commands/rate.js +69 -0
  28. package/dist/commands/registry.d.ts +14 -0
  29. package/dist/commands/registry.js +56 -0
  30. package/dist/commands/report.d.ts +2 -0
  31. package/dist/commands/report.js +168 -0
  32. package/dist/commands/session.d.ts +5 -0
  33. package/dist/commands/session.js +21 -0
  34. package/dist/commands/team.d.ts +5 -0
  35. package/dist/commands/team.js +61 -0
  36. package/dist/commands/upload.d.ts +85 -0
  37. package/dist/commands/upload.js +111 -0
  38. package/dist/commands/webhook.d.ts +5 -0
  39. package/dist/commands/webhook.js +56 -0
  40. package/dist/commands/workspace.d.ts +8 -0
  41. package/dist/commands/workspace.js +65 -0
  42. package/dist/config/schema.d.ts +21 -0
  43. package/dist/config/schema.js +33 -0
  44. package/dist/config/store.d.ts +17 -0
  45. package/dist/config/store.js +74 -0
  46. package/dist/context.d.ts +22 -0
  47. package/dist/context.js +100 -0
  48. package/dist/errors.d.ts +37 -0
  49. package/dist/errors.js +70 -0
  50. package/dist/globals.d.ts +10 -0
  51. package/dist/globals.js +38 -0
  52. package/dist/io.d.ts +28 -0
  53. package/dist/io.js +28 -0
  54. package/dist/lib/batch.d.ts +52 -0
  55. package/dist/lib/batch.js +0 -0
  56. package/dist/lib/confirm.d.ts +2 -0
  57. package/dist/lib/confirm.js +23 -0
  58. package/dist/lib/file.d.ts +6 -0
  59. package/dist/lib/file.js +43 -0
  60. package/dist/lib/input.d.ts +2 -0
  61. package/dist/lib/input.js +35 -0
  62. package/dist/lib/paginate.d.ts +33 -0
  63. package/dist/lib/paginate.js +47 -0
  64. package/dist/lib/period.d.ts +15 -0
  65. package/dist/lib/period.js +43 -0
  66. package/dist/lib/poll.d.ts +14 -0
  67. package/dist/lib/poll.js +17 -0
  68. package/dist/lib/resolve.d.ts +30 -0
  69. package/dist/lib/resolve.js +81 -0
  70. package/dist/main.d.ts +1 -0
  71. package/dist/main.js +17 -0
  72. package/dist/output/color.d.ts +26 -0
  73. package/dist/output/color.js +37 -0
  74. package/dist/output/csv.d.ts +25 -0
  75. package/dist/output/csv.js +119 -0
  76. package/dist/output/envelope.d.ts +29 -0
  77. package/dist/output/envelope.js +66 -0
  78. package/dist/output/help.d.ts +7 -0
  79. package/dist/output/help.js +57 -0
  80. package/dist/output/print.d.ts +14 -0
  81. package/dist/output/print.js +70 -0
  82. package/dist/output/schema-tree.d.ts +32 -0
  83. package/dist/output/schema-tree.js +33 -0
  84. package/dist/router.d.ts +6 -0
  85. package/dist/router.js +267 -0
  86. package/dist/types.d.ts +66 -0
  87. package/dist/types.js +1 -0
  88. package/dist/util.d.ts +11 -0
  89. package/dist/util.js +39 -0
  90. package/dist/version.d.ts +2 -0
  91. package/dist/version.js +41 -0
  92. package/package.json +53 -0
@@ -0,0 +1,409 @@
1
+ import { cover } from './registry.js';
2
+ import { loadFile } from '../lib/file.js';
3
+ import { readJsonInput } from '../lib/input.js';
4
+ import { confirmDestructive } from '../lib/confirm.js';
5
+ import { ApiError, CliError, UsageError, EXIT } from '../errors.js';
6
+ import { optStr, optBool, optNum } from '../util.js';
7
+ async function adminGate(fn) {
8
+ try {
9
+ return await fn();
10
+ }
11
+ catch (e) {
12
+ if (e instanceof ApiError && (e.status === 401 || e.status === 403)) {
13
+ throw new CliError('admin token required: this token is not authorized for admin endpoints', EXIT.AUTH);
14
+ }
15
+ throw e;
16
+ }
17
+ }
18
+ const a = (ctx) => ctx.client().admin;
19
+ export function adminCommands() {
20
+ registerCoverage();
21
+ return [
22
+ // ---- accounts -------------------------------------------------------
23
+ {
24
+ path: ['admin', 'account', 'list'],
25
+ summary: 'List accounts',
26
+ options: { status: { type: 'string', desc: 'Filter by status', choices: ['active', 'pending', 'suspended'], placeholder: '<s>' } },
27
+ run: (ctx, p) => adminGate(async () => (await a(ctx).listAccounts(optStr(p.options['status']))).data),
28
+ },
29
+ {
30
+ path: ['admin', 'account', 'create'],
31
+ summary: 'Create an account',
32
+ args: [{ name: 'name', required: true }],
33
+ run: (ctx, p) => adminGate(() => a(ctx).createAccount(p.args[0])),
34
+ },
35
+ {
36
+ path: ['admin', 'account', 'approve'],
37
+ summary: 'Approve a pending account',
38
+ args: [{ name: 'id', required: true }],
39
+ run: (ctx, p) => adminGate(() => a(ctx).approveAccount(p.args[0])),
40
+ },
41
+ {
42
+ path: ['admin', 'account', 'provision'],
43
+ summary: 'Provision (or re-provision) an account data store',
44
+ args: [{ name: 'id', required: true }],
45
+ options: { 'conn-string': { type: 'string', desc: 'Use an out-of-band DB connection string', placeholder: '<s>' } },
46
+ run: (ctx, p) => adminGate(() => a(ctx).provisionAccount(p.args[0], optStr(p.options['conn-string']))),
47
+ },
48
+ {
49
+ path: ['admin', 'account', 'overview'],
50
+ summary: 'Per-account dashboard (counts + latest rollup)',
51
+ args: [{ name: 'id', required: true }],
52
+ run: (ctx, p) => adminGate(() => a(ctx).accountOverview(p.args[0])),
53
+ },
54
+ {
55
+ path: ['admin', 'account', 'files'],
56
+ summary: 'List an account’s files',
57
+ args: [{ name: 'id', required: true }],
58
+ options: pageOpts(),
59
+ run: (ctx, p) => adminGate(async () => (await a(ctx).accountFiles(p.args[0], pageParams(p.options))).data),
60
+ },
61
+ {
62
+ path: ['admin', 'account', 'jobs'],
63
+ summary: 'List an account’s jobs',
64
+ args: [{ name: 'id', required: true }],
65
+ options: pageOpts(),
66
+ run: (ctx, p) => adminGate(async () => (await a(ctx).accountJobs(p.args[0], pageParams(p.options))).data),
67
+ },
68
+ {
69
+ path: ['admin', 'account', 'users'],
70
+ summary: 'List an account’s users',
71
+ args: [{ name: 'id', required: true }],
72
+ run: (ctx, p) => adminGate(async () => (await a(ctx).accountUsers(p.args[0])).data),
73
+ },
74
+ {
75
+ path: ['admin', 'account', 'purge-files'],
76
+ summary: 'Purge ALL raw bytes for an account (destructive)',
77
+ args: [{ name: 'id', required: true }],
78
+ run: (ctx, p) => adminGate(async () => {
79
+ await confirmDestructive(ctx, `purge ALL files for account ${p.args[0]}`);
80
+ return a(ctx).purgeAccountFiles(p.args[0]);
81
+ }),
82
+ },
83
+ {
84
+ path: ['admin', 'account', 'billing'],
85
+ summary: 'Get an account’s billing record',
86
+ args: [{ name: 'id', required: true }],
87
+ run: (ctx, p) => adminGate(() => a(ctx).getAccountBilling(p.args[0])),
88
+ },
89
+ {
90
+ path: ['admin', 'account', 'billing-rate'],
91
+ summary: 'Set/clear an account’s custom billing rate (cents)',
92
+ args: [{ name: 'id', required: true }],
93
+ options: {
94
+ cents: { type: 'string', desc: 'Custom per-member rate in cents', placeholder: '<n>' },
95
+ clear: { type: 'boolean', desc: 'Clear the custom rate' },
96
+ },
97
+ run: (ctx, p) => adminGate(() => {
98
+ if (optBool(p.options['clear']))
99
+ return a(ctx).setBillingRate(p.args[0], null);
100
+ const cents = optNum(p.options['cents']);
101
+ if (cents === undefined)
102
+ throw new UsageError('provide --cents <n> or --clear');
103
+ return a(ctx).setBillingRate(p.args[0], cents);
104
+ }),
105
+ },
106
+ {
107
+ path: ['admin', 'account', 'ai-settings'],
108
+ summary: 'Set an account’s AI cap + pass-through billing',
109
+ args: [{ name: 'id', required: true }],
110
+ options: {
111
+ 'cap-cents': { type: 'string', desc: 'Monthly AI cap in cents', placeholder: '<n>' },
112
+ passthrough: { type: 'boolean', desc: 'Enable pass-through billing' },
113
+ },
114
+ run: (ctx, p) => adminGate(() => a(ctx).setAiSettings(p.args[0], {
115
+ ...(optNum(p.options['cap-cents']) !== undefined ? { capCents: optNum(p.options['cap-cents']) } : {}),
116
+ ...(optBool(p.options['passthrough']) ? { passthrough: true } : {}),
117
+ })),
118
+ },
119
+ {
120
+ path: ['admin', 'account', 'surcharge'],
121
+ summary: 'Enable/disable an account surcharge',
122
+ args: [{ name: 'id', required: true }],
123
+ options: {
124
+ enabled: { type: 'boolean', desc: 'Enable the surcharge' },
125
+ disabled: { type: 'boolean', desc: 'Disable the surcharge' },
126
+ },
127
+ run: (ctx, p) => adminGate(() => {
128
+ const on = optBool(p.options['enabled']);
129
+ const off = optBool(p.options['disabled']);
130
+ if (on === off)
131
+ throw new UsageError('pass exactly one of --enabled or --disabled');
132
+ return a(ctx).setSurcharge(p.args[0], on);
133
+ }),
134
+ },
135
+ {
136
+ path: ['admin', 'account', 'credentials'],
137
+ summary: 'Store DB credentials for an account',
138
+ args: [{ name: 'accountId', required: true }],
139
+ options: {
140
+ 'conn-string': { type: 'string', desc: 'Connection string (required)', placeholder: '<s>' },
141
+ region: { type: 'string', desc: 'Region', placeholder: '<r>' },
142
+ },
143
+ run: (ctx, p) => adminGate(() => {
144
+ const connString = optStr(p.options['conn-string']);
145
+ if (!connString)
146
+ throw new UsageError('--conn-string is required');
147
+ return a(ctx).storeCredentials(p.args[0], {
148
+ connString,
149
+ ...(optStr(p.options['region']) ? { region: optStr(p.options['region']) } : {}),
150
+ });
151
+ }),
152
+ },
153
+ // ---- tokens ---------------------------------------------------------
154
+ {
155
+ path: ['admin', 'token', 'issue'],
156
+ summary: 'Issue an API token for an account (shown once)',
157
+ args: [{ name: 'accountId', required: true }],
158
+ options: { label: { type: 'string', desc: 'Token label', placeholder: '<l>' } },
159
+ run: (ctx, p) => adminGate(async () => {
160
+ const res = await a(ctx).issueToken(p.args[0], optStr(p.options['label']));
161
+ ctx.log('token shown once and not stored — copy it now');
162
+ return res;
163
+ }),
164
+ },
165
+ {
166
+ path: ['admin', 'token', 'list'],
167
+ summary: 'List an account’s API tokens (metadata only)',
168
+ args: [{ name: 'accountId', required: true }],
169
+ run: (ctx, p) => adminGate(async () => (await a(ctx).listTokens(p.args[0])).data),
170
+ },
171
+ {
172
+ path: ['admin', 'token', 'revoke'],
173
+ summary: 'Revoke an API token',
174
+ args: [{ name: 'tokenId', required: true }],
175
+ run: (ctx, p) => adminGate(() => a(ctx).revokeToken(p.args[0])),
176
+ },
177
+ // ---- carriers -------------------------------------------------------
178
+ {
179
+ path: ['admin', 'carrier', 'create'],
180
+ summary: 'Create a carrier',
181
+ options: {
182
+ name: { type: 'string', desc: 'Carrier name (required)', placeholder: '<n>' },
183
+ slug: { type: 'string', desc: 'Carrier slug (required)', placeholder: '<s>' },
184
+ },
185
+ run: (ctx, p) => adminGate(() => {
186
+ const name = optStr(p.options['name']);
187
+ const slug = optStr(p.options['slug']);
188
+ if (!name || !slug)
189
+ throw new UsageError('--name and --slug are required');
190
+ return a(ctx).createCarrier(name, slug);
191
+ }),
192
+ },
193
+ {
194
+ path: ['admin', 'carrier', 'rename'],
195
+ summary: 'Rename a carrier',
196
+ args: [{ name: 'id', required: true }],
197
+ options: {
198
+ name: { type: 'string', desc: 'New name (required)', placeholder: '<n>' },
199
+ slug: { type: 'string', desc: 'New slug (optional)', placeholder: '<s>' },
200
+ },
201
+ run: (ctx, p) => adminGate(() => {
202
+ const name = optStr(p.options['name']);
203
+ if (!name)
204
+ throw new UsageError('--name is required');
205
+ return a(ctx).renameCarrier(p.args[0], name, optStr(p.options['slug']));
206
+ }),
207
+ },
208
+ {
209
+ path: ['admin', 'carrier', 'config', 'create'],
210
+ summary: 'Create a global carrier config (-f file.json or stdin)',
211
+ args: [{ name: 'carrierId', required: true }],
212
+ options: { file: { type: 'string', short: 'f', desc: 'Config JSON', placeholder: '<f.json>' } },
213
+ run: (ctx, p) => adminGate(() => a(ctx).createGlobalConfig(p.args[0], readJsonInput(optStr(p.options['file'])))),
214
+ },
215
+ {
216
+ path: ['admin', 'carrier', 'config', 'list'],
217
+ summary: 'List a carrier’s global configs',
218
+ args: [{ name: 'carrierId', required: true }],
219
+ run: (ctx, p) => adminGate(async () => (await a(ctx).listCarrierConfigs(p.args[0])).data),
220
+ },
221
+ {
222
+ path: ['admin', 'carrier', 'config', 'update'],
223
+ summary: 'Update a carrier config (-f file.json or stdin)',
224
+ args: [
225
+ { name: 'carrierId', required: true },
226
+ { name: 'configId', required: true },
227
+ ],
228
+ options: { file: { type: 'string', short: 'f', desc: 'Config JSON', placeholder: '<f.json>' } },
229
+ run: (ctx, p) => adminGate(() => a(ctx).updateCarrierConfig(p.args[0], p.args[1], readJsonInput(optStr(p.options['file'])))),
230
+ },
231
+ {
232
+ path: ['admin', 'carrier', 'config', 'infer'],
233
+ summary: 'Infer a draft config from a sample file',
234
+ args: [{ name: 'carrierId', required: true }],
235
+ options: {
236
+ file: { type: 'string', desc: 'Sample file (required)', placeholder: '<sample>' },
237
+ sheet: { type: 'string', desc: 'Sheet name (xlsx)', placeholder: '<name>' },
238
+ type: { type: 'string', desc: 'File type', choices: ['csv', 'xlsx'], placeholder: '<t>' },
239
+ },
240
+ run: (ctx, p) => adminGate(() => {
241
+ const file = optStr(p.options['file']);
242
+ if (!file)
243
+ throw new UsageError('--file <sample> is required');
244
+ const opts = {
245
+ ...(optStr(p.options['sheet']) ? { sheetName: optStr(p.options['sheet']) } : {}),
246
+ ...(optStr(p.options['type']) ? { fileType: optStr(p.options['type']) } : {}),
247
+ };
248
+ return a(ctx).inferConfig(p.args[0], loadFile(file), Object.keys(opts).length ? opts : undefined);
249
+ }),
250
+ },
251
+ // ---- users ----------------------------------------------------------
252
+ {
253
+ path: ['admin', 'user', 'list'],
254
+ summary: 'List all users',
255
+ run: (ctx) => adminGate(async () => (await a(ctx).listUsers()).data),
256
+ },
257
+ {
258
+ path: ['admin', 'user', 'create'],
259
+ summary: 'Create a user',
260
+ options: {
261
+ email: { type: 'string', desc: 'Email (required)', placeholder: '<email>' },
262
+ account: { type: 'string', desc: 'Account id', placeholder: '<id>' },
263
+ role: { type: 'string', desc: 'Role', choices: ['member', 'admin'], placeholder: '<r>' },
264
+ },
265
+ run: (ctx, p) => adminGate(() => {
266
+ const email = optStr(p.options['email']);
267
+ if (!email)
268
+ throw new UsageError('--email is required');
269
+ return a(ctx).createUser({
270
+ email,
271
+ ...(optStr(p.options['account']) ? { accountId: optStr(p.options['account']) } : {}),
272
+ ...(optStr(p.options['role']) ? { role: optStr(p.options['role']) } : {}),
273
+ });
274
+ }),
275
+ },
276
+ {
277
+ path: ['admin', 'user', 'update'],
278
+ summary: 'Update a user’s role',
279
+ args: [{ name: 'id', required: true }],
280
+ options: { role: { type: 'string', desc: 'Role (required)', choices: ['member', 'admin'], placeholder: '<r>' } },
281
+ run: (ctx, p) => adminGate(() => {
282
+ const role = optStr(p.options['role']);
283
+ if (!role)
284
+ throw new UsageError('--role member|admin is required');
285
+ return a(ctx).updateUser(p.args[0], { role });
286
+ }),
287
+ },
288
+ {
289
+ path: ['admin', 'user', 'delete'],
290
+ summary: 'Delete a user (destructive)',
291
+ args: [{ name: 'id', required: true }],
292
+ run: (ctx, p) => adminGate(async () => {
293
+ await confirmDestructive(ctx, `delete user ${p.args[0]}`);
294
+ return a(ctx).deleteUser(p.args[0]);
295
+ }),
296
+ },
297
+ // ---- allowlist / jobs / system -------------------------------------
298
+ {
299
+ path: ['admin', 'allowlist', 'add'],
300
+ summary: 'Add an email to the allowlist',
301
+ args: [{ name: 'email', required: true }],
302
+ run: (ctx, p) => adminGate(() => a(ctx).addAllowlist(p.args[0])),
303
+ },
304
+ {
305
+ path: ['admin', 'job', 'list'],
306
+ summary: 'List jobs across accounts',
307
+ options: pageOpts({ status: { type: 'string', desc: 'Filter by status', placeholder: '<s>' } }),
308
+ run: (ctx, p) => adminGate(async () => (await a(ctx).listJobs({
309
+ ...(optStr(p.options['status']) ? { status: optStr(p.options['status']) } : {}),
310
+ ...pageParams(p.options),
311
+ })).data),
312
+ },
313
+ {
314
+ path: ['admin', 'job', 'get'],
315
+ summary: 'Get full job detail',
316
+ args: [{ name: 'id', required: true }],
317
+ run: (ctx, p) => adminGate(() => a(ctx).jobDetail(p.args[0])),
318
+ },
319
+ {
320
+ path: ['admin', 'job', 'retry'],
321
+ summary: 'Retry a job',
322
+ args: [{ name: 'id', required: true }],
323
+ run: (ctx, p) => adminGate(() => a(ctx).retryJob(p.args[0])),
324
+ },
325
+ {
326
+ path: ['admin', 'job', 'rescore'],
327
+ summary: 'Rescore a job',
328
+ args: [{ name: 'id', required: true }],
329
+ run: (ctx, p) => adminGate(() => a(ctx).rescoreJob(p.args[0])),
330
+ },
331
+ {
332
+ path: ['admin', 'metrics'],
333
+ summary: 'Platform metrics',
334
+ run: (ctx) => adminGate(() => a(ctx).metrics()),
335
+ },
336
+ {
337
+ path: ['admin', 'logs'],
338
+ summary: 'Recent platform log events',
339
+ options: pageOpts(),
340
+ run: (ctx, p) => adminGate(() => a(ctx).logs(pageParams(p.options))),
341
+ },
342
+ {
343
+ path: ['admin', 'revenue'],
344
+ summary: 'Platform billing/revenue summary',
345
+ run: (ctx) => adminGate(() => a(ctx).revenue()),
346
+ },
347
+ {
348
+ path: ['admin', 'cron-runs'],
349
+ summary: 'Recent scheduled-maintenance runs',
350
+ options: { limit: { type: 'string', desc: 'Max runs', placeholder: '<n>' } },
351
+ run: (ctx, p) => adminGate(() => a(ctx).cronRuns(optNum(p.options['limit']) !== undefined ? { limit: optNum(p.options['limit']) } : undefined)),
352
+ },
353
+ ];
354
+ }
355
+ function pageOpts(extra = {}) {
356
+ return {
357
+ limit: { type: 'string', desc: 'Page size', placeholder: '<n>' },
358
+ offset: { type: 'string', desc: 'Offset', placeholder: '<n>' },
359
+ ...extra,
360
+ };
361
+ }
362
+ function pageParams(options) {
363
+ return {
364
+ ...(optNum(options['limit']) !== undefined ? { limit: optNum(options['limit']) } : {}),
365
+ ...(optNum(options['offset']) !== undefined ? { offset: optNum(options['offset']) } : {}),
366
+ };
367
+ }
368
+ function registerCoverage() {
369
+ const map = {
370
+ 'admin.listAccounts': 'cs admin account list',
371
+ 'admin.createAccount': 'cs admin account create',
372
+ 'admin.approveAccount': 'cs admin account approve',
373
+ 'admin.provisionAccount': 'cs admin account provision',
374
+ 'admin.accountOverview': 'cs admin account overview',
375
+ 'admin.accountFiles': 'cs admin account files',
376
+ 'admin.accountJobs': 'cs admin account jobs',
377
+ 'admin.accountUsers': 'cs admin account users',
378
+ 'admin.purgeAccountFiles': 'cs admin account purge-files',
379
+ 'admin.getAccountBilling': 'cs admin account billing',
380
+ 'admin.setBillingRate': 'cs admin account billing-rate',
381
+ 'admin.setAiSettings': 'cs admin account ai-settings',
382
+ 'admin.setSurcharge': 'cs admin account surcharge',
383
+ 'admin.storeCredentials': 'cs admin account credentials',
384
+ 'admin.issueToken': 'cs admin token issue',
385
+ 'admin.listTokens': 'cs admin token list',
386
+ 'admin.revokeToken': 'cs admin token revoke',
387
+ 'admin.createCarrier': 'cs admin carrier create',
388
+ 'admin.renameCarrier': 'cs admin carrier rename',
389
+ 'admin.createGlobalConfig': 'cs admin carrier config create',
390
+ 'admin.listCarrierConfigs': 'cs admin carrier config list',
391
+ 'admin.updateCarrierConfig': 'cs admin carrier config update',
392
+ 'admin.inferConfig': 'cs admin carrier config infer',
393
+ 'admin.listUsers': 'cs admin user list',
394
+ 'admin.createUser': 'cs admin user create',
395
+ 'admin.updateUser': 'cs admin user update',
396
+ 'admin.deleteUser': 'cs admin user delete',
397
+ 'admin.addAllowlist': 'cs admin allowlist add',
398
+ 'admin.listJobs': 'cs admin job list',
399
+ 'admin.jobDetail': 'cs admin job get',
400
+ 'admin.retryJob': 'cs admin job retry',
401
+ 'admin.rescoreJob': 'cs admin job rescore',
402
+ 'admin.metrics': 'cs admin metrics',
403
+ 'admin.logs': 'cs admin logs',
404
+ 'admin.revenue': 'cs admin revenue',
405
+ 'admin.cronRuns': 'cs admin cron-runs',
406
+ };
407
+ for (const [method, command] of Object.entries(map))
408
+ cover(method, command);
409
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Auth commands (plan §4.3): login (store + verify), status, logout, and the
3
+ * admin-only `token issue`. Tokens are stored in the config file (0600) and
4
+ * never printed except as a masked suffix.
5
+ */
6
+ import type { Cmd } from '../types.js';
7
+ export declare function authCommands(): Cmd[];
@@ -0,0 +1,107 @@
1
+ import { cover } from './registry.js';
2
+ import { resolveExplicitToken, resolveBaseUrl, resolveToken, clientFor, } from '../context.js';
3
+ import { upsertContext, loadConfig, saveConfig, activeContextName } from '../config/store.js';
4
+ import { DEFAULT_CONTEXT_NAME } from '../config/schema.js';
5
+ import { UsageError } from '../errors.js';
6
+ import { maskToken, optStr } from '../util.js';
7
+ export function authCommands() {
8
+ cover('admin.issueToken', 'cs auth token issue');
9
+ return [
10
+ {
11
+ path: ['auth', 'login'],
12
+ summary: 'Store an API token in a context and verify it via me()',
13
+ options: {
14
+ // token sources + base-url + context are global flags.
15
+ },
16
+ async run(ctx) {
17
+ const { globals, io } = ctx;
18
+ const token = resolveExplicitToken(globals, io);
19
+ if (!token) {
20
+ throw new UsageError('no token provided. pass --token-stdin, --token-file <f>, --token <t>, or set COMMISSIONSIGHT_TOKEN');
21
+ }
22
+ if (globals.token && io.isTTY) {
23
+ io.stderr('warning: --token is visible in shell history; prefer --token-stdin\n');
24
+ }
25
+ const name = globals.context || DEFAULT_CONTEXT_NAME;
26
+ const baseUrl = resolveBaseUrl(globals, io);
27
+ upsertContext(io, name, { baseUrl, token }, { setCurrent: true });
28
+ // Verify against the API with the captured token (avoids re-reading stdin).
29
+ const me = await clientFor(baseUrl, token, io).me();
30
+ ctx.log(`logged in to context "${name}" (${baseUrl})`);
31
+ return {
32
+ context: name,
33
+ baseUrl,
34
+ token: maskToken(token),
35
+ accountId: me.accountId,
36
+ name: me.name,
37
+ status: me.status,
38
+ };
39
+ },
40
+ },
41
+ {
42
+ path: ['auth', 'status'],
43
+ summary: 'Verify the resolved token via me() and show account + context',
44
+ async run(ctx) {
45
+ const { globals, io } = ctx;
46
+ const token = resolveToken(globals, io);
47
+ const baseUrl = resolveBaseUrl(globals, io);
48
+ const config = loadConfig(io);
49
+ const name = activeContextName(io, config, globals.context);
50
+ const me = await ctx.client().me();
51
+ return {
52
+ context: name,
53
+ baseUrl,
54
+ token: maskToken(token),
55
+ accountId: me.accountId,
56
+ name: me.name,
57
+ status: me.status,
58
+ };
59
+ },
60
+ },
61
+ {
62
+ path: ['auth', 'logout'],
63
+ summary: 'Remove the stored token from a context (keep the context)',
64
+ options: {
65
+ all: { type: 'boolean', desc: 'Remove the token from every context' },
66
+ },
67
+ async run(ctx, parsed) {
68
+ const { globals, io } = ctx;
69
+ const config = loadConfig(io);
70
+ if (parsed.options['all'] === true) {
71
+ let cleared = 0;
72
+ for (const c of Object.values(config.contexts)) {
73
+ if (c.token) {
74
+ delete c.token;
75
+ cleared++;
76
+ }
77
+ }
78
+ saveConfig(io, config);
79
+ return { cleared };
80
+ }
81
+ const name = activeContextName(io, config, globals.context);
82
+ const target = config.contexts[name];
83
+ if (!target)
84
+ throw new UsageError(`unknown context: ${name}`);
85
+ const had = Boolean(target.token);
86
+ delete target.token;
87
+ saveConfig(io, config);
88
+ return { context: name, cleared: had ? 1 : 0 };
89
+ },
90
+ },
91
+ {
92
+ path: ['auth', 'token', 'issue'],
93
+ summary: 'Admin-only: issue an API token for an account (printed once)',
94
+ args: [{ name: 'accountId', required: true }],
95
+ options: {
96
+ label: { type: 'string', desc: 'Human label for the token', placeholder: '<l>' },
97
+ },
98
+ async run(ctx, parsed) {
99
+ const accountId = parsed.args[0];
100
+ const label = optStr(parsed.options['label']);
101
+ const res = await ctx.client().admin.issueToken(accountId, label);
102
+ ctx.log('this token is shown once and is not stored — copy it now');
103
+ return res;
104
+ },
105
+ },
106
+ ];
107
+ }
@@ -0,0 +1,2 @@
1
+ import type { Cmd } from '../types.js';
2
+ export declare function batchCommands(): Cmd[];
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `cs upload batch <target>` (plan §6.3) — bulk, idempotent, resumable uploader.
3
+ * Target is a directory, a glob, or a manifest (JSON/CSV). Writes an optional
4
+ * JSON report and respects the --json envelope for the aggregate summary.
5
+ */
6
+ import { writeFileSync } from 'node:fs';
7
+ import { runBatch } from '../lib/batch.js';
8
+ import { CliError, EXIT } from '../errors.js';
9
+ import { optStr, optBool, optNum } from '../util.js';
10
+ export function batchCommands() {
11
+ return [
12
+ {
13
+ path: ['upload', 'batch'],
14
+ summary: 'Bulk-upload statements from a directory, glob, or manifest',
15
+ args: [{ name: 'target', required: false }],
16
+ options: {
17
+ manifest: { type: 'string', desc: 'Manifest file (JSON/CSV) of uploads', placeholder: '<file>' },
18
+ pattern: { type: 'string', desc: 'Discovery regex with <carrier>/<period> groups', placeholder: '<re>' },
19
+ concurrency: { type: 'string', desc: 'Parallel uploads (default 3)', placeholder: '<n>' },
20
+ workspace: { type: 'string', desc: 'Target workspace for all rows', placeholder: '<w>' },
21
+ replace: { type: 'boolean', desc: 'Replace existing carrier+period' },
22
+ wait: { type: 'boolean', desc: 'Poll each job to a terminal state' },
23
+ 'dry-run': { type: 'boolean', desc: 'Plan only; upload nothing' },
24
+ 'continue-on-error': { type: 'boolean', desc: 'Keep going past failures' },
25
+ strict: { type: 'boolean', desc: 'Fail if any file does not match discovery' },
26
+ report: { type: 'string', desc: 'Write the full outcome array to JSON', placeholder: '<out.json>' },
27
+ timeout: { type: 'string', desc: 'Per-job wait timeout seconds (default 300)', placeholder: '<s>' },
28
+ interval: { type: 'string', desc: 'Poll interval seconds (default 2)', placeholder: '<s>' },
29
+ },
30
+ async run(ctx, parsed) {
31
+ const o = parsed.options;
32
+ const opts = {
33
+ target: parsed.args[0] ?? '',
34
+ ...(optStr(o['manifest']) ? { manifest: optStr(o['manifest']) } : {}),
35
+ ...(optStr(o['pattern']) ? { pattern: optStr(o['pattern']) } : {}),
36
+ ...(optNum(o['concurrency']) !== undefined ? { concurrency: optNum(o['concurrency']) } : {}),
37
+ ...(optStr(o['workspace']) ? { workspace: optStr(o['workspace']) } : {}),
38
+ replace: optBool(o['replace']),
39
+ wait: optBool(o['wait']),
40
+ dryRun: optBool(o['dry-run']),
41
+ continueOnError: optBool(o['continue-on-error']),
42
+ strict: optBool(o['strict']),
43
+ timeoutMs: (optNum(o['timeout']) ?? 300) * 1000,
44
+ intervalMs: (optNum(o['interval']) ?? 2) * 1000,
45
+ };
46
+ const summary = await runBatch(ctx, opts);
47
+ const reportPath = optStr(o['report']);
48
+ if (reportPath) {
49
+ writeFileSync(reportPath, JSON.stringify(summary, null, 2) + '\n');
50
+ ctx.log(`report written to ${reportPath}`);
51
+ }
52
+ // Non-zero exit when anything failed (unless continuing past errors).
53
+ if (summary.failed > 0 && !opts.continueOnError) {
54
+ ctx.log(`${summary.failed} upload(s) failed`);
55
+ throw new CliError(`batch completed with ${summary.failed} failure(s)`, EXIT.ERROR);
56
+ }
57
+ return summary;
58
+ },
59
+ render(data, ctx) {
60
+ const s = data;
61
+ ctx.io.stdout(`total ${s.total} uploaded ${s.uploaded} replaced ${s.replaced} skipped ${s.skipped} failed ${s.failed}` +
62
+ (s.unmatched ? ` unmatched ${s.unmatched}` : '') +
63
+ (s.planned ? ` planned ${s.planned}` : '') +
64
+ '\n');
65
+ },
66
+ },
67
+ ];
68
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Billing commands (plan §6.11). Payment-method / setup-intent are web-app-only
3
+ * Stripe flows (non-goal §1) and intentionally not exposed here.
4
+ */
5
+ import type { Cmd } from '../types.js';
6
+ export declare function billingCommands(): Cmd[];