coolhand-cli 0.11.1 → 0.12.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.
- package/README.md +8 -0
- package/dist/api/client-file-client.d.ts +19 -0
- package/dist/api/client-file-client.d.ts.map +1 -0
- package/dist/api/client-file-client.js +47 -0
- package/dist/api/client-file-client.js.map +1 -0
- package/dist/api/help-center-client.d.ts +26 -0
- package/dist/api/help-center-client.d.ts.map +1 -0
- package/dist/api/help-center-client.js +89 -0
- package/dist/api/help-center-client.js.map +1 -0
- package/dist/api/last-sync.d.ts.map +1 -1
- package/dist/api/last-sync.js +2 -1
- package/dist/api/last-sync.js.map +1 -1
- package/dist/api/workload-client.d.ts +16 -0
- package/dist/api/workload-client.d.ts.map +1 -0
- package/dist/api/workload-client.js +28 -0
- package/dist/api/workload-client.js.map +1 -0
- package/dist/auth/callback-server.d.ts.map +1 -1
- package/dist/auth/callback-server.js +13 -1
- package/dist/auth/callback-server.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +429 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/download-client-file.d.ts +9 -0
- package/dist/commands/download-client-file.d.ts.map +1 -0
- package/dist/commands/download-client-file.js +71 -0
- package/dist/commands/download-client-file.js.map +1 -0
- package/dist/commands/fetch-log.d.ts.map +1 -1
- package/dist/commands/fetch-log.js +9 -0
- package/dist/commands/fetch-log.js.map +1 -1
- package/dist/commands/get-help-article.d.ts +3 -0
- package/dist/commands/get-help-article.d.ts.map +1 -0
- package/dist/commands/get-help-article.js +49 -0
- package/dist/commands/get-help-article.js.map +1 -0
- package/dist/commands/get-template.d.ts.map +1 -1
- package/dist/commands/get-template.js +18 -1
- package/dist/commands/get-template.js.map +1 -1
- package/dist/commands/link-feedback.d.ts +5 -0
- package/dist/commands/link-feedback.d.ts.map +1 -0
- package/dist/commands/link-feedback.js +89 -0
- package/dist/commands/link-feedback.js.map +1 -0
- package/dist/commands/list-client-files.d.ts +3 -0
- package/dist/commands/list-client-files.d.ts.map +1 -0
- package/dist/commands/list-client-files.js +55 -0
- package/dist/commands/list-client-files.js.map +1 -0
- package/dist/commands/list-help-categories.d.ts +3 -0
- package/dist/commands/list-help-categories.d.ts.map +1 -0
- package/dist/commands/list-help-categories.js +47 -0
- package/dist/commands/list-help-categories.js.map +1 -0
- package/dist/commands/search-help-articles.d.ts +3 -0
- package/dist/commands/search-help-articles.d.ts.map +1 -0
- package/dist/commands/search-help-articles.js +73 -0
- package/dist/commands/search-help-articles.js.map +1 -0
- package/dist/commands/search-logs.d.ts.map +1 -1
- package/dist/commands/search-logs.js +26 -0
- package/dist/commands/search-logs.js.map +1 -1
- package/dist/commands/search-templates.d.ts.map +1 -1
- package/dist/commands/search-templates.js +12 -0
- package/dist/commands/search-templates.js.map +1 -1
- package/dist/commands/search-workloads.d.ts +3 -0
- package/dist/commands/search-workloads.d.ts.map +1 -0
- package/dist/commands/search-workloads.js +79 -0
- package/dist/commands/search-workloads.js.map +1 -0
- package/dist/commands/update-client-file.d.ts +3 -0
- package/dist/commands/update-client-file.d.ts.map +1 -0
- package/dist/commands/update-client-file.js +39 -0
- package/dist/commands/update-client-file.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/format-cost.d.ts +9 -0
- package/dist/format-cost.d.ts.map +1 -0
- package/dist/format-cost.js +22 -0
- package/dist/format-cost.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +12 -1
- package/dist/logger.js.map +1 -1
- package/dist/sessions/redact-secrets.d.ts.map +1 -1
- package/dist/sessions/redact-secrets.js +9 -3
- package/dist/sessions/redact-secrets.js.map +1 -1
- package/dist/skills/skill-scanner.d.ts.map +1 -1
- package/dist/skills/skill-scanner.js +3 -1
- package/dist/skills/skill-scanner.js.map +1 -1
- package/dist/types.d.ts +86 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/package.json +6 -4
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,9 @@ import { run as runMonitor } from './commands/monitor.js';
|
|
|
16
16
|
import { run as runAnalyzeClaudeSessions } from './commands/analyze-claude-sessions.js';
|
|
17
17
|
import { run as runMapClaudeProjects } from './commands/map-claude-projects.js';
|
|
18
18
|
import { run as runUploadClientFile } from './commands/upload-client-file.js';
|
|
19
|
+
import { run as runListClientFiles } from './commands/list-client-files.js';
|
|
20
|
+
import { run as runDownloadClientFile } from './commands/download-client-file.js';
|
|
21
|
+
import { run as runUpdateClientFile } from './commands/update-client-file.js';
|
|
19
22
|
import { run as runSyncSkills } from './commands/sync-skills.js';
|
|
20
23
|
import { run as runListWorkloads } from './commands/list-workloads.js';
|
|
21
24
|
import { run as runGetWorkload } from './commands/get-workload.js';
|
|
@@ -23,15 +26,20 @@ import { run as runUpdateWorkload } from './commands/update-workload.js';
|
|
|
23
26
|
import { run as runFetchLog } from './commands/fetch-log.js';
|
|
24
27
|
import { run as runSearchLogs } from './commands/search-logs.js';
|
|
25
28
|
import { run as runSearchTemplates } from './commands/search-templates.js';
|
|
29
|
+
import { run as runSearchWorkloads } from './commands/search-workloads.js';
|
|
26
30
|
import { run as runGetTemplate } from './commands/get-template.js';
|
|
27
31
|
import { run as runFlushPending, spawnBackgroundFlush } from './commands/flush-pending.js';
|
|
28
32
|
import { run as runSearchFeedback } from './commands/search-feedback.js';
|
|
29
33
|
import { run as runGetFeedback } from './commands/get-feedback.js';
|
|
34
|
+
import { run as runSearchHelpArticles } from './commands/search-help-articles.js';
|
|
35
|
+
import { run as runGetHelpArticle } from './commands/get-help-article.js';
|
|
36
|
+
import { run as runListHelpCategories } from './commands/list-help-categories.js';
|
|
37
|
+
import { run as runLinkFeedback } from './commands/link-feedback.js';
|
|
30
38
|
import { run as runSearchReferencedFiles } from './commands/search-referenced-files.js';
|
|
31
39
|
import { run as runListReferencedFileSessions } from './commands/list-referenced-file-sessions.js';
|
|
32
40
|
import { countPending, flushFailed } from './pending-store.js';
|
|
33
41
|
import { confirm } from './prompt.js';
|
|
34
|
-
const BOOLEAN_FLAGS = new Set(['all', 'help', 'h', 'json', 'version', 'v', 'dry-run', 'include-archived', 'include-system', 'include-templates', 'full', 'matched', 'unmatched', 'include-thinking', 'unmatched-only', 'include-prompts', 'include-deprecated', 'force']);
|
|
42
|
+
const BOOLEAN_FLAGS = new Set(['all', 'help', 'h', 'json', 'version', 'v', 'dry-run', 'include-archived', 'include-system', 'include-templates', 'full', 'matched', 'unmatched', 'include-thinking', 'unmatched-only', 'include-prompts', 'include-total', 'include-metrics', 'no-metrics', 'include-deprecated', 'force']);
|
|
35
43
|
/** Flags whose repeated occurrences accumulate into an array instead of overwriting. */
|
|
36
44
|
const REPEATABLE_FLAGS = new Set(['project', 'exclude-project', 'skill', 'exclude-skill', 'source']);
|
|
37
45
|
const COMMANDS = [
|
|
@@ -172,6 +180,17 @@ const COMMANDS = [
|
|
|
172
180
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
173
181
|
],
|
|
174
182
|
},
|
|
183
|
+
{
|
|
184
|
+
name: 'link-feedback',
|
|
185
|
+
oneLiner: 'Link feedback records to an optimization in bulk (requires a private key)',
|
|
186
|
+
usage: 'coolhand link-feedback <optimization-id> [feedback-id...] [--file PATH|--file=-] [--note TEXT] [options]',
|
|
187
|
+
options: [
|
|
188
|
+
{ flag: '--file PATH', description: 'Read feedback ids from a file (whitespace/comma separated); use --file=- for stdin' },
|
|
189
|
+
{ flag: '--note TEXT', description: 'Note attached to every link created' },
|
|
190
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
191
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
192
|
+
],
|
|
193
|
+
},
|
|
175
194
|
{
|
|
176
195
|
name: 'get-feedback',
|
|
177
196
|
oneLiner: 'Get a single feedback record by ID (requires a private key)',
|
|
@@ -222,6 +241,40 @@ const COMMANDS = [
|
|
|
222
241
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
223
242
|
],
|
|
224
243
|
},
|
|
244
|
+
{
|
|
245
|
+
name: 'list-client-files',
|
|
246
|
+
oneLiner: "List the client's uploaded files, newest first (requires a client admin's private key)",
|
|
247
|
+
usage: 'coolhand list-client-files [options]',
|
|
248
|
+
options: [
|
|
249
|
+
{ flag: '--status VALUE', description: 'Filter by status: draft, published, or archived' },
|
|
250
|
+
{ flag: '--file-type TYPE', description: 'Filter by type: slide_deck, report, or document' },
|
|
251
|
+
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
252
|
+
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
253
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
254
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'download-client-file',
|
|
259
|
+
oneLiner: "Download a client file's contents to disk (requires a client admin's private key)",
|
|
260
|
+
usage: 'coolhand download-client-file <file-id> [options]',
|
|
261
|
+
options: [
|
|
262
|
+
{ flag: '--output PATH', description: 'Where to write the file; a directory keeps the server-provided filename (default: that filename in the current directory)' },
|
|
263
|
+
{ flag: '--force', description: 'Overwrite the destination if it already exists' },
|
|
264
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
265
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: 'update-client-file',
|
|
270
|
+
oneLiner: "Change a client file's status (requires a client admin's private key)",
|
|
271
|
+
usage: 'coolhand update-client-file <file-id> --status <draft|published|archived> [options]',
|
|
272
|
+
options: [
|
|
273
|
+
{ flag: '--status VALUE', description: 'New status: draft, published, or archived (required)' },
|
|
274
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
275
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
276
|
+
],
|
|
277
|
+
},
|
|
225
278
|
{
|
|
226
279
|
name: 'sync-skills',
|
|
227
280
|
oneLiner: 'Upload locally-built Claude skills (SKILL.md files) to Coolhand as client files',
|
|
@@ -318,8 +371,15 @@ const COMMANDS = [
|
|
|
318
371
|
{ flag: '--source-api VALUE', description: "Filter by source API (e.g. 'openai', 'anthropic', 'vertex')" },
|
|
319
372
|
{ flag: '--source-api-result VALUE', description: 'Filter by result status: success, failed, operational, unmatched' },
|
|
320
373
|
{ flag: '--unmatched-only', description: 'Only return logs with no assigned template' },
|
|
321
|
-
{ flag: '--
|
|
374
|
+
{ flag: '--source-application VALUE', description: 'Filter by exact source_application' },
|
|
375
|
+
{ flag: '--project-path PATH', description: 'Filter by exact metadata.project_path' },
|
|
376
|
+
{ flag: '--days-back N', description: 'Limit to logs created in the last N days (unrestricted if omitted; ignored with --since/--until)' },
|
|
377
|
+
{ flag: '--since TS', description: 'Only logs created at or after this ISO8601 timestamp (replaces --days-back)' },
|
|
378
|
+
{ flag: '--until TS', description: 'Only logs created before this ISO8601 timestamp (exclusive; must be after --since)' },
|
|
379
|
+
{ flag: '--min-cost USD', description: 'Only logs whose cost is at least this many USD (unpriced logs are excluded; 0 is valid)' },
|
|
380
|
+
{ flag: '--order VALUE', description: "Order by cost instead of --sort: 'cost_desc' (highest cost first)" },
|
|
322
381
|
{ flag: '--include-prompts', description: 'Include system_prompt and user_prompt in results (may be large)' },
|
|
382
|
+
{ flag: '--include-total', description: 'Ask the backend to compute the exact total_count/total_pages instead of the cheaper lower-bound estimate (costs a COUNT(*) server-side; off by default)' },
|
|
323
383
|
{ flag: '--sort VALUE', description: "Sort expression, e.g. 'created_at desc' (default: newest first)" },
|
|
324
384
|
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
325
385
|
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
@@ -337,16 +397,44 @@ const COMMANDS = [
|
|
|
337
397
|
{ flag: '--status VALUE', description: 'Filter by status: draft, published, or failure' },
|
|
338
398
|
{ flag: '--include-deprecated', description: 'Include deprecated templates (hidden by default)' },
|
|
339
399
|
{ flag: '--include-system', description: "Include the 'Unmatched' and 'Ignored API Calls' system buckets (hidden by default) — inspect 'Unmatched' when logs are misrouting" },
|
|
400
|
+
{ flag: '--include-metrics', description: 'Add a cost and performance metrics object to each template' },
|
|
401
|
+
{ flag: '--days-back N', description: 'Metrics window: rolling N days ending now (server default 28, max 365; ignored with --since)' },
|
|
402
|
+
{ flag: '--since TS', description: 'Metrics window start, ISO8601 (inclusive; overrides --days-back)' },
|
|
403
|
+
{ flag: '--until TS', description: 'Metrics window end, ISO8601 (exclusive; defaults to now)' },
|
|
340
404
|
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
341
405
|
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
342
406
|
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
343
407
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
344
408
|
],
|
|
345
|
-
},
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: 'search-workloads',
|
|
412
|
+
oneLiner: 'List and search workloads, optionally with cost and performance metrics (requires a private key)',
|
|
413
|
+
usage: 'coolhand search-workloads [options]',
|
|
414
|
+
options: [
|
|
415
|
+
{ flag: '--search TEXT', description: 'Case-insensitive substring to match in the workload name' },
|
|
416
|
+
{ flag: '--include-archived', description: 'Include archived workloads (hidden by default)' },
|
|
417
|
+
{ flag: '--include-system', description: "Include system workloads such as 'Unmatched' (hidden by default)" },
|
|
418
|
+
{ flag: '--include-templates', description: "Add each workload's active templates and their routing patterns" },
|
|
419
|
+
{ flag: '--include-metrics', description: 'Add a cost and performance metrics object to each workload' },
|
|
420
|
+
{ flag: '--days-back N', description: 'Metrics window: rolling N days ending now (server default 28, max 365; ignored with --since)' },
|
|
421
|
+
{ flag: '--since TS', description: 'Metrics window start, ISO8601 (inclusive; overrides --days-back)' },
|
|
422
|
+
{ flag: '--until TS', description: 'Metrics window end, ISO8601 (exclusive; defaults to now)' },
|
|
423
|
+
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
424
|
+
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
425
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
426
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
427
|
+
],
|
|
428
|
+
},
|
|
429
|
+
{
|
|
346
430
|
name: 'get-template',
|
|
347
|
-
oneLiner: 'Get a single LLM request template by ID, including its prompt patterns (requires a private key)',
|
|
431
|
+
oneLiner: 'Get a single LLM request template by ID, including its prompt patterns and metrics (requires a private key)',
|
|
348
432
|
usage: 'coolhand get-template <template-id> [options]',
|
|
349
433
|
options: [
|
|
434
|
+
{ flag: '--no-metrics', description: 'Omit the metrics object (included by default)' },
|
|
435
|
+
{ flag: '--days-back N', description: 'Metrics window: rolling N days ending now (server default 28, max 365; ignored with --since)' },
|
|
436
|
+
{ flag: '--since TS', description: 'Metrics window start, ISO8601 (inclusive; overrides --days-back)' },
|
|
437
|
+
{ flag: '--until TS', description: 'Metrics window end, ISO8601 (exclusive; defaults to now)' },
|
|
350
438
|
{ flag: '--client-id ID', description: 'Use a specific stored client' },
|
|
351
439
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
352
440
|
],
|
|
@@ -375,6 +463,40 @@ const COMMANDS = [
|
|
|
375
463
|
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
376
464
|
],
|
|
377
465
|
},
|
|
466
|
+
{
|
|
467
|
+
name: 'search-help-articles',
|
|
468
|
+
oneLiner: 'Search or list Coolhand help center articles (no login needed)',
|
|
469
|
+
usage: 'coolhand search-help-articles [query] [options]',
|
|
470
|
+
options: [
|
|
471
|
+
{ flag: '--query TEXT', description: 'Relevance search: quoted phrases, -exclusions and "or" work (or pass it as positional text). Omit to list every article' },
|
|
472
|
+
{ flag: '--category SLUG', description: 'Only articles in one section (see list-help-categories)' },
|
|
473
|
+
{ flag: '--page N', description: 'Page number (default: 1)' },
|
|
474
|
+
{ flag: '--per-page N', description: 'Results per page (default: 25, max: 100)' },
|
|
475
|
+
{ flag: '--base-url URL', description: 'Read from another Coolhand server; the stored key is only sent to the server you logged in to' },
|
|
476
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client (optional — reads work anonymously)' },
|
|
477
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
478
|
+
],
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
name: 'get-help-article',
|
|
482
|
+
oneLiner: 'Read one help center article by slug, with its full markdown body (no login needed)',
|
|
483
|
+
usage: 'coolhand get-help-article <slug> [options]',
|
|
484
|
+
options: [
|
|
485
|
+
{ flag: '--base-url URL', description: 'Read from another Coolhand server; the stored key is only sent to the server you logged in to' },
|
|
486
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client (optional — reads work anonymously)' },
|
|
487
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
488
|
+
],
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
name: 'list-help-categories',
|
|
492
|
+
oneLiner: 'List help center sections and their allowed tags (no login needed)',
|
|
493
|
+
usage: 'coolhand list-help-categories [options]',
|
|
494
|
+
options: [
|
|
495
|
+
{ flag: '--base-url URL', description: 'Read from another Coolhand server; the stored key is only sent to the server you logged in to' },
|
|
496
|
+
{ flag: '--client-id ID', description: 'Use a specific stored client (optional — reads work anonymously)' },
|
|
497
|
+
{ flag: '--json', description: 'Emit JSON output instead of human-readable text' },
|
|
498
|
+
],
|
|
499
|
+
},
|
|
378
500
|
{
|
|
379
501
|
name: 'help',
|
|
380
502
|
oneLiner: 'Show this message or per-command help',
|
|
@@ -934,6 +1056,145 @@ function fetchLogOptions(parsed) {
|
|
|
934
1056
|
}
|
|
935
1057
|
return opts;
|
|
936
1058
|
}
|
|
1059
|
+
const CLIENT_FILE_STATUSES = new Set(['draft', 'published', 'archived']);
|
|
1060
|
+
function parseClientFileStatus(raw) {
|
|
1061
|
+
if (!CLIENT_FILE_STATUSES.has(raw)) {
|
|
1062
|
+
throw new CliError('INVALID_ARGS', '--status must be one of: draft, published, archived');
|
|
1063
|
+
}
|
|
1064
|
+
return raw;
|
|
1065
|
+
}
|
|
1066
|
+
function listClientFilesOptions(parsed) {
|
|
1067
|
+
const opts = {};
|
|
1068
|
+
if (typeof parsed.flags['status'] === 'string') {
|
|
1069
|
+
opts.status = parseClientFileStatus(parsed.flags['status']);
|
|
1070
|
+
}
|
|
1071
|
+
if (typeof parsed.flags['file-type'] === 'string') {
|
|
1072
|
+
if (!CLIENT_FILE_TYPES.has(parsed.flags['file-type'])) {
|
|
1073
|
+
throw new CliError('INVALID_ARGS', '--file-type must be one of: slide_deck, report, document');
|
|
1074
|
+
}
|
|
1075
|
+
opts.fileType = parsed.flags['file-type'];
|
|
1076
|
+
}
|
|
1077
|
+
if (typeof parsed.flags['page'] === 'string') {
|
|
1078
|
+
const raw = parsed.flags['page'];
|
|
1079
|
+
const n = parseInt(raw, 10);
|
|
1080
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1) {
|
|
1081
|
+
throw new CliError('INVALID_ARGS', '--page must be a positive integer');
|
|
1082
|
+
}
|
|
1083
|
+
opts.page = n;
|
|
1084
|
+
}
|
|
1085
|
+
if (typeof parsed.flags['per-page'] === 'string') {
|
|
1086
|
+
const raw = parsed.flags['per-page'];
|
|
1087
|
+
const n = parseInt(raw, 10);
|
|
1088
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1 || n > 100) {
|
|
1089
|
+
throw new CliError('INVALID_ARGS', '--per-page must be an integer between 1 and 100');
|
|
1090
|
+
}
|
|
1091
|
+
opts.perPage = n;
|
|
1092
|
+
}
|
|
1093
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1094
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1095
|
+
}
|
|
1096
|
+
if (parsed.flags.json === true) {
|
|
1097
|
+
opts.json = true;
|
|
1098
|
+
}
|
|
1099
|
+
return opts;
|
|
1100
|
+
}
|
|
1101
|
+
function downloadClientFileOptions(parsed) {
|
|
1102
|
+
const id = parsed.positional[0];
|
|
1103
|
+
if (!id) {
|
|
1104
|
+
throw new CliError('INVALID_ARGS', 'download-client-file requires a <file-id> argument');
|
|
1105
|
+
}
|
|
1106
|
+
const opts = { id };
|
|
1107
|
+
if ('output' in parsed.flags) {
|
|
1108
|
+
const v = parsed.flags['output'];
|
|
1109
|
+
if (typeof v !== 'string' || v.length === 0) {
|
|
1110
|
+
throw new CliError('INVALID_ARGS', '--output requires a path argument');
|
|
1111
|
+
}
|
|
1112
|
+
opts.output = v;
|
|
1113
|
+
}
|
|
1114
|
+
if (parsed.flags['force'] === true) {
|
|
1115
|
+
opts.force = true;
|
|
1116
|
+
}
|
|
1117
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1118
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1119
|
+
}
|
|
1120
|
+
if (parsed.flags.json === true) {
|
|
1121
|
+
opts.json = true;
|
|
1122
|
+
}
|
|
1123
|
+
return opts;
|
|
1124
|
+
}
|
|
1125
|
+
function updateClientFileOptions(parsed) {
|
|
1126
|
+
const id = parsed.positional[0];
|
|
1127
|
+
if (!id) {
|
|
1128
|
+
throw new CliError('INVALID_ARGS', 'update-client-file requires a <file-id> argument');
|
|
1129
|
+
}
|
|
1130
|
+
const rawStatus = parsed.flags['status'];
|
|
1131
|
+
if (typeof rawStatus !== 'string') {
|
|
1132
|
+
throw new CliError('INVALID_ARGS', 'update-client-file requires --status (draft, published, or archived)');
|
|
1133
|
+
}
|
|
1134
|
+
const opts = { id, status: parseClientFileStatus(rawStatus) };
|
|
1135
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1136
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1137
|
+
}
|
|
1138
|
+
if (parsed.flags.json === true) {
|
|
1139
|
+
opts.json = true;
|
|
1140
|
+
}
|
|
1141
|
+
return opts;
|
|
1142
|
+
}
|
|
1143
|
+
/** Strict positive integer (`/^\d+$/`, `>= 1`), as `--days-back` has always been parsed. */
|
|
1144
|
+
function positiveIntFlag(parsed, name) {
|
|
1145
|
+
const raw = parsed.flags[name];
|
|
1146
|
+
if (raw === undefined) {
|
|
1147
|
+
return undefined;
|
|
1148
|
+
}
|
|
1149
|
+
const n = typeof raw === 'string' ? parseInt(raw, 10) : NaN;
|
|
1150
|
+
if (typeof raw !== 'string' || !/^\d+$/.test(raw) || isNaN(n) || n < 1) {
|
|
1151
|
+
throw new CliError('INVALID_ARGS', `--${name} must be a positive integer`);
|
|
1152
|
+
}
|
|
1153
|
+
return n;
|
|
1154
|
+
}
|
|
1155
|
+
/** `--per-page`: strict integer between 1 and 100. */
|
|
1156
|
+
function perPageFlag(parsed) {
|
|
1157
|
+
const raw = parsed.flags['per-page'];
|
|
1158
|
+
if (raw === undefined) {
|
|
1159
|
+
return undefined;
|
|
1160
|
+
}
|
|
1161
|
+
const n = typeof raw === 'string' ? parseInt(raw, 10) : NaN;
|
|
1162
|
+
if (typeof raw !== 'string' || !/^\d+$/.test(raw) || isNaN(n) || n < 1 || n > 100) {
|
|
1163
|
+
throw new CliError('INVALID_ARGS', '--per-page must be an integer between 1 and 100');
|
|
1164
|
+
}
|
|
1165
|
+
return n;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* ISO8601 `--since`/`--until`: passed straight through, never parsed into a `Date` locally — the
|
|
1169
|
+
* server validates it and a 422 surfaces as a `CliError` with its message.
|
|
1170
|
+
*/
|
|
1171
|
+
function timestampFlag(parsed, name) {
|
|
1172
|
+
const raw = parsed.flags[name];
|
|
1173
|
+
if (raw === undefined) {
|
|
1174
|
+
return undefined;
|
|
1175
|
+
}
|
|
1176
|
+
if (typeof raw !== 'string' || raw.length === 0) {
|
|
1177
|
+
throw new CliError('INVALID_ARGS', `--${name} requires an ISO8601 timestamp`);
|
|
1178
|
+
}
|
|
1179
|
+
return raw;
|
|
1180
|
+
}
|
|
1181
|
+
/** The metrics window flags shared by search-templates, get-template and search-workloads. */
|
|
1182
|
+
function metricsWindow(parsed) {
|
|
1183
|
+
const window = {};
|
|
1184
|
+
const daysBack = positiveIntFlag(parsed, 'days-back');
|
|
1185
|
+
if (daysBack !== undefined) {
|
|
1186
|
+
window.daysBack = daysBack;
|
|
1187
|
+
}
|
|
1188
|
+
const since = timestampFlag(parsed, 'since');
|
|
1189
|
+
if (since !== undefined) {
|
|
1190
|
+
window.since = since;
|
|
1191
|
+
}
|
|
1192
|
+
const until = timestampFlag(parsed, 'until');
|
|
1193
|
+
if (until !== undefined) {
|
|
1194
|
+
window.until = until;
|
|
1195
|
+
}
|
|
1196
|
+
return window;
|
|
1197
|
+
}
|
|
937
1198
|
const TEMPLATE_STATUSES = new Set(['draft', 'published', 'failure']);
|
|
938
1199
|
function searchTemplatesOptions(parsed) {
|
|
939
1200
|
const opts = {};
|
|
@@ -956,6 +1217,10 @@ function searchTemplatesOptions(parsed) {
|
|
|
956
1217
|
if (parsed.flags['include-system'] === true) {
|
|
957
1218
|
opts.includeSystem = true;
|
|
958
1219
|
}
|
|
1220
|
+
if (parsed.flags['include-metrics'] === true) {
|
|
1221
|
+
opts.includeMetrics = true;
|
|
1222
|
+
}
|
|
1223
|
+
Object.assign(opts, metricsWindow(parsed));
|
|
959
1224
|
if (typeof parsed.flags['page'] === 'string') {
|
|
960
1225
|
const raw = parsed.flags['page'];
|
|
961
1226
|
const n = parseInt(raw, 10);
|
|
@@ -980,12 +1245,63 @@ function searchTemplatesOptions(parsed) {
|
|
|
980
1245
|
}
|
|
981
1246
|
return opts;
|
|
982
1247
|
}
|
|
1248
|
+
function searchWorkloadsOptions(parsed) {
|
|
1249
|
+
const opts = {};
|
|
1250
|
+
if (typeof parsed.flags['search'] === 'string') {
|
|
1251
|
+
opts.search = parsed.flags['search'];
|
|
1252
|
+
}
|
|
1253
|
+
if (parsed.flags['include-archived'] === true) {
|
|
1254
|
+
opts.includeArchived = true;
|
|
1255
|
+
}
|
|
1256
|
+
if (parsed.flags['include-system'] === true) {
|
|
1257
|
+
opts.includeSystem = true;
|
|
1258
|
+
}
|
|
1259
|
+
if (parsed.flags['include-templates'] === true) {
|
|
1260
|
+
opts.includeTemplates = true;
|
|
1261
|
+
}
|
|
1262
|
+
if (parsed.flags['include-metrics'] === true) {
|
|
1263
|
+
opts.includeMetrics = true;
|
|
1264
|
+
}
|
|
1265
|
+
Object.assign(opts, metricsWindow(parsed));
|
|
1266
|
+
const page = positiveIntFlag(parsed, 'page');
|
|
1267
|
+
if (page !== undefined) {
|
|
1268
|
+
opts.page = page;
|
|
1269
|
+
}
|
|
1270
|
+
const perPage = perPageFlag(parsed);
|
|
1271
|
+
if (perPage !== undefined) {
|
|
1272
|
+
opts.perPage = perPage;
|
|
1273
|
+
}
|
|
1274
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1275
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1276
|
+
}
|
|
1277
|
+
if (parsed.flags.json === true) {
|
|
1278
|
+
opts.json = true;
|
|
1279
|
+
}
|
|
1280
|
+
return opts;
|
|
1281
|
+
}
|
|
983
1282
|
function getTemplateOptions(parsed) {
|
|
984
1283
|
const id = parsed.positional[0];
|
|
985
1284
|
if (!id) {
|
|
986
1285
|
throw new CliError('INVALID_ARGS', 'get-template requires a <template-id> argument');
|
|
987
1286
|
}
|
|
988
1287
|
const opts = { id };
|
|
1288
|
+
if (parsed.flags['no-metrics'] === true) {
|
|
1289
|
+
opts.noMetrics = true;
|
|
1290
|
+
}
|
|
1291
|
+
Object.assign(opts, metricsWindow(parsed));
|
|
1292
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1293
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1294
|
+
}
|
|
1295
|
+
if (parsed.flags.json === true) {
|
|
1296
|
+
opts.json = true;
|
|
1297
|
+
}
|
|
1298
|
+
return opts;
|
|
1299
|
+
}
|
|
1300
|
+
function helpCenterReadOptions(parsed) {
|
|
1301
|
+
const opts = {};
|
|
1302
|
+
if (typeof parsed.flags['base-url'] === 'string') {
|
|
1303
|
+
opts.baseUrl = parsed.flags['base-url'];
|
|
1304
|
+
}
|
|
989
1305
|
if (typeof parsed.flags['client-id'] === 'string') {
|
|
990
1306
|
opts.clientId = parsed.flags['client-id'];
|
|
991
1307
|
}
|
|
@@ -994,6 +1310,47 @@ function getTemplateOptions(parsed) {
|
|
|
994
1310
|
}
|
|
995
1311
|
return opts;
|
|
996
1312
|
}
|
|
1313
|
+
function searchHelpArticlesOptions(parsed) {
|
|
1314
|
+
const opts = helpCenterReadOptions(parsed);
|
|
1315
|
+
if (parsed.flags['query'] === true) {
|
|
1316
|
+
throw new CliError('INVALID_ARGS', '--query requires a value (attach a leading-dash query as --query=-text)');
|
|
1317
|
+
}
|
|
1318
|
+
const positionalQuery = parsed.positional.join(' ');
|
|
1319
|
+
if (typeof parsed.flags['query'] === 'string' && positionalQuery.trim()) {
|
|
1320
|
+
throw new CliError('INVALID_ARGS', 'Pass the search text either as --query or as positional text, not both');
|
|
1321
|
+
}
|
|
1322
|
+
const query = typeof parsed.flags['query'] === 'string' ? parsed.flags['query'] : positionalQuery;
|
|
1323
|
+
if (query.trim()) {
|
|
1324
|
+
opts.query = query;
|
|
1325
|
+
}
|
|
1326
|
+
if (typeof parsed.flags['category'] === 'string') {
|
|
1327
|
+
opts.category = parsed.flags['category'];
|
|
1328
|
+
}
|
|
1329
|
+
if (typeof parsed.flags['page'] === 'string') {
|
|
1330
|
+
const raw = parsed.flags['page'];
|
|
1331
|
+
const n = parseInt(raw, 10);
|
|
1332
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1) {
|
|
1333
|
+
throw new CliError('INVALID_ARGS', '--page must be a positive integer');
|
|
1334
|
+
}
|
|
1335
|
+
opts.page = n;
|
|
1336
|
+
}
|
|
1337
|
+
if (typeof parsed.flags['per-page'] === 'string') {
|
|
1338
|
+
const raw = parsed.flags['per-page'];
|
|
1339
|
+
const n = parseInt(raw, 10);
|
|
1340
|
+
if (!/^\d+$/.test(raw) || isNaN(n) || n < 1 || n > 100) {
|
|
1341
|
+
throw new CliError('INVALID_ARGS', '--per-page must be an integer between 1 and 100');
|
|
1342
|
+
}
|
|
1343
|
+
opts.perPage = n;
|
|
1344
|
+
}
|
|
1345
|
+
return opts;
|
|
1346
|
+
}
|
|
1347
|
+
function getHelpArticleOptions(parsed) {
|
|
1348
|
+
const slug = parsed.positional[0];
|
|
1349
|
+
if (!slug) {
|
|
1350
|
+
throw new CliError('INVALID_ARGS', 'get-help-article requires a <slug> argument');
|
|
1351
|
+
}
|
|
1352
|
+
return { ...helpCenterReadOptions(parsed), slug };
|
|
1353
|
+
}
|
|
997
1354
|
function searchLogsOptions(parsed) {
|
|
998
1355
|
const opts = {};
|
|
999
1356
|
if (typeof parsed.flags['template-id'] === 'string') {
|
|
@@ -1028,9 +1385,38 @@ function searchLogsOptions(parsed) {
|
|
|
1028
1385
|
}
|
|
1029
1386
|
opts.daysBack = n;
|
|
1030
1387
|
}
|
|
1388
|
+
if (typeof parsed.flags['source-application'] === 'string') {
|
|
1389
|
+
opts.sourceApplication = parsed.flags['source-application'];
|
|
1390
|
+
}
|
|
1391
|
+
if (typeof parsed.flags['project-path'] === 'string') {
|
|
1392
|
+
opts.projectPath = parsed.flags['project-path'];
|
|
1393
|
+
}
|
|
1394
|
+
const since = timestampFlag(parsed, 'since');
|
|
1395
|
+
if (since !== undefined) {
|
|
1396
|
+
opts.since = since;
|
|
1397
|
+
}
|
|
1398
|
+
const until = timestampFlag(parsed, 'until');
|
|
1399
|
+
if (until !== undefined) {
|
|
1400
|
+
opts.until = until;
|
|
1401
|
+
}
|
|
1402
|
+
if (parsed.flags['min-cost'] !== undefined) {
|
|
1403
|
+
const raw = parsed.flags['min-cost'];
|
|
1404
|
+
// `0` is a valid floor and must reach the server, so compare against undefined, not falsiness.
|
|
1405
|
+
if (typeof raw !== 'string' || !/^\d+(\.\d+)?$/.test(raw)) {
|
|
1406
|
+
throw new CliError('INVALID_ARGS', '--min-cost must be a non-negative decimal number (USD)');
|
|
1407
|
+
}
|
|
1408
|
+
opts.minCost = parseFloat(raw);
|
|
1409
|
+
}
|
|
1410
|
+
if (typeof parsed.flags['order'] === 'string') {
|
|
1411
|
+
// Not allowlisted here: the server validates it and a 422 surfaces as a CliError.
|
|
1412
|
+
opts.order = parsed.flags['order'];
|
|
1413
|
+
}
|
|
1031
1414
|
if (parsed.flags['include-prompts'] === true) {
|
|
1032
1415
|
opts.includePrompts = true;
|
|
1033
1416
|
}
|
|
1417
|
+
if (parsed.flags['include-total'] === true) {
|
|
1418
|
+
opts.includeTotal = true;
|
|
1419
|
+
}
|
|
1034
1420
|
if (typeof parsed.flags['sort'] === 'string') {
|
|
1035
1421
|
opts.sort = parsed.flags['sort'];
|
|
1036
1422
|
}
|
|
@@ -1229,6 +1615,29 @@ function searchFeedbackOptions(parsed) {
|
|
|
1229
1615
|
}
|
|
1230
1616
|
return opts;
|
|
1231
1617
|
}
|
|
1618
|
+
function linkFeedbackOptions(parsed) {
|
|
1619
|
+
const optimizationId = parsed.positional[0];
|
|
1620
|
+
if (!optimizationId) {
|
|
1621
|
+
throw new CliError('INVALID_ARGS', 'link-feedback requires an <optimization-id> argument');
|
|
1622
|
+
}
|
|
1623
|
+
const opts = { optimizationId, feedbackIds: parsed.positional.slice(1) };
|
|
1624
|
+
if (typeof parsed.flags.file === 'string') {
|
|
1625
|
+
opts.file = parsed.flags.file;
|
|
1626
|
+
}
|
|
1627
|
+
else if (parsed.flags.file === true) {
|
|
1628
|
+
throw new CliError('INVALID_ARGS', 'link-feedback --file requires a path; use --file=- to read ids from stdin');
|
|
1629
|
+
}
|
|
1630
|
+
if (typeof parsed.flags.note === 'string') {
|
|
1631
|
+
opts.note = parsed.flags.note;
|
|
1632
|
+
}
|
|
1633
|
+
if (typeof parsed.flags['client-id'] === 'string') {
|
|
1634
|
+
opts.clientId = parsed.flags['client-id'];
|
|
1635
|
+
}
|
|
1636
|
+
if (parsed.flags.json === true) {
|
|
1637
|
+
opts.json = true;
|
|
1638
|
+
}
|
|
1639
|
+
return opts;
|
|
1640
|
+
}
|
|
1232
1641
|
function getFeedbackOptions(parsed) {
|
|
1233
1642
|
const id = parsed.positional[0];
|
|
1234
1643
|
if (!id) {
|
|
@@ -1400,6 +1809,8 @@ export async function run(argv) {
|
|
|
1400
1809
|
return await runWildcard(wildcardOptions(parsed));
|
|
1401
1810
|
case 'search-feedback':
|
|
1402
1811
|
return await runSearchFeedback(searchFeedbackOptions(parsed));
|
|
1812
|
+
case 'link-feedback':
|
|
1813
|
+
return await runLinkFeedback(linkFeedbackOptions(parsed));
|
|
1403
1814
|
case 'get-feedback':
|
|
1404
1815
|
return await runGetFeedback(getFeedbackOptions(parsed));
|
|
1405
1816
|
case 'analyze-claude-sessions':
|
|
@@ -1408,6 +1819,12 @@ export async function run(argv) {
|
|
|
1408
1819
|
return await runMapClaudeProjects(mapClaudeProjectsOptions(parsed));
|
|
1409
1820
|
case 'upload-client-file':
|
|
1410
1821
|
return await runUploadClientFile(uploadClientFileOptions(parsed));
|
|
1822
|
+
case 'list-client-files':
|
|
1823
|
+
return await runListClientFiles(listClientFilesOptions(parsed));
|
|
1824
|
+
case 'download-client-file':
|
|
1825
|
+
return await runDownloadClientFile(downloadClientFileOptions(parsed));
|
|
1826
|
+
case 'update-client-file':
|
|
1827
|
+
return await runUpdateClientFile(updateClientFileOptions(parsed));
|
|
1411
1828
|
case 'sync-skills':
|
|
1412
1829
|
return await runSyncSkills(syncSkillsOptions(parsed));
|
|
1413
1830
|
case 'list-workloads':
|
|
@@ -1422,8 +1839,16 @@ export async function run(argv) {
|
|
|
1422
1839
|
return await runSearchLogs(searchLogsOptions(parsed));
|
|
1423
1840
|
case 'search-templates':
|
|
1424
1841
|
return await runSearchTemplates(searchTemplatesOptions(parsed));
|
|
1842
|
+
case 'search-workloads':
|
|
1843
|
+
return await runSearchWorkloads(searchWorkloadsOptions(parsed));
|
|
1425
1844
|
case 'get-template':
|
|
1426
1845
|
return await runGetTemplate(getTemplateOptions(parsed));
|
|
1846
|
+
case 'search-help-articles':
|
|
1847
|
+
return await runSearchHelpArticles(searchHelpArticlesOptions(parsed));
|
|
1848
|
+
case 'get-help-article':
|
|
1849
|
+
return await runGetHelpArticle(getHelpArticleOptions(parsed));
|
|
1850
|
+
case 'list-help-categories':
|
|
1851
|
+
return await runListHelpCategories(helpCenterReadOptions(parsed));
|
|
1427
1852
|
case 'search-referenced-files':
|
|
1428
1853
|
return await runSearchReferencedFiles(searchReferencedFilesOptions(parsed));
|
|
1429
1854
|
case 'list-referenced-file-sessions':
|