@socialseal/cli 0.1.3 → 0.1.4

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,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.4 - 2026-03-19
6
+ - Add explicit `group_add_item` / `group_add_items` CLI aliases for tracking-group membership workflows.
7
+ - Add `tracking resolve` / `get_by_value` so existing tracked searches can be resolved by value using the same duplicate-detection semantics as create.
8
+ - Return operational duplicate metadata for tracking conflicts, including `existing_item_id`, `member_of_group_ids`, platform, region, workspace, and active state.
9
+
5
10
  ## 0.1.3 - 2026-03-19
6
11
  - Republish the current CLI release line after the successful `0.1.2` npm publish, keeping the internal and OSS package versions aligned.
7
12
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socialseal/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "SocialSeal CLI (non-interactive)",
package/src/index.js CHANGED
@@ -76,8 +76,8 @@ const KNOWN_TOOLS = [
76
76
  transport: 'rest_edge_function',
77
77
  workspaceScoped: true,
78
78
  knownLocalDevState: 'disabled_by_default',
79
- actionAliases: ['list', 'get', 'create', 'update', 'delete', 'refresh', 'list_items'],
80
- notes: 'REST-style surface under /groups. Use action aliases via `tools call` or raw REST semantics.',
79
+ actionAliases: ['list', 'get', 'create', 'update', 'delete', 'refresh', 'list_items', 'add_item', 'group_add_item', 'add_items', 'group_add_items', 'remove_item', 'group_remove_item'],
80
+ 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.',
81
81
  },
82
82
  {
83
83
  name: 'tracking',
@@ -87,8 +87,8 @@ const KNOWN_TOOLS = [
87
87
  transport: 'rest_edge_function',
88
88
  workspaceScoped: true,
89
89
  knownLocalDevState: 'enabled',
90
- actionAliases: ['list', 'get', 'create', 'update', 'delete', 'refresh'],
91
- notes: 'REST-style surface. The CLI normalizes common action payloads for this tool.',
90
+ actionAliases: ['list', 'get', 'resolve', 'get_by_value', 'create', 'update', 'delete', 'refresh'],
91
+ notes: 'REST-style surface. `resolve`/`get_by_value` uses the same workspace/platform/region duplicate-detection lookup as create and returns inactive matches too.',
92
92
  },
93
93
  { name: 'journey-feedback', category: 'vnext', description: 'Record acceptance or rejection feedback for opportunity bundles.' },
94
94
  { name: 'opportunity-bundle-approve', category: 'vnext', description: 'Approve an opportunity bundle and create tracking coverage.' },
@@ -458,18 +458,40 @@ function normalizeTrackingPayload(payload, fallbackWorkspaceId) {
458
458
 
459
459
  function normalizeGroupManagementPayload(payload, fallbackWorkspaceId) {
460
460
  const groupId = firstDefined(payload, ['group_id', 'groupId', 'id']);
461
+ const itemId = firstDefined(payload, ['item_id', 'itemId']);
462
+ const itemIds = firstDefined(payload, ['item_ids', 'itemIds']);
463
+ const items = firstDefined(payload, ['items']);
461
464
  const limit = firstDefined(payload, ['limit']);
462
465
  const page = firstDefined(payload, ['page']);
463
466
  return stripUndefinedEntries({
464
467
  action: trimString(firstDefined(payload, ['action'])) || undefined,
465
468
  workspaceId: resolvePayloadWorkspaceId(payload, fallbackWorkspaceId),
466
469
  group_id: coercePositiveInteger(groupId, 'group_id'),
470
+ item_id: coercePositiveInteger(itemId, 'item_id'),
471
+ item_ids: Array.isArray(itemIds)
472
+ ? itemIds.map((value, index) => {
473
+ const parsed = coercePositiveInteger(value, `item_ids[${index}]`);
474
+ if (!parsed) {
475
+ throw new CliError(`Invalid item_ids[${index}]: expected a positive integer.`, {
476
+ code: 'INVALID_ARGUMENT',
477
+ exitCode: EXIT_CODES.USAGE,
478
+ });
479
+ }
480
+ return parsed;
481
+ })
482
+ : undefined,
483
+ items: Array.isArray(items) ? items : undefined,
467
484
  name: trimString(firstDefined(payload, ['name'])) || undefined,
468
485
  description: firstDefined(payload, ['description']),
469
486
  platform: trimString(firstDefined(payload, ['platform', 'groupPlatform'])) || undefined,
470
487
  refresh_frequency: trimString(firstDefined(payload, ['refresh_frequency', 'refreshFrequency'])) || undefined,
471
488
  next_refresh_at: firstDefined(payload, ['next_refresh_at', 'nextRefreshAt']) ?? undefined,
472
489
  brand_id: trimString(firstDefined(payload, ['brand_id', 'brandId'])) || undefined,
490
+ track_type: normalizeTrackingType(firstDefined(payload, ['track_type', 'trackType', 'type'])),
491
+ track_value: trimString(firstDefined(payload, ['track_value', 'trackValue', 'value'])) || undefined,
492
+ region: typeof firstDefined(payload, ['region']) === 'string'
493
+ ? trimString(firstDefined(payload, ['region'])) || undefined
494
+ : firstDefined(payload, ['region']),
473
495
  limit: limit !== undefined ? Number(limit) : undefined,
474
496
  page: page !== undefined ? Number(page) : undefined,
475
497
  });
@@ -512,6 +534,122 @@ function normalizeTrackingExportPayload(payload, fallbackWorkspaceId) {
512
534
  });
513
535
  }
514
536
 
537
+ function buildGroupAddPayloadFromValue(rawValue, payload, label) {
538
+ const value = trimString(rawValue);
539
+ if (!value) {
540
+ throw new CliError(`Invalid ${label}: expected a non-empty tracking value.`, {
541
+ code: 'INVALID_ARGUMENT',
542
+ exitCode: EXIT_CODES.USAGE,
543
+ });
544
+ }
545
+ if (!payload.track_type) {
546
+ throw new CliError(`${label} requires track_type/type when using raw values.`, {
547
+ code: 'MISSING_ARGUMENT',
548
+ exitCode: EXIT_CODES.USAGE,
549
+ hint: 'Use type=keyword/search, hashtag, or account/creator when adding items by value.',
550
+ });
551
+ }
552
+ return stripUndefinedEntries({
553
+ name: value,
554
+ track_type: payload.track_type,
555
+ track_value: value,
556
+ refresh_frequency: payload.refresh_frequency,
557
+ next_refresh_at: payload.next_refresh_at,
558
+ region: payload.region,
559
+ platform: payload.platform,
560
+ });
561
+ }
562
+
563
+ function buildGroupAddPayloadFromItem(rawItem, payload, label) {
564
+ if (typeof rawItem === 'number') {
565
+ return { item_id: coercePositiveInteger(rawItem, label) };
566
+ }
567
+
568
+ if (typeof rawItem === 'string') {
569
+ const trimmed = rawItem.trim();
570
+ if (/^\d+$/.test(trimmed)) {
571
+ return { item_id: coercePositiveInteger(trimmed, label) };
572
+ }
573
+ return buildGroupAddPayloadFromValue(rawItem, payload, label);
574
+ }
575
+
576
+ if (!rawItem || typeof rawItem !== 'object' || Array.isArray(rawItem)) {
577
+ throw new CliError(`Invalid ${label}: expected an item id, string value, or object payload.`, {
578
+ code: 'INVALID_ARGUMENT',
579
+ exitCode: EXIT_CODES.USAGE,
580
+ });
581
+ }
582
+
583
+ const itemId = coercePositiveInteger(firstDefined(rawItem, ['item_id', 'itemId', 'id']), `${label}.item_id`);
584
+ if (itemId) {
585
+ return { item_id: itemId };
586
+ }
587
+
588
+ const trackValue = trimString(firstDefined(rawItem, ['track_value', 'trackValue', 'value'])) || undefined;
589
+ const name = trimString(firstDefined(rawItem, ['name'])) || trackValue;
590
+ const trackType = normalizeTrackingType(firstDefined(rawItem, ['track_type', 'trackType', 'type'])) || payload.track_type;
591
+ const region = firstDefined(rawItem, ['region']) ?? payload.region;
592
+ const platform = trimString(firstDefined(rawItem, ['platform'])) || payload.platform;
593
+ const refreshFrequency = trimString(firstDefined(rawItem, ['refresh_frequency', 'refreshFrequency'])) || payload.refresh_frequency;
594
+ const nextRefreshAt = firstDefined(rawItem, ['next_refresh_at', 'nextRefreshAt']) ?? payload.next_refresh_at;
595
+
596
+ if (!name || !trackValue || !trackType) {
597
+ throw new CliError(`${label} requires item_id or name/track_value + track_type.`, {
598
+ code: 'MISSING_ARGUMENT',
599
+ exitCode: EXIT_CODES.USAGE,
600
+ });
601
+ }
602
+
603
+ return stripUndefinedEntries({
604
+ name,
605
+ track_type: trackType,
606
+ track_value: trackValue,
607
+ refresh_frequency: refreshFrequency,
608
+ next_refresh_at: nextRefreshAt,
609
+ region,
610
+ platform,
611
+ });
612
+ }
613
+
614
+ function buildSingleGroupAddBody(payload) {
615
+ if (payload.item_id) {
616
+ return { item_id: payload.item_id };
617
+ }
618
+ if (payload.track_value || payload.name) {
619
+ return buildGroupAddPayloadFromItem({
620
+ name: payload.name,
621
+ track_type: payload.track_type,
622
+ track_value: payload.track_value,
623
+ refresh_frequency: payload.refresh_frequency,
624
+ next_refresh_at: payload.next_refresh_at,
625
+ region: payload.region,
626
+ platform: payload.platform,
627
+ }, payload, 'group add item payload');
628
+ }
629
+ throw new CliError('group add_item requires item_id or a tracking payload.', {
630
+ code: 'MISSING_ARGUMENT',
631
+ exitCode: EXIT_CODES.USAGE,
632
+ hint: 'Provide item_id to attach an existing tracking item, or provide track_type + track_value to create/link by value.',
633
+ });
634
+ }
635
+
636
+ function buildBulkGroupAddBody(payload) {
637
+ if (Array.isArray(payload.item_ids) && payload.item_ids.length > 0) {
638
+ return payload.item_ids.map((item_id) => ({ item_id }));
639
+ }
640
+ if (Array.isArray(payload.items) && payload.items.length > 0) {
641
+ return payload.items.map((item, index) => buildGroupAddPayloadFromItem(item, payload, `items[${index}]`));
642
+ }
643
+ if (payload.item_id || payload.track_value || payload.name) {
644
+ return [buildSingleGroupAddBody(payload)];
645
+ }
646
+ throw new CliError('group add_items requires item_ids, items, or a single item payload.', {
647
+ code: 'MISSING_ARGUMENT',
648
+ exitCode: EXIT_CODES.USAGE,
649
+ hint: 'Use item_ids to bulk attach existing tracking items, or use items with track_type/type for value-based adds.',
650
+ });
651
+ }
652
+
515
653
  function translateTrackingAction(payload, workspaceId) {
516
654
  const action = payload.action ? payload.action.toLowerCase() : null;
517
655
  if (!action) {
@@ -572,6 +710,28 @@ function translateTrackingAction(payload, workspaceId) {
572
710
  };
573
711
  }
574
712
 
713
+ if (action === 'resolve' || action === 'item_resolve' || action === 'get_by_value' || action === 'item_get_by_value') {
714
+ if (!payload.track_type || !payload.track_value) {
715
+ throw new CliError('track_type and track_value are required for tracking resolve.', {
716
+ code: 'MISSING_ARGUMENT',
717
+ exitCode: EXIT_CODES.USAGE,
718
+ });
719
+ }
720
+ return {
721
+ method: 'GET',
722
+ pathSuffix: buildPathWithQuery('', {
723
+ workspace_id: workspaceId || undefined,
724
+ resolve: 'true',
725
+ track_type: payload.track_type,
726
+ track_value: payload.track_value,
727
+ platform: payload.platform,
728
+ region: payload.region,
729
+ }),
730
+ body: undefined,
731
+ workspaceId,
732
+ };
733
+ }
734
+
575
735
  if (action === 'create' || action === 'item_create') {
576
736
  return {
577
737
  method: 'POST',
@@ -644,7 +804,7 @@ function translateTrackingAction(payload, workspaceId) {
644
804
  throw new CliError(`Unsupported tracking action: ${payload.action}`, {
645
805
  code: 'INVALID_ARGUMENT',
646
806
  exitCode: EXIT_CODES.USAGE,
647
- hint: 'Supported tracking actions: list, get, create, update, delete, refresh.',
807
+ hint: 'Supported tracking actions: list, get, resolve, create, update, delete, refresh.',
648
808
  });
649
809
  }
650
810
 
@@ -791,10 +951,59 @@ function translateGroupManagementAction(payload, workspaceId, originalMethod) {
791
951
  };
792
952
  }
793
953
 
954
+ if (action === 'add_item' || action === 'group_add_item') {
955
+ const groupId = coercePositiveInteger(payload.group_id, 'group_id');
956
+ if (!groupId) {
957
+ throw new CliError('group_id is required for group add_item.', {
958
+ code: 'MISSING_ARGUMENT',
959
+ exitCode: EXIT_CODES.USAGE,
960
+ });
961
+ }
962
+ return {
963
+ method: 'POST',
964
+ pathSuffix: buildPathWithQuery(`/groups/${groupId}/items`, { workspace_id: workspaceId || undefined }),
965
+ body: buildSingleGroupAddBody(payload),
966
+ workspaceId,
967
+ };
968
+ }
969
+
970
+ if (action === 'add_items' || action === 'group_add_items') {
971
+ const groupId = coercePositiveInteger(payload.group_id, 'group_id');
972
+ if (!groupId) {
973
+ throw new CliError('group_id is required for group add_items.', {
974
+ code: 'MISSING_ARGUMENT',
975
+ exitCode: EXIT_CODES.USAGE,
976
+ });
977
+ }
978
+ return {
979
+ method: 'POST',
980
+ pathSuffix: buildPathWithQuery(`/groups/${groupId}/items/bulk`, { workspace_id: workspaceId || undefined }),
981
+ body: buildBulkGroupAddBody(payload),
982
+ workspaceId,
983
+ };
984
+ }
985
+
986
+ if (action === 'remove_item' || action === 'group_remove_item') {
987
+ const groupId = coercePositiveInteger(payload.group_id, 'group_id');
988
+ const itemId = coercePositiveInteger(payload.item_id, 'item_id');
989
+ if (!groupId || !itemId) {
990
+ throw new CliError('group_id and item_id are required for group remove_item.', {
991
+ code: 'MISSING_ARGUMENT',
992
+ exitCode: EXIT_CODES.USAGE,
993
+ });
994
+ }
995
+ return {
996
+ method: 'DELETE',
997
+ pathSuffix: buildPathWithQuery(`/groups/${groupId}/items/${itemId}`, { workspace_id: workspaceId || undefined }),
998
+ body: undefined,
999
+ workspaceId,
1000
+ };
1001
+ }
1002
+
794
1003
  throw new CliError(`Unsupported group-management action: ${payload.action}`, {
795
1004
  code: 'INVALID_ARGUMENT',
796
1005
  exitCode: EXIT_CODES.USAGE,
797
- hint: 'Supported group-management actions: list, get, create, update, delete, refresh, list_items.',
1006
+ hint: 'Supported group-management actions: list, get, create, update, delete, refresh, list_items, add_item, add_items, remove_item.',
798
1007
  });
799
1008
  }
800
1009