@socialseal/cli 0.1.5 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.6 - 2026-03-19
6
+ - Fix runtime version reporting so `socialseal --version` reads from package metadata instead of a hardcoded source string.
7
+ - Fix `tracking` create request translation so `--workspace-id` is sent on the REST query path the backend uses for workspace binding.
8
+ - Improve tracked-video extraction failure messages by avoiding `[object Object]` item errors and returning explicit guidance when `videoId` is actually a search-result id or tracking item id.
9
+ - Fail fast for `group-management` and `export_tracking_data` when no workspace is selected, instead of silently relying on backend personal-workspace fallback.
10
+ - Warn when `tracking create` runs without a workspace and when short numeric `--video-id` values look like internal row ids.
11
+ - Clarify in workspace discovery output and docs that `workspace_id` and `brand_id` are different identifiers.
12
+
5
13
  ## 0.1.5 - 2026-03-19
6
14
  - Add first-class tracked-video workflows with `video queue-analysis` and `video extract`.
7
15
  - Make `--video-id` the primary ergonomic selector for tracked-video analysis and asset extraction, while keeping `--search-result-id` as a fallback selector.
package/README.md CHANGED
@@ -56,7 +56,7 @@ Optional config file:
56
56
  - `socialseal video extract --body @payload.json --out-dir ./video-assets`
57
57
 
58
58
  - Data exports (provisional):
59
- - `socialseal data export-tracking --group-id 123 --time-period 30d --out out.csv`
59
+ - `socialseal data export-tracking --group-id 123 --time-period 30d --workspace-id <uuid> --out out.csv`
60
60
  - `socialseal data export-report --report-type keyword_universe --format csv --payload @payload.json --out out.csv`
61
61
 
62
62
  ## Notes
@@ -66,11 +66,14 @@ Optional config file:
66
66
  - `search-journey-run` supports CLI-managed async polling: `--async` starts backend async mode, polling is on by default, `--no-poll` returns the initial `runId`, and `--poll-interval <ms>` controls the status polling cadence.
67
67
  - `video queue-analysis` wraps the tracked-video extraction backend in queue-only mode so you can queue one or many tracked videos without downloading assets first.
68
68
  - `video extract` wraps the same backend in extraction mode and returns a normalized JSON payload with resolved tracking context, structured analysis, thumbnail/frame assets, and optional local downloads under `--out-dir`.
69
- - `--video-id` is the primary ergonomic selector for video workflows. The backend tries it as `video_uid` first, then as platform video id. `--search-result-id` remains available when you are starting from a specific tracked rank row.
69
+ - `--video-id` is the primary ergonomic selector for video workflows. The backend tries it as `video_uid` first, then as platform video id. It does not accept tracking item ids. `--search-result-id` remains available when you are starting from a specific tracked rank row.
70
+ - `group-management` and `export_tracking_data` now fail fast when no workspace is selected, instead of letting the backend silently fall back to the personal workspace.
71
+ - `tracking create` without a workspace now prints a warning that the backend may create a personal/null-scope item.
72
+ - Short numeric `--video-id` inputs now print a warning that they may be internal row ids and that `--search-result-id` is often the intended selector.
70
73
  - `socialseal agent run` now defaults to a fresh conversation. The CLI prints a continuation token to `stderr`; pass it back with `--continue <token>` to resume the same agent conversation explicitly.
71
- - Effective workspace precedence is: `--workspace-id` → `SOCIALSEAL_WORKSPACE_ID` → config `workspaceId` backend personal-workspace fallback.
74
+ - Effective workspace precedence is: `--workspace-id` → `SOCIALSEAL_WORKSPACE_ID` → config `workspaceId`. For commands that are easy to misuse (`group-management`, `export_tracking_data`, tracked-video workflows), the CLI now requires an explicit or preconfigured workspace instead of relying on backend fallback.
72
75
  - `socialseal workspace use ...` writes a local default workspace into `~/.config/socialseal/config.json`, which the CLI reuses for `agent`, `tools`, and `data` commands.
73
- - `socialseal workspace list` discovers the workspaces accessible to the current CLI key and marks the active/suggested default.
76
+ - `socialseal workspace list` discovers the workspaces accessible to the current CLI key, marks the active/suggested default, and reminds you that `workspace_id` and `brand_id` are different identifiers.
74
77
  - If a scoped CLI key cannot safely infer a workspace, `agent run` now fails closed and tells you to set `--workspace-id` or configure a local default first.
75
78
 
76
79
  ## Errors and exit codes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socialseal/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "SocialSeal CLI (non-interactive)",
package/src/index.js CHANGED
@@ -16,6 +16,7 @@ const DEFAULT_POLL_INTERVAL_MS = 2000;
16
16
  const DEFAULT_FRAME_COUNT = 3;
17
17
  const MAX_TIMEOUT_MS = 900000;
18
18
  const LEGACY_ENABLED = process.env.SOCIALSEAL_ENABLE_LEGACY === '1';
19
+ const CLI_VERSION = loadRuntimeVersion();
19
20
  const STATIC_TOOL_REGISTRY_NOTE = 'This registry is shipped with the CLI for stable discovery. It is not live backend enumeration, so environment-specific availability can drift.';
20
21
  const EXIT_CODES = {
21
22
  OK: 0,
@@ -61,7 +62,7 @@ const KNOWN_TOOLS = [
61
62
  transport: 'post_edge_function',
62
63
  workspaceScoped: true,
63
64
  knownLocalDevState: 'disabled_by_default',
64
- notes: 'group_id expects a numeric tracking_group id, not a brand_group UUID.',
65
+ notes: 'group_id expects a numeric tracking_group id, not a brand_group UUID. Always pass a workspace id or configure a default workspace so the export does not silently target the personal workspace.',
65
66
  },
66
67
  {
67
68
  name: 'tracked-video-extract',
@@ -71,7 +72,7 @@ const KNOWN_TOOLS = [
71
72
  transport: 'post_edge_function',
72
73
  workspaceScoped: true,
73
74
  knownLocalDevState: 'enabled',
74
- notes: 'Accepts videoId/videoUid/platformVideoId/searchResultId items; can queue analysis and return/download asset URLs.',
75
+ notes: 'Accepts videoId/videoUid/platformVideoId/searchResultId items; videoId means video_uid or platform-native video id, not a tracking item id.',
75
76
  },
76
77
  { name: 'douyin-geo-api', category: 'search', description: 'Query Douyin search and geo data.' },
77
78
  { name: 'google-ai-search', category: 'search', description: 'Run Google AI search queries and fetch result snapshots.' },
@@ -88,7 +89,7 @@ const KNOWN_TOOLS = [
88
89
  workspaceScoped: true,
89
90
  knownLocalDevState: 'disabled_by_default',
90
91
  actionAliases: ['list', 'get', 'create', 'update', 'delete', 'refresh', 'list_items', 'add_item', 'group_add_item', 'add_items', 'group_add_items', 'remove_item', 'group_remove_item'],
91
- notes: 'REST-style surface under /groups. `add_item`/`group_add_item` accepts an existing `item_id`; `add_items`/`group_add_items` accepts `item_ids` or item payloads for bulk membership adds.',
92
+ notes: 'REST-style surface under /groups. `add_item`/`group_add_item` accepts an existing `item_id`; `add_items`/`group_add_items` accepts `item_ids` or item payloads for bulk membership adds. Always pass a workspace id or configure a default workspace so the backend does not fall back to the personal workspace.',
92
93
  },
93
94
  {
94
95
  name: 'tracking',
@@ -123,6 +124,26 @@ function getConfigPath() {
123
124
  return process.env.SOCIALSEAL_CONFIG || DEFAULT_CONFIG_PATH;
124
125
  }
125
126
 
127
+ function loadRuntimeVersion() {
128
+ const envVersion = typeof process.env.npm_package_version === 'string'
129
+ ? process.env.npm_package_version.trim()
130
+ : '';
131
+ if (envVersion) return envVersion;
132
+
133
+ try {
134
+ const packageJsonPath = new URL('../package.json', import.meta.url);
135
+ const raw = fs.readFileSync(packageJsonPath, 'utf8');
136
+ const parsed = JSON.parse(raw);
137
+ if (typeof parsed?.version === 'string' && parsed.version.trim().length > 0) {
138
+ return parsed.version.trim();
139
+ }
140
+ } catch {
141
+ // fall through to the safe fallback below
142
+ }
143
+
144
+ return '0.0.0';
145
+ }
146
+
126
147
  function loadConfig() {
127
148
  const configPath = getConfigPath();
128
149
  try {
@@ -424,6 +445,7 @@ function buildVideoExtractBody(opts, workspaceId) {
424
445
  throw new CliError('Provide --body or one of --video-id, --video-uid, --platform-video-id, or --search-result-id.', {
425
446
  code: 'MISSING_ARGUMENT',
426
447
  exitCode: EXIT_CODES.USAGE,
448
+ hint: '--video-id accepts a video_uid or platform video id. It does not accept tracking item ids.',
427
449
  });
428
450
  }
429
451
 
@@ -933,7 +955,7 @@ function translateTrackingAction(payload, workspaceId) {
933
955
  if (!action) {
934
956
  return {
935
957
  method: 'POST',
936
- pathSuffix: '',
958
+ pathSuffix: buildPathWithQuery('', { workspace_id: workspaceId || undefined }),
937
959
  body: stripUndefinedEntries({
938
960
  name: payload.name,
939
961
  track_type: payload.track_type,
@@ -1013,7 +1035,7 @@ function translateTrackingAction(payload, workspaceId) {
1013
1035
  if (action === 'create' || action === 'item_create') {
1014
1036
  return {
1015
1037
  method: 'POST',
1016
- pathSuffix: '',
1038
+ pathSuffix: buildPathWithQuery('', { workspace_id: workspaceId || undefined }),
1017
1039
  body: stripUndefinedEntries({
1018
1040
  name: payload.name,
1019
1041
  track_type: payload.track_type,
@@ -1480,6 +1502,61 @@ function emitWorkspaceContext(opts, { workspaceId, source, functionName, method
1480
1502
  );
1481
1503
  }
1482
1504
 
1505
+ function describeWorkspaceSource(source) {
1506
+ switch (source) {
1507
+ case 'flag':
1508
+ return '--workspace-id';
1509
+ case 'env':
1510
+ return 'SOCIALSEAL_WORKSPACE_ID';
1511
+ case 'config':
1512
+ return 'the saved default workspace';
1513
+ case 'body':
1514
+ return 'the request body';
1515
+ default:
1516
+ return 'implicit selection';
1517
+ }
1518
+ }
1519
+
1520
+ function emitWorkspaceSelectionNotice(opts, { workspaceId, source, label }) {
1521
+ if (!workspaceId || !source || source === 'flag' || source === 'body') return;
1522
+ process.stderr.write(
1523
+ `[socialseal] Using workspace ${workspaceId} from ${describeWorkspaceSource(source)} for ${label}. Pass --workspace-id to override.\n`,
1524
+ );
1525
+ }
1526
+
1527
+ function requireWorkspaceSelection(workspaceId, { label, hint }) {
1528
+ if (workspaceId) return workspaceId;
1529
+ throw new CliError(`${label} requires a workspace id.`, {
1530
+ code: 'WORKSPACE_REQUIRED',
1531
+ exitCode: EXIT_CODES.USAGE,
1532
+ hint,
1533
+ });
1534
+ }
1535
+
1536
+ function emitTrackingCreateScopeWarning(action, workspaceId) {
1537
+ if (action !== 'create' || workspaceId) return;
1538
+ process.stderr.write(
1539
+ '[socialseal] tracking create is running without a workspace id. The backend may create a personal/null-scope item that is not attached to a workspace or group.\n',
1540
+ );
1541
+ }
1542
+
1543
+ function looksLikeShortNumericVideoId(value) {
1544
+ return typeof value === 'string' && /^\d{1,7}$/.test(value.trim());
1545
+ }
1546
+
1547
+ function emitAmbiguousVideoIdWarnings(items) {
1548
+ const references = Array.isArray(items) ? items : [];
1549
+ for (const item of references) {
1550
+ if (!item || typeof item !== 'object' || Array.isArray(item)) continue;
1551
+ if (looksLikeShortNumericVideoId(item.videoId)) {
1552
+ process.stderr.write(
1553
+ `[socialseal] videoId "${item.videoId}" looks like a short internal row id. If you meant a ranked result row, use --search-result-id. If you meant a tracking item id, resolve it first and retry with --video-uid or --platform-video-id.\n`,
1554
+ );
1555
+ return;
1556
+ }
1557
+ }
1558
+ }
1559
+
1483
1560
  function sleep(ms) {
1484
1561
  return new Promise((resolve) => setTimeout(resolve, ms));
1485
1562
  }
@@ -1629,7 +1706,7 @@ function buildStatusHint(status, context = {}) {
1629
1706
  case 404:
1630
1707
  if (context.functionName) {
1631
1708
  if (isLocallyDisabledByDefaultFunction(context.functionName)) {
1632
- return `Unknown function "${context.functionName}". This tool is listed in the static registry, but it is disabled by default in some local Supabase environments. Check the deployment or enable it in supabase/config.toml.`;
1709
+ return `Unknown function "${context.functionName}". The CLI ships a static registry, but availability depends on the backend you are calling. Verify the tool is deployed on the current API base; for local direct Supabase calls, enable it in supabase/config.toml.`;
1633
1710
  }
1634
1711
  return `Unknown function "${context.functionName}". Double-check the name and API base.`;
1635
1712
  }
@@ -2197,14 +2274,64 @@ async function handleToolsCall(opts) {
2197
2274
  resolvedWorkspaceId,
2198
2275
  });
2199
2276
  const method = normalizeMethod(translated.method);
2200
- const effectiveWorkspaceId = translated.workspaceId ?? resolvedWorkspaceId ?? null;
2277
+ const payloadWorkspaceId = isJsonObject(translated.normalizedPayload)
2278
+ ? resolvePayloadWorkspaceId(translated.normalizedPayload, resolvedWorkspaceId)
2279
+ : (isJsonObject(translated.body)
2280
+ ? resolvePayloadWorkspaceId(translated.body, resolvedWorkspaceId)
2281
+ : (resolvedWorkspaceId ?? null));
2282
+ const effectiveWorkspaceId = translated.workspaceId ?? payloadWorkspaceId ?? null;
2283
+ const effectiveWorkspaceSource =
2284
+ translated.workspaceId && translated.workspaceId !== resolvedWorkspaceId
2285
+ ? 'body'
2286
+ : (payloadWorkspaceId && payloadWorkspaceId !== resolvedWorkspaceId ? 'body' : workspaceSource);
2201
2287
  const path = useGateway
2202
2288
  ? `/cli/tools/${opts.function}${translated.pathSuffix || ''}`
2203
2289
  : `/functions/v1/${opts.function}${translated.pathSuffix || ''}`;
2204
2290
 
2291
+ if (opts.function === 'group-management') {
2292
+ requireWorkspaceSelection(effectiveWorkspaceId, {
2293
+ label: 'group-management',
2294
+ hint: 'Pass --workspace-id, set SOCIALSEAL_WORKSPACE_ID, or configure a default workspace. Omitting workspace lets the backend fall back to the personal workspace.',
2295
+ });
2296
+ emitWorkspaceSelectionNotice(opts, {
2297
+ workspaceId: effectiveWorkspaceId,
2298
+ source: effectiveWorkspaceSource,
2299
+ label: 'group-management',
2300
+ });
2301
+ }
2302
+
2303
+ if (opts.function === 'export_tracking_data') {
2304
+ requireWorkspaceSelection(effectiveWorkspaceId, {
2305
+ label: 'export_tracking_data',
2306
+ hint: 'Pass --workspace-id, set SOCIALSEAL_WORKSPACE_ID, or configure a default workspace before exporting tracking data.',
2307
+ });
2308
+ emitWorkspaceSelectionNotice(opts, {
2309
+ workspaceId: effectiveWorkspaceId,
2310
+ source: effectiveWorkspaceSource,
2311
+ label: 'export_tracking_data',
2312
+ });
2313
+ }
2314
+
2315
+ if (opts.function === 'tracked-video-extract') {
2316
+ requireWorkspaceSelection(effectiveWorkspaceId, {
2317
+ label: 'tracked-video-extract',
2318
+ hint: 'Pass --workspace-id, set SOCIALSEAL_WORKSPACE_ID, or configure a default workspace.',
2319
+ });
2320
+ emitWorkspaceSelectionNotice(opts, {
2321
+ workspaceId: effectiveWorkspaceId,
2322
+ source: effectiveWorkspaceSource,
2323
+ label: 'tracked-video-extract',
2324
+ });
2325
+ }
2326
+
2327
+ emitTrackingCreateScopeWarning(
2328
+ isJsonObject(translated.normalizedPayload) ? trimString(translated.normalizedPayload.action).toLowerCase() : '',
2329
+ effectiveWorkspaceId,
2330
+ );
2331
+
2205
2332
  emitWorkspaceContext(opts, {
2206
2333
  workspaceId: effectiveWorkspaceId,
2207
- source: effectiveWorkspaceId === resolvedWorkspaceId ? workspaceSource : 'body',
2334
+ source: effectiveWorkspaceSource,
2208
2335
  functionName: opts.function,
2209
2336
  method,
2210
2337
  });
@@ -2319,7 +2446,7 @@ async function handleDataExportTracking(opts) {
2319
2446
  const supabaseUrl = resolveLegacyUrl(resolveSupabaseUrl(opts, config), 'SOCIALSEAL_SUPABASE_URL');
2320
2447
  const { resolvedApiBase, legacyUrl, useGateway } = resolveApiTarget({ apiBase, legacyUrl: supabaseUrl });
2321
2448
  const timeoutMs = resolveTimeoutMs(opts, config);
2322
- const { workspaceId: resolvedWorkspaceId } = resolveWorkspaceSelection(opts, config);
2449
+ const { workspaceId: resolvedWorkspaceId, source: workspaceSource } = resolveWorkspaceSelection(opts, config);
2323
2450
 
2324
2451
  if (!opts.groupId && !opts.itemId) {
2325
2452
  throw new CliError('Provide --group-id or --item-id.', {
@@ -2341,6 +2468,16 @@ async function handleDataExportTracking(opts) {
2341
2468
  time_period: opts.timePeriod,
2342
2469
  };
2343
2470
 
2471
+ requireWorkspaceSelection(resolvedWorkspaceId, {
2472
+ label: 'Tracking export',
2473
+ hint: 'Pass --workspace-id, set SOCIALSEAL_WORKSPACE_ID, or configure a default workspace before exporting tracking data.',
2474
+ });
2475
+ emitWorkspaceSelectionNotice(opts, {
2476
+ workspaceId: resolvedWorkspaceId,
2477
+ source: workspaceSource,
2478
+ label: 'tracking export',
2479
+ });
2480
+
2344
2481
  const res = await callApi({
2345
2482
  apiBase: useGateway ? resolvedApiBase : legacyUrl,
2346
2483
  apiKey,
@@ -2445,10 +2582,19 @@ async function handleVideoExtract(opts) {
2445
2582
 
2446
2583
  const body = buildVideoExtractBody(opts, resolvedWorkspaceId);
2447
2584
  const path = useGateway ? '/cli/tools/tracked-video-extract' : '/functions/v1/tracked-video-extract';
2585
+ const effectiveWorkspaceId = body.workspaceId || resolvedWorkspaceId;
2586
+ const effectiveWorkspaceSource = body.workspaceId && body.workspaceId !== resolvedWorkspaceId ? 'body' : workspaceSource;
2587
+
2588
+ emitWorkspaceSelectionNotice(opts, {
2589
+ workspaceId: effectiveWorkspaceId,
2590
+ source: effectiveWorkspaceSource,
2591
+ label: 'tracked-video extract',
2592
+ });
2593
+ emitAmbiguousVideoIdWarnings(body.items);
2448
2594
 
2449
2595
  emitWorkspaceContext(opts, {
2450
- workspaceId: body.workspaceId || resolvedWorkspaceId,
2451
- source: body.workspaceId === resolvedWorkspaceId ? workspaceSource : 'body',
2596
+ workspaceId: effectiveWorkspaceId,
2597
+ source: effectiveWorkspaceSource,
2452
2598
  functionName: 'tracked-video-extract',
2453
2599
  method: 'POST',
2454
2600
  });
@@ -2460,7 +2606,7 @@ async function handleVideoExtract(opts) {
2460
2606
  path,
2461
2607
  method: 'POST',
2462
2608
  body,
2463
- workspaceId: body.workspaceId || resolvedWorkspaceId,
2609
+ workspaceId: effectiveWorkspaceId,
2464
2610
  timeoutMs: remainingTimeoutMs,
2465
2611
  });
2466
2612
 
@@ -2523,11 +2669,20 @@ async function handleVideoQueueAnalysis(opts) {
2523
2669
  const { workspaceId: resolvedWorkspaceId, source: workspaceSource } = resolveWorkspaceSelection(opts, config);
2524
2670
 
2525
2671
  const body = buildVideoQueueBody(opts, resolvedWorkspaceId);
2672
+ const effectiveWorkspaceId = body.workspaceId || resolvedWorkspaceId;
2673
+ const effectiveWorkspaceSource = body.workspaceId && body.workspaceId !== resolvedWorkspaceId ? 'body' : workspaceSource;
2674
+
2675
+ emitWorkspaceSelectionNotice(opts, {
2676
+ workspaceId: effectiveWorkspaceId,
2677
+ source: effectiveWorkspaceSource,
2678
+ label: 'tracked-video queue-analysis',
2679
+ });
2680
+ emitAmbiguousVideoIdWarnings(body.items);
2526
2681
  const path = useGateway ? '/cli/tools/tracked-video-extract' : '/functions/v1/tracked-video-extract';
2527
2682
 
2528
2683
  emitWorkspaceContext(opts, {
2529
- workspaceId: body.workspaceId || resolvedWorkspaceId,
2530
- source: body.workspaceId === resolvedWorkspaceId ? workspaceSource : 'body',
2684
+ workspaceId: effectiveWorkspaceId,
2685
+ source: effectiveWorkspaceSource,
2531
2686
  functionName: 'tracked-video-extract',
2532
2687
  method: 'POST',
2533
2688
  });
@@ -2539,7 +2694,7 @@ async function handleVideoQueueAnalysis(opts) {
2539
2694
  path,
2540
2695
  method: 'POST',
2541
2696
  body,
2542
- workspaceId: body.workspaceId || resolvedWorkspaceId,
2697
+ workspaceId: effectiveWorkspaceId,
2543
2698
  timeoutMs: remainingTimeoutMs,
2544
2699
  });
2545
2700
 
@@ -2624,6 +2779,8 @@ async function handleWorkspaceList(opts) {
2624
2779
  process.stdout.write(`${formatWorkspaceLine(workspace, { isEffective, source: selection.source, isSuggested })}\n`);
2625
2780
  }
2626
2781
 
2782
+ process.stdout.write('\n[socialseal] Note: workspace ids are not brand ids. When a payload includes both workspace_id and brand_id, pass the workspace id to --workspace-id.\n');
2783
+
2627
2784
  if (!selection.workspaceId && directory.defaultWorkspaceId) {
2628
2785
  process.stdout.write('\n[socialseal] No local default is configured. Set one with: socialseal workspace use <id>\n');
2629
2786
  }
@@ -2668,6 +2825,7 @@ async function handleWorkspaceCurrent(opts) {
2668
2825
 
2669
2826
  if (effectiveWorkspace) {
2670
2827
  process.stdout.write(`[socialseal] Effective workspace: ${effectiveWorkspace.name} (${effectiveWorkspace.id}) via ${selection.source}\n`);
2828
+ process.stdout.write('[socialseal] Note: workspace ids are not brand ids. Use the workspace id, not brand_id, with --workspace-id.\n');
2671
2829
  return;
2672
2830
  }
2673
2831
 
@@ -2736,7 +2894,7 @@ const program = new Command();
2736
2894
  program
2737
2895
  .name('socialseal')
2738
2896
  .description('SocialSeal CLI (non-interactive)')
2739
- .version('0.1.1');
2897
+ .version(CLI_VERSION);
2740
2898
 
2741
2899
  if (typeof program.showHelpAfterError === 'function') {
2742
2900
  program.showHelpAfterError(true);
@@ -2874,10 +3032,10 @@ const video = program.command('video').description('Tracked video extraction wor
2874
3032
  video
2875
3033
  .command('queue-analysis')
2876
3034
  .description('Queue video analysis for tracked videos or tracked search results')
2877
- .option('--video-id <id>', 'Tracked video id (tries video_uid first, then platform video id)')
2878
- .option('--search-result-id <id>', 'Tracked search result id')
2879
- .option('--video-uid <id>', 'Tracked video_uid')
2880
- .option('--platform-video-id <id>', 'Tracked platform video id')
3035
+ .option('--video-id <id>', 'Tracked video identifier (video_uid first, then platform video id; not a tracking item id)')
3036
+ .option('--search-result-id <id>', 'Tracked search result id for a ranked result row')
3037
+ .option('--video-uid <id>', 'Canonical tracked video_uid')
3038
+ .option('--platform-video-id <id>', 'Platform-native video id')
2881
3039
  .option('--body <jsonOrFile>', 'JSON body or @payload.json for batch queueing')
2882
3040
  .option('--wait', 'Poll until queued/completing analyses settle')
2883
3041
  .option('--poll-interval <ms>', 'Polling interval in milliseconds when --wait is enabled')
@@ -2893,10 +3051,10 @@ video
2893
3051
  video
2894
3052
  .command('extract')
2895
3053
  .description('Resolve tracked videos/results into structured analysis plus reference assets')
2896
- .option('--video-id <id>', 'Tracked video id (tries video_uid first, then platform video id)')
2897
- .option('--search-result-id <id>', 'Tracked search result id')
2898
- .option('--video-uid <id>', 'Tracked video_uid')
2899
- .option('--platform-video-id <id>', 'Tracked platform video id')
3054
+ .option('--video-id <id>', 'Tracked video identifier (video_uid first, then platform video id; not a tracking item id)')
3055
+ .option('--search-result-id <id>', 'Tracked search result id for a ranked result row')
3056
+ .option('--video-uid <id>', 'Canonical tracked video_uid')
3057
+ .option('--platform-video-id <id>', 'Platform-native video id')
2900
3058
  .option('--body <jsonOrFile>', 'JSON body or @payload.json for batch extraction')
2901
3059
  .option('--ensure-analysis', 'Queue analysis when it is missing')
2902
3060
  .option('--wait', 'Poll until queued/completing analyses settle')