@duvoai/cli 0.8.0 → 0.9.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/dist/bin/duvo.js CHANGED
@@ -1974,6 +1974,11 @@ var DEFAULT_AGENT_CONFIG = {
1974
1974
  browserProvider: DEFAULT_BROWSER_PROVIDER
1975
1975
  }
1976
1976
  };
1977
+ var BUILD_STATUS = {
1978
+ DRAFT: "draft",
1979
+ LIVE: "live",
1980
+ HISTORIC: "historic"
1981
+ };
1977
1982
  var CASE_QUEUE_ITEM_STATUS = {
1978
1983
  PENDING: "pending",
1979
1984
  CLAIMED: "claimed",
@@ -2628,6 +2633,7 @@ var clarityProcessListItemSchema = z12.object({
2628
2633
  vulnerabilityCount: z12.number().optional(),
2629
2634
  contributors: z12.array(z12.object({ userId: z12.string(), userName: z12.string() })).optional(),
2630
2635
  createdAt: z12.coerce.date(),
2636
+ updatedAt: z12.coerce.date(),
2631
2637
  createdByName: z12.string().optional()
2632
2638
  });
2633
2639
  var clarityProcessListResponseSchema = z12.object({
@@ -3541,6 +3547,12 @@ function parseAskUserQuestionInput(raw) {
3541
3547
  }
3542
3548
  __name(parseAskUserQuestionInput, "parseAskUserQuestionInput");
3543
3549
  __name2(parseAskUserQuestionInput, "parseAskUserQuestionInput");
3550
+ var clarityProcessChatConversationStatusSchema = z18.enum([
3551
+ "open",
3552
+ "running",
3553
+ "waiting_for_patch",
3554
+ "waiting_for_hitl"
3555
+ ]);
3544
3556
  var clarityProcessChatMessageRoleSchema = z18.enum([
3545
3557
  "user",
3546
3558
  "assistant",
@@ -3554,6 +3566,12 @@ var clarityProcessChatMessageKindSchema = z18.enum([
3554
3566
  "error",
3555
3567
  "info"
3556
3568
  ]);
3569
+ var clarityProcessChatPatchStatusSchema = z18.enum([
3570
+ "pending",
3571
+ "accepted",
3572
+ "declined",
3573
+ "failed"
3574
+ ]);
3557
3575
  var artifactChatJsonPrimitiveSchema = z18.union([
3558
3576
  z18.boolean(),
3559
3577
  z18.number(),
@@ -3567,6 +3585,9 @@ var artifactChatJsonValueSchema = z18.lazy(
3567
3585
  z18.record(z18.string(), artifactChatJsonValueSchema)
3568
3586
  ])
3569
3587
  );
3588
+ z18.globalRegistry.add(artifactChatJsonValueSchema, {
3589
+ id: "ArtifactChatJsonValue"
3590
+ });
3570
3591
  var jsonPatchOperationSchema = z18.union([
3571
3592
  z18.object({
3572
3593
  op: z18.enum(["add", "replace", "test"]),
@@ -3583,44 +3604,72 @@ var jsonPatchOperationSchema = z18.union([
3583
3604
  path: z18.string()
3584
3605
  })
3585
3606
  ]);
3586
- var clarityProcessChatPatchStatusSchema = z18.enum([
3587
- "pending",
3588
- "accepted",
3589
- "declined",
3590
- "failed"
3607
+ var clarityProcessChatConversationSchema = z18.object({
3608
+ id: z18.guid(),
3609
+ user_id: z18.guid(),
3610
+ process_id: z18.guid(),
3611
+ snapshot_kind: claritySnapshotKindSchema,
3612
+ title: z18.string().nullable(),
3613
+ sandbox_id: z18.string().nullable(),
3614
+ session_id: z18.string().nullable(),
3615
+ status: clarityProcessChatConversationStatusSchema,
3616
+ created_at: z18.string(),
3617
+ updated_at: z18.string(),
3618
+ deleted_at: z18.string().nullable()
3619
+ });
3620
+ var clarityProcessChatMessageBodySchema = z18.discriminatedUnion("kind", [
3621
+ z18.object({
3622
+ kind: z18.literal("text"),
3623
+ text: z18.string()
3624
+ }),
3625
+ z18.object({
3626
+ kind: z18.literal("patch"),
3627
+ status: clarityProcessChatPatchStatusSchema,
3628
+ summary: z18.string(),
3629
+ patch: z18.array(jsonPatchOperationSchema).readonly(),
3630
+ snapshot_id: z18.guid().nullable()
3631
+ }),
3632
+ z18.object({
3633
+ kind: z18.literal("hitl_question"),
3634
+ question: askUserQuestionSchema
3635
+ }),
3636
+ z18.object({
3637
+ kind: z18.literal("hitl_answer"),
3638
+ answers: z18.record(z18.string(), z18.string()).readonly()
3639
+ }),
3640
+ z18.object({
3641
+ kind: z18.literal("error"),
3642
+ text: z18.string()
3643
+ }),
3644
+ z18.object({
3645
+ kind: z18.literal("info"),
3646
+ text: z18.string()
3647
+ })
3591
3648
  ]);
3592
- var clarityProcessChatMessageBodySchema = z18.discriminatedUnion(
3593
- "kind",
3594
- [
3595
- z18.object({
3596
- kind: z18.literal("text"),
3597
- text: z18.string()
3598
- }),
3599
- z18.object({
3600
- kind: z18.literal("patch"),
3601
- status: clarityProcessChatPatchStatusSchema,
3602
- summary: z18.string(),
3603
- patch: z18.array(jsonPatchOperationSchema).readonly(),
3604
- snapshot_id: z18.guid().nullable()
3605
- }),
3606
- z18.object({
3607
- kind: z18.literal("hitl_question"),
3608
- question: askUserQuestionSchema
3609
- }),
3610
- z18.object({
3611
- kind: z18.literal("hitl_answer"),
3612
- answers: z18.record(z18.string(), z18.string()).readonly()
3613
- }),
3614
- z18.object({
3615
- kind: z18.literal("error"),
3616
- text: z18.string()
3617
- }),
3618
- z18.object({
3619
- kind: z18.literal("info"),
3620
- text: z18.string()
3621
- })
3622
- ]
3623
- );
3649
+ var clarityProcessChatMessageSchema = z18.object({
3650
+ id: z18.guid(),
3651
+ conversation_id: z18.guid(),
3652
+ role: clarityProcessChatMessageRoleSchema,
3653
+ kind: clarityProcessChatMessageKindSchema,
3654
+ body: clarityProcessChatMessageBodySchema,
3655
+ baseline_snapshot_id: z18.guid(),
3656
+ created_at: z18.string(),
3657
+ updated_at: z18.string()
3658
+ });
3659
+ var listArtifactChatConversationsQuerySchema = z18.object({
3660
+ snapshot_kind: claritySnapshotKindSchema
3661
+ });
3662
+ var listArtifactChatConversationsResponseSchema = z18.object({
3663
+ conversations: z18.array(clarityProcessChatConversationSchema)
3664
+ });
3665
+ var getArtifactChatMessagesResponseSchema = z18.object({
3666
+ conversation: clarityProcessChatConversationSchema,
3667
+ messages: z18.array(clarityProcessChatMessageSchema)
3668
+ });
3669
+ var artifactChatMessageUpsertEventDataSchema = z18.object({
3670
+ conversation: clarityProcessChatConversationSchema,
3671
+ message: clarityProcessChatMessageSchema
3672
+ });
3624
3673
  var MAX_PROCESS_GUIDANCE_PROMPT_CHARS = 500;
3625
3674
  function removeControlCharacters(value) {
3626
3675
  return [...value].map((character) => {
@@ -5680,10 +5729,10 @@ var caseQueueSchema = z43.object({
5680
5729
  folder_id: z43.guid().nullable(),
5681
5730
  created_at: z43.string(),
5682
5731
  /**
5683
- * True when at least one case in this queue has ever had a
5684
- * case_approval_request (pending, resolved, or cancelled). The frontend
5685
- * uses this to relabel the "Needs input" filter to "Needs input/approval"
5686
- * and to show the per-case approvals column.
5732
+ * True when at least one case in this queue has ever had an approval
5733
+ * batch (open, resolved, or cancelled). The frontend uses this to relabel
5734
+ * the "Needs input" filter to "Needs input/approval" and to show the
5735
+ * per-case approvals column.
5687
5736
  */
5688
5737
  has_approvals: z43.boolean()
5689
5738
  });
@@ -7711,6 +7760,21 @@ function isLabelHue(value) {
7711
7760
  }
7712
7761
  __name(isLabelHue, "isLabelHue");
7713
7762
  __name2(isLabelHue, "isLabelHue");
7763
+ function imageMimeTypeToExtension(mimeType) {
7764
+ switch (mimeType) {
7765
+ case "image/jpeg":
7766
+ case "image/jpg":
7767
+ return "jpg";
7768
+ case "image/png":
7769
+ return "png";
7770
+ case "image/webp":
7771
+ return "webp";
7772
+ default:
7773
+ return "bin";
7774
+ }
7775
+ }
7776
+ __name(imageMimeTypeToExtension, "imageMimeTypeToExtension");
7777
+ __name2(imageMimeTypeToExtension, "imageMimeTypeToExtension");
7714
7778
  function generateMcpServerLabel(integrationType, instanceId) {
7715
7779
  if (!instanceId) {
7716
7780
  return integrationType;
@@ -10812,15 +10876,44 @@ function parseTypedField(input) {
10812
10876
  return { key, value: raw };
10813
10877
  }
10814
10878
  __name(parseTypedField, "parseTypedField");
10879
+ function assignField(result, key, value) {
10880
+ if (!key.endsWith("[]")) {
10881
+ if (Array.isArray(result[key])) {
10882
+ throw new Error(
10883
+ `Conflicting field "${key}": it was already set as an array via "${key}[]=\u2026". Use one syntax \u2014 either repeated "${key}[]=\u2026" for an array, or a single "${key}=\u2026" for a scalar.`
10884
+ );
10885
+ }
10886
+ result[key] = value;
10887
+ return;
10888
+ }
10889
+ const base = key.slice(0, -2);
10890
+ if (base.length === 0) {
10891
+ throw new Error(
10892
+ `Invalid field "${key}=\u2026". Array key cannot be empty (e.g. -F "ids[]=42").`
10893
+ );
10894
+ }
10895
+ const existing = result[base];
10896
+ if (Array.isArray(existing)) {
10897
+ existing.push(value);
10898
+ return;
10899
+ }
10900
+ if (existing !== void 0) {
10901
+ throw new Error(
10902
+ `Conflicting field "${base}[]": "${base}" was already set as a scalar. Use one syntax \u2014 either repeated "${base}[]=\u2026" for an array, or a single "${base}=\u2026" for a scalar.`
10903
+ );
10904
+ }
10905
+ result[base] = [value];
10906
+ }
10907
+ __name(assignField, "assignField");
10815
10908
  function buildFieldObject(stringFields = [], typedFields = []) {
10816
10909
  const result = {};
10817
10910
  for (const input of stringFields) {
10818
10911
  const { key, value } = parseStringField(input);
10819
- result[key] = value;
10912
+ assignField(result, key, value);
10820
10913
  }
10821
10914
  for (const input of typedFields) {
10822
10915
  const { key, value } = parseTypedField(input);
10823
- result[key] = value;
10916
+ assignField(result, key, value);
10824
10917
  }
10825
10918
  return result;
10826
10919
  }
@@ -11274,6 +11367,94 @@ function registerBuildIntegrationsList(buildIntegrations) {
11274
11367
  }
11275
11368
  __name(registerBuildIntegrationsList, "registerBuildIntegrationsList");
11276
11369
 
11370
+ // src/commands/build-integrations/queues-list.ts
11371
+ var TABLE_COLUMNS9 = [
11372
+ { header: "QUEUE ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11373
+ { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") }
11374
+ ];
11375
+ function registerBuildIntegrationsQueuesList(queues) {
11376
+ queues.command("list", { isDefault: true }).description(
11377
+ "List the case queues linked to a revision's case-queue integration slot"
11378
+ ).requiredOption("--agent <id>", "ID of the agent the revision belongs to").requiredOption("--revision <id>", "ID of the revision").requiredOption(
11379
+ "--integration <id>",
11380
+ "ID of the case-queue integration slot"
11381
+ ).option("--json", "output raw JSON from the API").action(/* @__PURE__ */ __name(async function listAction(options) {
11382
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
11383
+ const raw = await httpRequest({
11384
+ method: "GET",
11385
+ path: `/v1/agents/${encodeURIComponent(options.agent)}/revisions/${encodeURIComponent(options.revision)}/integrations/${encodeURIComponent(options.integration)}/queues`,
11386
+ apiKey,
11387
+ apiBaseUrl
11388
+ });
11389
+ if (options.json) {
11390
+ printJson(raw);
11391
+ return;
11392
+ }
11393
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
11394
+ if (!parsed.success) {
11395
+ throw new CliError(
11396
+ `Unexpected response from the API: ${parsed.error.message}`
11397
+ );
11398
+ }
11399
+ if (parsed.data.queues.length === 0) {
11400
+ console.log("No case queues linked to this integration slot.");
11401
+ return;
11402
+ }
11403
+ console.log(renderTable(TABLE_COLUMNS9, parsed.data.queues));
11404
+ console.log(`Showing ${parsed.data.queues.length}`);
11405
+ }, "listAction"));
11406
+ }
11407
+ __name(registerBuildIntegrationsQueuesList, "registerBuildIntegrationsQueuesList");
11408
+
11409
+ // src/commands/build-integrations/queues-set.ts
11410
+ var TABLE_COLUMNS10 = [
11411
+ { header: "QUEUE ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11412
+ { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") }
11413
+ ];
11414
+ function collectMultiOption2(value, previous) {
11415
+ return [...previous ?? [], value];
11416
+ }
11417
+ __name(collectMultiOption2, "collectMultiOption");
11418
+ function registerBuildIntegrationsQueuesSet(queues) {
11419
+ queues.command("set").description(
11420
+ "Replace the full set of case queues linked to a revision's case-queue integration slot. Omit --queue to unlink all."
11421
+ ).requiredOption("--agent <id>", "ID of the agent the revision belongs to").requiredOption("--revision <id>", "ID of the revision").requiredOption(
11422
+ "--integration <id>",
11423
+ "ID of the case-queue integration slot"
11424
+ ).option(
11425
+ "--queue <id>",
11426
+ "queue ID to link (repeat for multiple; omit to unlink all)",
11427
+ collectMultiOption2,
11428
+ []
11429
+ ).option("--json", "output raw JSON from the API").action(/* @__PURE__ */ __name(async function setAction(options) {
11430
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
11431
+ const raw = await httpRequest({
11432
+ method: "PUT",
11433
+ path: `/v1/agents/${encodeURIComponent(options.agent)}/revisions/${encodeURIComponent(options.revision)}/integrations/${encodeURIComponent(options.integration)}/queues`,
11434
+ apiKey,
11435
+ apiBaseUrl,
11436
+ body: { queue_ids: options.queue }
11437
+ });
11438
+ if (options.json) {
11439
+ printJson(raw);
11440
+ return;
11441
+ }
11442
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
11443
+ if (!parsed.success) {
11444
+ throw new CliError(
11445
+ `Unexpected response from the API: ${parsed.error.message}`
11446
+ );
11447
+ }
11448
+ console.log(
11449
+ green(`Linked ${parsed.data.queues.length} case queue(s) to the slot.`)
11450
+ );
11451
+ if (parsed.data.queues.length > 0) {
11452
+ console.log(renderTable(TABLE_COLUMNS10, parsed.data.queues));
11453
+ }
11454
+ }, "setAction"));
11455
+ }
11456
+ __name(registerBuildIntegrationsQueuesSet, "registerBuildIntegrationsQueuesSet");
11457
+
11277
11458
  // src/commands/build-integrations/remove.ts
11278
11459
  function registerBuildIntegrationsRemove(buildIntegrations) {
11279
11460
  buildIntegrations.command("remove <integration-id>").description(
@@ -11317,6 +11498,11 @@ function registerBuildIntegrations(program) {
11317
11498
  registerBuildIntegrationsConnectionsList(connections);
11318
11499
  registerBuildIntegrationsConnectionsPin(connections);
11319
11500
  registerBuildIntegrationsConnectionsUnpin(connections);
11501
+ const queues = buildIntegrations.command("queues").description(
11502
+ "Inspect and replace the case queues linked to a revision's case-queue integration slot"
11503
+ );
11504
+ registerBuildIntegrationsQueuesList(queues);
11505
+ registerBuildIntegrationsQueuesSet(queues);
11320
11506
  }
11321
11507
  __name(registerBuildIntegrations, "registerBuildIntegrations");
11322
11508
 
@@ -11341,6 +11527,7 @@ var publicBuildSchema2 = z71.object({
11341
11527
  id: z71.string(),
11342
11528
  agent_id: z71.string(),
11343
11529
  name: z71.string(),
11530
+ status: z71.string().nullish(),
11344
11531
  config: jsonValueSchema.nullish(),
11345
11532
  revision_number: z71.number().nullish(),
11346
11533
  created_at: z71.string().nullish(),
@@ -11451,7 +11638,7 @@ __name(registerBuildsGet, "registerBuildsGet");
11451
11638
  var DEFAULT_LIMIT2 = 20;
11452
11639
  var MIN_LIMIT2 = 1;
11453
11640
  var MAX_LIMIT2 = 100;
11454
- var TABLE_COLUMNS9 = [
11641
+ var TABLE_COLUMNS11 = [
11455
11642
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11456
11643
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
11457
11644
  {
@@ -11494,7 +11681,7 @@ function registerBuildsList(builds) {
11494
11681
  console.log("No revisions found.");
11495
11682
  return;
11496
11683
  }
11497
- console.log(renderTable(TABLE_COLUMNS9, response.builds));
11684
+ console.log(renderTable(TABLE_COLUMNS11, response.builds));
11498
11685
  const resolvedOffset = response.offset ?? offset;
11499
11686
  const total = response.total ?? null;
11500
11687
  const summary = total === null ? `Showing ${response.builds.length} \xB7 offset ${resolvedOffset}` : `Showing ${response.builds.length} of ${total} \xB7 offset ${resolvedOffset}`;
@@ -11965,7 +12152,7 @@ __name(fetchQueueCaseCount, "fetchQueueCaseCount");
11965
12152
 
11966
12153
  // src/commands/cases/create.ts
11967
12154
  import { readFile as readFile4 } from "node:fs/promises";
11968
- var TABLE_COLUMNS10 = [
12155
+ var TABLE_COLUMNS12 = [
11969
12156
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11970
12157
  { header: "TITLE", cell: /* @__PURE__ */ __name((row) => row.title, "cell"), maxWidth: 50 },
11971
12158
  { header: "STATUS", cell: /* @__PURE__ */ __name((row) => row.status, "cell") }
@@ -12008,7 +12195,7 @@ function registerCasesCreate(cases) {
12008
12195
  )
12009
12196
  );
12010
12197
  console.log();
12011
- console.log(renderTable(TABLE_COLUMNS10, addedCases));
12198
+ console.log(renderTable(TABLE_COLUMNS12, addedCases));
12012
12199
  }, "createAction"));
12013
12200
  }
12014
12201
  __name(registerCasesCreate, "registerCasesCreate");
@@ -12243,7 +12430,7 @@ __name(registerCasesGet, "registerCasesGet");
12243
12430
 
12244
12431
  // src/commands/cases/labels.ts
12245
12432
  var MAX_LABELS_PER_REQUEST = 50;
12246
- var TABLE_COLUMNS11 = [
12433
+ var TABLE_COLUMNS13 = [
12247
12434
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
12248
12435
  { header: "KEY", cell: /* @__PURE__ */ __name((row) => row.key, "cell") },
12249
12436
  { header: "VALUE", cell: /* @__PURE__ */ __name((row) => row.value, "cell") },
@@ -12273,7 +12460,7 @@ function registerCasesLabels(cases) {
12273
12460
  console.log("No labels assigned.");
12274
12461
  return;
12275
12462
  }
12276
- console.log(renderTable(TABLE_COLUMNS11, parsed.data.labels));
12463
+ console.log(renderTable(TABLE_COLUMNS13, parsed.data.labels));
12277
12464
  console.log(`Showing ${parsed.data.labels.length}`);
12278
12465
  }, "listAction"));
12279
12466
  labels.command("assign <case-id>").description(
@@ -12313,24 +12500,40 @@ function registerCasesLabels(cases) {
12313
12500
  );
12314
12501
  }
12315
12502
  console.log(green("Labels assigned!"));
12316
- console.log(renderTable(TABLE_COLUMNS11, parsed.data.labels));
12503
+ console.log(renderTable(TABLE_COLUMNS13, parsed.data.labels));
12317
12504
  }, "assignAction"));
12318
- labels.command("unlink <case-id>").description("Remove one or more labels from a case").requiredOption("--queue <id>", "ID of the queue the case belongs to").requiredOption(
12505
+ labels.command("unlink <case-id>").description(
12506
+ "Remove one or more labels from a case (by --label-id or --label key=value, for symmetry with assign)"
12507
+ ).requiredOption("--queue <id>", "ID of the queue the case belongs to").option(
12319
12508
  "--label-id <id>",
12320
- "label ID to unlink (repeatable, at least one required)",
12509
+ "label ID to unlink (repeatable)",
12510
+ collectRepeatable,
12511
+ []
12512
+ ).option(
12513
+ "--label <kv>",
12514
+ 'label as "key=value" or "value" for tag-only (repeatable); resolved to an ID against the labels currently on the case',
12321
12515
  collectRepeatable,
12322
12516
  []
12323
12517
  ).option("--json", "output raw JSON from the API").action(/* @__PURE__ */ __name(async function unlinkAction(caseId, options) {
12324
- const labelIds = options.labelId ?? [];
12325
- if (labelIds.length === 0) {
12326
- throw new CliError("Pass at least one --label-id");
12518
+ const directIds = options.labelId ?? [];
12519
+ const labelInputs = options.label ?? [];
12520
+ if (directIds.length === 0 && labelInputs.length === 0) {
12521
+ throw new CliError("Pass at least one --label-id or --label");
12327
12522
  }
12523
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
12524
+ const resolvedIds = labelInputs.length > 0 ? await resolveLabelIdsFromCase(
12525
+ caseId,
12526
+ options.queue,
12527
+ labelInputs,
12528
+ apiKey,
12529
+ apiBaseUrl
12530
+ ) : [];
12531
+ const labelIds = [.../* @__PURE__ */ new Set([...directIds, ...resolvedIds])];
12328
12532
  if (labelIds.length > MAX_LABELS_PER_REQUEST) {
12329
12533
  throw new CliError(
12330
- `Pass at most ${MAX_LABELS_PER_REQUEST} --label-id values per request`
12534
+ `Pass at most ${MAX_LABELS_PER_REQUEST} labels per request`
12331
12535
  );
12332
12536
  }
12333
- const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
12334
12537
  const raw = await httpRequest({
12335
12538
  method: "POST",
12336
12539
  path: `/v1/queues/${encodeURIComponent(options.queue)}/cases/${encodeURIComponent(caseId)}/labels/unlink`,
@@ -12359,6 +12562,33 @@ function collectRepeatable(value, previous) {
12359
12562
  return [...previous, value];
12360
12563
  }
12361
12564
  __name(collectRepeatable, "collectRepeatable");
12565
+ async function resolveLabelIdsFromCase(caseId, queueId, labelInputs, apiKey, apiBaseUrl) {
12566
+ const raw = await httpRequest({
12567
+ method: "GET",
12568
+ path: `/v1/queues/${encodeURIComponent(queueId)}/cases/${encodeURIComponent(caseId)}/labels`,
12569
+ apiKey,
12570
+ apiBaseUrl
12571
+ });
12572
+ const parsed = caseLabelsListResponseSchema.safeParse(raw);
12573
+ if (!parsed.success) {
12574
+ throw new CliError(
12575
+ `Unexpected response from the API: ${parsed.error.message}`
12576
+ );
12577
+ }
12578
+ return labelInputs.map((input) => {
12579
+ const { key, value } = parseLabel(input);
12580
+ const match = parsed.data.labels.find(
12581
+ (label) => label.key === key && label.value === value
12582
+ );
12583
+ if (!match) {
12584
+ throw new CliError(
12585
+ `No label "${input}" is currently assigned to this case.`
12586
+ );
12587
+ }
12588
+ return match.id;
12589
+ });
12590
+ }
12591
+ __name(resolveLabelIdsFromCase, "resolveLabelIdsFromCase");
12362
12592
  function parseLabel(input) {
12363
12593
  const eq = input.indexOf("=");
12364
12594
  if (eq === -1) {
@@ -12388,7 +12618,7 @@ var MIN_LIMIT3 = 1;
12388
12618
  var MAX_LIMIT3 = 100;
12389
12619
  var SORT_FIELDS = ["created_at", "updated_at", "postponed_to"];
12390
12620
  var SORT_ORDERS = ["asc", "desc"];
12391
- var TABLE_COLUMNS12 = [
12621
+ var TABLE_COLUMNS14 = [
12392
12622
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
12393
12623
  { header: "TITLE", cell: /* @__PURE__ */ __name((row) => row.title, "cell"), maxWidth: 50 },
12394
12624
  { header: "STATUS", cell: /* @__PURE__ */ __name((row) => row.status, "cell") },
@@ -12461,7 +12691,7 @@ function registerCasesList(cases) {
12461
12691
  console.log("No cases found.");
12462
12692
  return;
12463
12693
  }
12464
- console.log(renderTable(TABLE_COLUMNS12, response.cases));
12694
+ console.log(renderTable(TABLE_COLUMNS14, response.cases));
12465
12695
  const resolvedOffset = response.offset ?? offset;
12466
12696
  const total = response.total ?? null;
12467
12697
  const summary = total === null ? `Showing ${response.cases.length} \xB7 offset ${resolvedOffset}` : `Showing ${response.cases.length} of ${total} \xB7 offset ${resolvedOffset}`;
@@ -13850,7 +14080,7 @@ __name(registerClarityGet, "registerClarityGet");
13850
14080
  var DEFAULT_LIMIT5 = 20;
13851
14081
  var MIN_LIMIT5 = 1;
13852
14082
  var MAX_LIMIT5 = 100;
13853
- var TABLE_COLUMNS13 = [
14083
+ var TABLE_COLUMNS15 = [
13854
14084
  {
13855
14085
  header: "ID",
13856
14086
  cell: /* @__PURE__ */ __name((row) => row.id, "cell")
@@ -13911,7 +14141,7 @@ function registerClarityListExtraCaptureRequests(clarity) {
13911
14141
  console.log("No extra-capture requests found.");
13912
14142
  return;
13913
14143
  }
13914
- console.log(renderTable(TABLE_COLUMNS13, response.requests));
14144
+ console.log(renderTable(TABLE_COLUMNS15, response.requests));
13915
14145
  console.log(
13916
14146
  `Showing ${response.requests.length} of ${response.total} \xB7 offset ${response.offset}`
13917
14147
  );
@@ -15615,7 +15845,7 @@ function registerConnectionsGet(connections) {
15615
15845
  __name(registerConnectionsGet, "registerConnectionsGet");
15616
15846
 
15617
15847
  // src/commands/connections/list.ts
15618
- var TABLE_COLUMNS14 = [
15848
+ var TABLE_COLUMNS16 = [
15619
15849
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
15620
15850
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
15621
15851
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
@@ -15651,7 +15881,7 @@ function registerConnectionsList(connections) {
15651
15881
  console.log("No connections found.");
15652
15882
  return;
15653
15883
  }
15654
- console.log(renderTable(TABLE_COLUMNS14, rows));
15884
+ console.log(renderTable(TABLE_COLUMNS16, rows));
15655
15885
  console.log(`Showing ${rows.length}`);
15656
15886
  }, "listAction"));
15657
15887
  }
@@ -15670,7 +15900,7 @@ var probeResponseSchema = z77.object({
15670
15900
  error: z77.string().optional()
15671
15901
  });
15672
15902
  var DESCRIPTION_MAX_WIDTH = 80;
15673
- var TABLE_COLUMNS15 = [
15903
+ var TABLE_COLUMNS17 = [
15674
15904
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
15675
15905
  {
15676
15906
  header: "DESCRIPTION",
@@ -15726,7 +15956,7 @@ function registerConnectionsProbe(connections) {
15726
15956
  console.log("No tools found.");
15727
15957
  return;
15728
15958
  }
15729
- console.log(renderTable(TABLE_COLUMNS15, tools));
15959
+ console.log(renderTable(TABLE_COLUMNS17, tools));
15730
15960
  console.log(
15731
15961
  `Showing ${tools.length} tool${tools.length === 1 ? "" : "s"}`
15732
15962
  );
@@ -16111,7 +16341,7 @@ function registerCredentialsGet(credentials) {
16111
16341
  __name(registerCredentialsGet, "registerCredentialsGet");
16112
16342
 
16113
16343
  // src/commands/credentials/list.ts
16114
- var TABLE_COLUMNS16 = [
16344
+ var TABLE_COLUMNS18 = [
16115
16345
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
16116
16346
  { header: "DOMAIN", cell: /* @__PURE__ */ __name((row) => row.domain, "cell") },
16117
16347
  {
@@ -16166,7 +16396,7 @@ function registerCredentialsList(credentials) {
16166
16396
  console.log("No credentials found.");
16167
16397
  return;
16168
16398
  }
16169
- console.log(renderTable(TABLE_COLUMNS16, rows));
16399
+ console.log(renderTable(TABLE_COLUMNS18, rows));
16170
16400
  console.log(`Showing ${rows.length}`);
16171
16401
  }, "listAction"));
16172
16402
  }
@@ -16426,7 +16656,7 @@ function registerFilesDownloadUrl(files) {
16426
16656
  __name(registerFilesDownloadUrl, "registerFilesDownloadUrl");
16427
16657
 
16428
16658
  // src/commands/files/list.ts
16429
- var TABLE_COLUMNS17 = [
16659
+ var TABLE_COLUMNS19 = [
16430
16660
  { header: "PATH", cell: /* @__PURE__ */ __name((row) => row.path, "cell") },
16431
16661
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
16432
16662
  {
@@ -16465,7 +16695,7 @@ function registerFilesList(files) {
16465
16695
  console.log("No files found.");
16466
16696
  return;
16467
16697
  }
16468
- console.log(renderTable(TABLE_COLUMNS17, parsed.data.files));
16698
+ console.log(renderTable(TABLE_COLUMNS19, parsed.data.files));
16469
16699
  console.log(`Showing ${parsed.data.files.length}`);
16470
16700
  }, "listAction"));
16471
16701
  }
@@ -16715,7 +16945,7 @@ function registerIntegrationsCustom(integrations) {
16715
16945
  __name(registerIntegrationsCustom, "registerIntegrationsCustom");
16716
16946
 
16717
16947
  // src/commands/integrations/list.ts
16718
- var TABLE_COLUMNS18 = [
16948
+ var TABLE_COLUMNS20 = [
16719
16949
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
16720
16950
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
16721
16951
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
@@ -16749,7 +16979,7 @@ function registerIntegrationsList(integrations) {
16749
16979
  console.log("No integrations found.");
16750
16980
  return;
16751
16981
  }
16752
- console.log(renderTable(TABLE_COLUMNS18, rows));
16982
+ console.log(renderTable(TABLE_COLUMNS20, rows));
16753
16983
  console.log(`Showing ${rows.length}`);
16754
16984
  }, "listAction"));
16755
16985
  }
@@ -17576,7 +17806,7 @@ function parseHeaders(raw) {
17576
17806
  return result;
17577
17807
  }
17578
17808
  __name(parseHeaders, "parseHeaders");
17579
- var TABLE_COLUMNS19 = [
17809
+ var TABLE_COLUMNS21 = [
17580
17810
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
17581
17811
  { header: "DESCRIPTION", cell: /* @__PURE__ */ __name((row) => row.description, "cell") }
17582
17812
  ];
@@ -17630,7 +17860,7 @@ function registerOauthMcpProbe(mcp) {
17630
17860
  console.log("No tools advertised by this MCP server.");
17631
17861
  return;
17632
17862
  }
17633
- console.log(renderTable(TABLE_COLUMNS19, rows));
17863
+ console.log(renderTable(TABLE_COLUMNS21, rows));
17634
17864
  console.log(`Showing ${rows.length}`);
17635
17865
  }, "probeMcpAction"));
17636
17866
  }
@@ -17705,7 +17935,7 @@ function registerOauth(program) {
17705
17935
  __name(registerOauth, "registerOauth");
17706
17936
 
17707
17937
  // src/commands/plugins/list.ts
17708
- var TABLE_COLUMNS20 = [
17938
+ var TABLE_COLUMNS22 = [
17709
17939
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
17710
17940
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
17711
17941
  {
@@ -17739,7 +17969,7 @@ function registerPluginsList(plugins) {
17739
17969
  console.log("No plugins found.");
17740
17970
  return;
17741
17971
  }
17742
- console.log(renderTable(TABLE_COLUMNS20, parsed.data.plugins));
17972
+ console.log(renderTable(TABLE_COLUMNS22, parsed.data.plugins));
17743
17973
  console.log(`Showing ${parsed.data.plugins.length}`);
17744
17974
  }, "listAction"));
17745
17975
  }
@@ -17937,7 +18167,7 @@ function registerProfiles(program) {
17937
18167
  __name(registerProfiles, "registerProfiles");
17938
18168
 
17939
18169
  // src/commands/queue-labels/index.ts
17940
- var TABLE_COLUMNS21 = [
18170
+ var TABLE_COLUMNS23 = [
17941
18171
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
17942
18172
  { header: "KEY", cell: /* @__PURE__ */ __name((row) => row.key, "cell") },
17943
18173
  { header: "VALUE", cell: /* @__PURE__ */ __name((row) => row.value, "cell") },
@@ -17991,7 +18221,7 @@ function registerList3(labels) {
17991
18221
  console.log("No labels defined on this queue.");
17992
18222
  return;
17993
18223
  }
17994
- console.log(renderTable(TABLE_COLUMNS21, parsed.data.labels));
18224
+ console.log(renderTable(TABLE_COLUMNS23, parsed.data.labels));
17995
18225
  console.log(`Showing ${parsed.data.labels.length}`);
17996
18226
  }, "listAction"));
17997
18227
  }
@@ -18064,12 +18294,15 @@ function registerUpdate(labels) {
18064
18294
  "Pass at least one of --key, --value, or --color-hue."
18065
18295
  );
18066
18296
  }
18297
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
18067
18298
  if (body.value === void 0) {
18068
- throw new CliError(
18069
- "--value is required by the API; pass --value <text> to keep or change it."
18299
+ body.value = await fetchCurrentLabelValue(
18300
+ options.queue,
18301
+ labelId,
18302
+ apiKey,
18303
+ apiBaseUrl
18070
18304
  );
18071
18305
  }
18072
- const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
18073
18306
  const raw = await httpRequest({
18074
18307
  method: "PATCH",
18075
18308
  path: `/v1/queues/${encodeURIComponent(options.queue)}/labels/${encodeURIComponent(labelId)}`,
@@ -18132,6 +18365,38 @@ function registerDelete(labels) {
18132
18365
  }, "deleteAction"));
18133
18366
  }
18134
18367
  __name(registerDelete, "registerDelete");
18368
+ var LABEL_PAGE_SIZE = 1e3;
18369
+ async function fetchCurrentLabelValue(queueId, labelId, apiKey, apiBaseUrl) {
18370
+ for (let offset = 0; ; offset += LABEL_PAGE_SIZE) {
18371
+ const raw = await httpRequest({
18372
+ method: "GET",
18373
+ path: `/v1/queues/${encodeURIComponent(queueId)}/labels`,
18374
+ apiKey,
18375
+ apiBaseUrl,
18376
+ searchParams: new URLSearchParams({
18377
+ limit: String(LABEL_PAGE_SIZE),
18378
+ offset: String(offset)
18379
+ })
18380
+ });
18381
+ const parsed = caseLabelsWithCountsListResponseSchema.safeParse(raw);
18382
+ if (!parsed.success) {
18383
+ throw new CliError(
18384
+ `Unexpected response from the API: ${parsed.error.message}`
18385
+ );
18386
+ }
18387
+ const match = parsed.data.labels.find((label) => label.id === labelId);
18388
+ if (match) {
18389
+ return match.value;
18390
+ }
18391
+ if (parsed.data.labels.length < LABEL_PAGE_SIZE) {
18392
+ break;
18393
+ }
18394
+ }
18395
+ throw new CliError(
18396
+ `Label "${labelId}" not found on queue "${queueId}". Pass --value to set it explicitly.`
18397
+ );
18398
+ }
18399
+ __name(fetchCurrentLabelValue, "fetchCurrentLabelValue");
18135
18400
  function parseColorHue(raw) {
18136
18401
  if (raw === void 0) {
18137
18402
  return void 0;
@@ -18348,11 +18613,361 @@ function registerQueuesGet(queues) {
18348
18613
  }
18349
18614
  __name(registerQueuesGet, "registerQueuesGet");
18350
18615
 
18616
+ // src/commands/shared/resolve-queue-link.ts
18617
+ var ROLE_INTEGRATION_TYPE = {
18618
+ producer: "case-queue-producer",
18619
+ consumer: "case-queue-consumer"
18620
+ };
18621
+ function parseRole(raw) {
18622
+ if (raw === "producer" || raw === "consumer") {
18623
+ return raw;
18624
+ }
18625
+ throw new CliError('--role must be "producer" or "consumer"');
18626
+ }
18627
+ __name(parseRole, "parseRole");
18628
+ var REVISION_PAGE_SIZE = 100;
18629
+ var SLOT_SETTLE_BACKOFF_MS = [250, 500, 1e3, 2e3];
18630
+ function delay(ms) {
18631
+ return new Promise((resolve) => {
18632
+ setTimeout(resolve, ms);
18633
+ });
18634
+ }
18635
+ __name(delay, "delay");
18636
+ function queuesPath(agentId, revisionId, integrationId) {
18637
+ return `/v1/agents/${encodeURIComponent(agentId)}/revisions/${encodeURIComponent(revisionId)}/integrations/${encodeURIComponent(integrationId)}/queues`;
18638
+ }
18639
+ __name(queuesPath, "queuesPath");
18640
+ async function resolveLiveRevisionId(agentId, ctx) {
18641
+ for (let offset = 0; ; offset += REVISION_PAGE_SIZE) {
18642
+ const searchParams = new URLSearchParams({
18643
+ limit: String(REVISION_PAGE_SIZE),
18644
+ offset: String(offset)
18645
+ });
18646
+ const raw = await httpRequest({
18647
+ method: "GET",
18648
+ path: `/v1/agents/${encodeURIComponent(agentId)}/revisions`,
18649
+ searchParams,
18650
+ apiKey: ctx.apiKey,
18651
+ apiBaseUrl: ctx.apiBaseUrl
18652
+ });
18653
+ const parsed = listBuildsResponseSchema2.safeParse(raw);
18654
+ if (!parsed.success) {
18655
+ throw new CliError(
18656
+ `Unexpected response from the API: ${parsed.error.message}`
18657
+ );
18658
+ }
18659
+ const live = parsed.data.builds.find(
18660
+ (build) => build.status === BUILD_STATUS.LIVE
18661
+ );
18662
+ if (live) {
18663
+ return live.id;
18664
+ }
18665
+ const fetched = parsed.data.builds.length;
18666
+ const total = parsed.data.total ?? offset + fetched;
18667
+ if (fetched === 0 || offset + fetched >= total) {
18668
+ break;
18669
+ }
18670
+ }
18671
+ throw new CliError(
18672
+ `No live revision found for agent ${agentId}. Pass --revision <id> to target a specific revision.`
18673
+ );
18674
+ }
18675
+ __name(resolveLiveRevisionId, "resolveLiveRevisionId");
18676
+ async function resolveCatalogIntegrationId(type, ctx) {
18677
+ const raw = await httpRequest({
18678
+ method: "GET",
18679
+ path: "/v1/integrations",
18680
+ apiKey: ctx.apiKey,
18681
+ apiBaseUrl: ctx.apiBaseUrl
18682
+ });
18683
+ const parsed = integrationsListResponseSchema2.safeParse(raw);
18684
+ if (!parsed.success) {
18685
+ throw new CliError(
18686
+ `Unexpected response from the API: ${parsed.error.message}`
18687
+ );
18688
+ }
18689
+ const match = parsed.data.integrations.find(
18690
+ (integration) => integration.type === type
18691
+ );
18692
+ if (!match) {
18693
+ throw new CliError(
18694
+ `No "${type}" integration found in the team catalog. Run \`duvo integrations list\` to inspect it.`
18695
+ );
18696
+ }
18697
+ return match.id;
18698
+ }
18699
+ __name(resolveCatalogIntegrationId, "resolveCatalogIntegrationId");
18700
+ async function listSlotIntegrationIds(agentId, revisionId, ctx) {
18701
+ const raw = await httpRequest({
18702
+ method: "GET",
18703
+ path: `/v1/agents/${encodeURIComponent(agentId)}/revisions/${encodeURIComponent(revisionId)}/integrations`,
18704
+ apiKey: ctx.apiKey,
18705
+ apiBaseUrl: ctx.apiBaseUrl
18706
+ });
18707
+ const parsed = listBuildIntegrationsResponseSchema2.safeParse(raw);
18708
+ if (!parsed.success) {
18709
+ throw new CliError(
18710
+ `Unexpected response from the API: ${parsed.error.message}`
18711
+ );
18712
+ }
18713
+ return parsed.data.integrations.map((config) => config.integration_id).filter((id) => typeof id === "string" && id.length > 0);
18714
+ }
18715
+ __name(listSlotIntegrationIds, "listSlotIntegrationIds");
18716
+ async function isIntegrationAttached(agentId, revisionId, integrationId, ctx) {
18717
+ const ids = await listSlotIntegrationIds(agentId, revisionId, ctx);
18718
+ return ids.includes(integrationId);
18719
+ }
18720
+ __name(isIntegrationAttached, "isIntegrationAttached");
18721
+ async function ensureIntegrationAttached(agentId, revisionId, integrationId, ctx) {
18722
+ if (await isIntegrationAttached(agentId, revisionId, integrationId, ctx)) {
18723
+ return;
18724
+ }
18725
+ for (let attempt = 0; attempt < SLOT_SETTLE_BACKOFF_MS.length; attempt++) {
18726
+ const raw = await httpRequest({
18727
+ method: "POST",
18728
+ path: `/v1/agents/${encodeURIComponent(agentId)}/revisions/${encodeURIComponent(revisionId)}/integrations`,
18729
+ apiKey: ctx.apiKey,
18730
+ apiBaseUrl: ctx.apiBaseUrl,
18731
+ body: { integration_ids: [integrationId] }
18732
+ });
18733
+ const parsed = attachBuildIntegrationsResponseSchema.safeParse(raw);
18734
+ if (!parsed.success) {
18735
+ throw new CliError(
18736
+ `Unexpected response from the API: ${parsed.error.message}`
18737
+ );
18738
+ }
18739
+ if (await isIntegrationAttached(agentId, revisionId, integrationId, ctx)) {
18740
+ return;
18741
+ }
18742
+ if (attempt < SLOT_SETTLE_BACKOFF_MS.length - 1) {
18743
+ await delay(SLOT_SETTLE_BACKOFF_MS[attempt]);
18744
+ }
18745
+ }
18746
+ throw new CliError(
18747
+ `Attached the integration but the revision slot did not persist after ${SLOT_SETTLE_BACKOFF_MS.length} attempts. The build may still be settling \u2014 retry in a moment.`
18748
+ );
18749
+ }
18750
+ __name(ensureIntegrationAttached, "ensureIntegrationAttached");
18751
+ async function getLinkedQueueIds(agentId, revisionId, integrationId, ctx) {
18752
+ const raw = await httpRequest({
18753
+ method: "GET",
18754
+ path: queuesPath(agentId, revisionId, integrationId),
18755
+ apiKey: ctx.apiKey,
18756
+ apiBaseUrl: ctx.apiBaseUrl
18757
+ });
18758
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
18759
+ if (!parsed.success) {
18760
+ throw new CliError(
18761
+ `Unexpected response from the API: ${parsed.error.message}`
18762
+ );
18763
+ }
18764
+ return parsed.data.queues.map((queue) => queue.id);
18765
+ }
18766
+ __name(getLinkedQueueIds, "getLinkedQueueIds");
18767
+ async function createCaseTrigger(agentId, queueId, enabled, concurrency, ctx) {
18768
+ const body = {
18769
+ case_queue_id: queueId,
18770
+ enabled
18771
+ };
18772
+ if (concurrency !== void 0) {
18773
+ body.concurrency = concurrency;
18774
+ }
18775
+ const raw = await httpRequest({
18776
+ method: "POST",
18777
+ path: `/v1/agents/${encodeURIComponent(agentId)}/case-triggers`,
18778
+ apiKey: ctx.apiKey,
18779
+ apiBaseUrl: ctx.apiBaseUrl,
18780
+ body
18781
+ });
18782
+ const parsed = caseTriggerResponseSchema.safeParse(raw);
18783
+ if (!parsed.success) {
18784
+ throw new CliError(
18785
+ `Unexpected response from the API: ${parsed.error.message}`
18786
+ );
18787
+ }
18788
+ return parsed.data.case_trigger;
18789
+ }
18790
+ __name(createCaseTrigger, "createCaseTrigger");
18791
+ async function findCaseTriggerForQueue(agentId, queueId, ctx) {
18792
+ const raw = await httpRequest({
18793
+ method: "GET",
18794
+ path: `/v1/agents/${encodeURIComponent(agentId)}/case-triggers`,
18795
+ apiKey: ctx.apiKey,
18796
+ apiBaseUrl: ctx.apiBaseUrl
18797
+ });
18798
+ const parsed = caseTriggersListResponseSchema.safeParse(raw);
18799
+ if (!parsed.success) {
18800
+ throw new CliError(
18801
+ `Unexpected response from the API: ${parsed.error.message}`
18802
+ );
18803
+ }
18804
+ return parsed.data.case_triggers.find(
18805
+ (trigger) => trigger.case_queue_id === queueId
18806
+ ) ?? null;
18807
+ }
18808
+ __name(findCaseTriggerForQueue, "findCaseTriggerForQueue");
18809
+ async function deleteCaseTrigger(agentId, triggerId, ctx) {
18810
+ const raw = await httpRequest({
18811
+ method: "DELETE",
18812
+ path: `/v1/agents/${encodeURIComponent(agentId)}/case-triggers/${encodeURIComponent(triggerId)}`,
18813
+ apiKey: ctx.apiKey,
18814
+ apiBaseUrl: ctx.apiBaseUrl
18815
+ });
18816
+ const parsed = deleteCaseTriggerResponseSchema.safeParse(raw);
18817
+ if (!parsed.success) {
18818
+ throw new CliError(
18819
+ `Unexpected response from the API: ${parsed.error.message}`
18820
+ );
18821
+ }
18822
+ }
18823
+ __name(deleteCaseTrigger, "deleteCaseTrigger");
18824
+ async function replaceLinkedQueues(agentId, revisionId, integrationId, queueIds, ctx) {
18825
+ const raw = await httpRequest({
18826
+ method: "PUT",
18827
+ path: queuesPath(agentId, revisionId, integrationId),
18828
+ apiKey: ctx.apiKey,
18829
+ apiBaseUrl: ctx.apiBaseUrl,
18830
+ body: { queue_ids: [...queueIds] }
18831
+ });
18832
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
18833
+ if (!parsed.success) {
18834
+ throw new CliError(
18835
+ `Unexpected response from the API: ${parsed.error.message}`
18836
+ );
18837
+ }
18838
+ return parsed.data.queues.map((queue) => queue.id);
18839
+ }
18840
+ __name(replaceLinkedQueues, "replaceLinkedQueues");
18841
+
18842
+ // src/commands/queues/link-agent.ts
18843
+ var POSITIVE_INT4 = /^\d+$/;
18844
+ function parseConcurrency3(raw) {
18845
+ if (!POSITIVE_INT4.test(raw)) {
18846
+ throw new CliError("--concurrency must be a positive integer");
18847
+ }
18848
+ const value = Number(raw);
18849
+ if (value <= 0) {
18850
+ throw new CliError("--concurrency must be a positive integer");
18851
+ }
18852
+ return value;
18853
+ }
18854
+ __name(parseConcurrency3, "parseConcurrency");
18855
+ function registerQueuesLinkAgent(queues) {
18856
+ queues.command("link-agent <queue-id>").description(
18857
+ "Link an agent to a queue as a producer or consumer. Attaches the case-queue integration on the agent's live revision and maps the queue; for consumers it also creates the case trigger."
18858
+ ).requiredOption("--agent <id>", "ID of the agent to link").requiredOption(
18859
+ "--role <role>",
18860
+ 'role to link as: "producer" (creates cases) or "consumer" (handles cases)'
18861
+ ).option(
18862
+ "--revision <id>",
18863
+ "target a specific revision (default: the agent's live revision)"
18864
+ ).option(
18865
+ "--enable-trigger",
18866
+ "for consumers, create the case trigger enabled (default: disabled)"
18867
+ ).option(
18868
+ "--concurrency <n>",
18869
+ "for consumers, max concurrent runs on the case trigger (omit for unlimited)"
18870
+ ).option("--json", "output raw JSON").action(/* @__PURE__ */ __name(async function linkAgentAction(queueId, options) {
18871
+ const trimmedQueueId = trimRequired(queueId, "<queue-id>");
18872
+ const trimmedAgentId = trimRequired(options.agent, "--agent");
18873
+ const role = parseRole(options.role);
18874
+ if (role === "producer" && options.enableTrigger === true) {
18875
+ throw new CliError(
18876
+ "--enable-trigger only applies to consumers (producers have no case trigger)"
18877
+ );
18878
+ }
18879
+ if (role === "producer" && options.concurrency !== void 0) {
18880
+ throw new CliError(
18881
+ "--concurrency only applies to consumers (producers have no case trigger)"
18882
+ );
18883
+ }
18884
+ const concurrency = options.concurrency !== void 0 ? parseConcurrency3(options.concurrency) : void 0;
18885
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
18886
+ const ctx = { apiKey, apiBaseUrl };
18887
+ const revisionId = options.revision !== void 0 ? trimRequired(options.revision, "--revision") : await resolveLiveRevisionId(trimmedAgentId, ctx);
18888
+ const integrationId = await resolveCatalogIntegrationId(
18889
+ ROLE_INTEGRATION_TYPE[role],
18890
+ ctx
18891
+ );
18892
+ await ensureIntegrationAttached(
18893
+ trimmedAgentId,
18894
+ revisionId,
18895
+ integrationId,
18896
+ ctx
18897
+ );
18898
+ const existingQueues = await getLinkedQueueIds(
18899
+ trimmedAgentId,
18900
+ revisionId,
18901
+ integrationId,
18902
+ ctx
18903
+ );
18904
+ const desiredQueues = existingQueues.includes(trimmedQueueId) ? existingQueues : [...existingQueues, trimmedQueueId];
18905
+ const linkedQueues = await replaceLinkedQueues(
18906
+ trimmedAgentId,
18907
+ revisionId,
18908
+ integrationId,
18909
+ desiredQueues,
18910
+ ctx
18911
+ );
18912
+ let trigger = null;
18913
+ if (role === "consumer") {
18914
+ const existingTrigger = await findCaseTriggerForQueue(
18915
+ trimmedAgentId,
18916
+ trimmedQueueId,
18917
+ ctx
18918
+ );
18919
+ trigger = existingTrigger ?? await createCaseTrigger(
18920
+ trimmedAgentId,
18921
+ trimmedQueueId,
18922
+ options.enableTrigger === true,
18923
+ concurrency,
18924
+ ctx
18925
+ );
18926
+ }
18927
+ if (options.json) {
18928
+ printJson({
18929
+ revision_id: revisionId,
18930
+ integration_id: integrationId,
18931
+ linked_queue_ids: linkedQueues,
18932
+ case_trigger: trigger
18933
+ });
18934
+ return;
18935
+ }
18936
+ console.log(
18937
+ green(
18938
+ `Linked agent ${trimmedAgentId} to queue ${trimmedQueueId} as ${role}.`
18939
+ )
18940
+ );
18941
+ const entries = [
18942
+ { label: "Revision", value: revisionId },
18943
+ { label: "Integration", value: integrationId },
18944
+ { label: "Linked queues", value: String(linkedQueues.length) }
18945
+ ];
18946
+ if (trigger !== null) {
18947
+ entries.push({ label: "Case trigger", value: trigger.id });
18948
+ entries.push({
18949
+ label: "Trigger enabled",
18950
+ value: trigger.enabled ? "yes" : "no"
18951
+ });
18952
+ }
18953
+ console.log(renderKeyValue(entries));
18954
+ if (role === "consumer" && trigger !== null && !trigger.enabled) {
18955
+ console.log("");
18956
+ console.log(
18957
+ yellow(
18958
+ "Heads up: the case trigger is disabled, so this consumer won't pick up cases yet. Re-run with --enable-trigger or enable it via `duvo agents case-triggers update`."
18959
+ )
18960
+ );
18961
+ }
18962
+ }, "linkAgentAction"));
18963
+ }
18964
+ __name(registerQueuesLinkAgent, "registerQueuesLinkAgent");
18965
+
18351
18966
  // src/commands/queues/list.ts
18352
18967
  var DEFAULT_LIMIT7 = 20;
18353
18968
  var MIN_LIMIT7 = 1;
18354
18969
  var MAX_LIMIT7 = 100;
18355
- var TABLE_COLUMNS22 = [
18970
+ var TABLE_COLUMNS24 = [
18356
18971
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18357
18972
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
18358
18973
  {
@@ -18396,7 +19011,7 @@ function registerQueuesList(queues) {
18396
19011
  console.log("No queues found.");
18397
19012
  return;
18398
19013
  }
18399
- console.log(renderTable(TABLE_COLUMNS22, response.queues));
19014
+ console.log(renderTable(TABLE_COLUMNS24, response.queues));
18400
19015
  const resolvedOffset = response.offset ?? offset;
18401
19016
  const total = response.total ?? null;
18402
19017
  const summary = total === null ? `Showing ${response.queues.length} \xB7 offset ${resolvedOffset}` : `Showing ${response.queues.length} of ${total} \xB7 offset ${resolvedOffset}`;
@@ -18405,6 +19020,96 @@ function registerQueuesList(queues) {
18405
19020
  }
18406
19021
  __name(registerQueuesList, "registerQueuesList");
18407
19022
 
19023
+ // src/commands/queues/unlink-agent.ts
19024
+ function registerQueuesUnlinkAgent(queues) {
19025
+ queues.command("unlink-agent <queue-id>").description(
19026
+ "Unlink an agent from a queue. Removes the queue from the role's integration slot on the agent's live revision; for consumers it also removes the case trigger for that queue."
19027
+ ).requiredOption("--agent <id>", "ID of the agent to unlink").requiredOption("--role <role>", 'role to unlink: "producer" or "consumer"').option(
19028
+ "--revision <id>",
19029
+ "target a specific revision (default: the agent's live revision)"
19030
+ ).option("--json", "output raw JSON").action(/* @__PURE__ */ __name(async function unlinkAgentAction(queueId, options) {
19031
+ const trimmedQueueId = trimRequired(queueId, "<queue-id>");
19032
+ const trimmedAgentId = trimRequired(options.agent, "--agent");
19033
+ const role = parseRole(options.role);
19034
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
19035
+ const ctx = { apiKey, apiBaseUrl };
19036
+ const revisionId = options.revision !== void 0 ? trimRequired(options.revision, "--revision") : await resolveLiveRevisionId(trimmedAgentId, ctx);
19037
+ const integrationId = await resolveCatalogIntegrationId(
19038
+ ROLE_INTEGRATION_TYPE[role],
19039
+ ctx
19040
+ );
19041
+ let queueUnlinked = false;
19042
+ const attached = await isIntegrationAttached(
19043
+ trimmedAgentId,
19044
+ revisionId,
19045
+ integrationId,
19046
+ ctx
19047
+ );
19048
+ if (attached) {
19049
+ const existingQueues = await getLinkedQueueIds(
19050
+ trimmedAgentId,
19051
+ revisionId,
19052
+ integrationId,
19053
+ ctx
19054
+ );
19055
+ if (existingQueues.includes(trimmedQueueId)) {
19056
+ await replaceLinkedQueues(
19057
+ trimmedAgentId,
19058
+ revisionId,
19059
+ integrationId,
19060
+ existingQueues.filter((id) => id !== trimmedQueueId),
19061
+ ctx
19062
+ );
19063
+ queueUnlinked = true;
19064
+ }
19065
+ }
19066
+ let triggerRemoved = false;
19067
+ if (role === "consumer") {
19068
+ const trigger = await findCaseTriggerForQueue(
19069
+ trimmedAgentId,
19070
+ trimmedQueueId,
19071
+ ctx
19072
+ );
19073
+ if (trigger !== null) {
19074
+ await deleteCaseTrigger(trimmedAgentId, trigger.id, ctx);
19075
+ triggerRemoved = true;
19076
+ }
19077
+ }
19078
+ if (options.json) {
19079
+ printJson({
19080
+ revision_id: revisionId,
19081
+ integration_id: integrationId,
19082
+ queue_unlinked: queueUnlinked,
19083
+ case_trigger_removed: triggerRemoved
19084
+ });
19085
+ return;
19086
+ }
19087
+ if (!queueUnlinked && !triggerRemoved) {
19088
+ console.log(
19089
+ `Agent ${trimmedAgentId} was not linked to queue ${trimmedQueueId} as ${role}; nothing to do.`
19090
+ );
19091
+ return;
19092
+ }
19093
+ console.log(
19094
+ green(
19095
+ `Unlinked agent ${trimmedAgentId} from queue ${trimmedQueueId} as ${role}.`
19096
+ )
19097
+ );
19098
+ console.log(
19099
+ renderKeyValue([
19100
+ { label: "Revision", value: revisionId },
19101
+ { label: "Integration", value: integrationId },
19102
+ { label: "Queue unlinked", value: queueUnlinked ? "yes" : "no" },
19103
+ {
19104
+ label: "Case trigger removed",
19105
+ value: triggerRemoved ? "yes" : "no"
19106
+ }
19107
+ ])
19108
+ );
19109
+ }, "unlinkAgentAction"));
19110
+ }
19111
+ __name(registerQueuesUnlinkAgent, "registerQueuesUnlinkAgent");
19112
+
18408
19113
  // src/commands/queues/update.ts
18409
19114
  function registerQueuesUpdate(queues) {
18410
19115
  queues.command("update <id>").description("Update a case queue").option("--name <name>", "new queue name").option("--description <text>", "new description").option("--clear-description", "clear the existing description").option("--folder-id <id>", "move queue under a folder").option("--clear-folder", "move queue out of any folder (root)").option("--json", "output raw JSON from the API").action(/* @__PURE__ */ __name(async function updateAction(id, options) {
@@ -18494,6 +19199,8 @@ function registerQueues(program) {
18494
19199
  registerQueuesUpdate(queues);
18495
19200
  registerQueuesDelete(queues);
18496
19201
  registerQueuesAgents(queues);
19202
+ registerQueuesLinkAgent(queues);
19203
+ registerQueuesUnlinkAgent(queues);
18497
19204
  }
18498
19205
  __name(registerQueues, "registerQueues");
18499
19206
 
@@ -18558,7 +19265,7 @@ function registerRevisionLoginsDetach(revisionLogins) {
18558
19265
  __name(registerRevisionLoginsDetach, "registerRevisionLoginsDetach");
18559
19266
 
18560
19267
  // src/commands/revision-logins/list.ts
18561
- var TABLE_COLUMNS23 = [
19268
+ var TABLE_COLUMNS25 = [
18562
19269
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18563
19270
  { header: "DOMAIN", cell: /* @__PURE__ */ __name((row) => row.domain, "cell") },
18564
19271
  {
@@ -18594,7 +19301,7 @@ function registerRevisionLoginsList(revisionLogins) {
18594
19301
  console.log("No browser logins attached to this revision.");
18595
19302
  return;
18596
19303
  }
18597
- console.log(renderTable(TABLE_COLUMNS23, rows));
19304
+ console.log(renderTable(TABLE_COLUMNS25, rows));
18598
19305
  console.log(`Showing ${rows.length}`);
18599
19306
  }, "listAction"));
18600
19307
  }
@@ -18667,7 +19374,7 @@ function registerRevisionSecretsDetach(revisionSecrets) {
18667
19374
  __name(registerRevisionSecretsDetach, "registerRevisionSecretsDetach");
18668
19375
 
18669
19376
  // src/commands/revision-secrets/list.ts
18670
- var TABLE_COLUMNS24 = [
19377
+ var TABLE_COLUMNS26 = [
18671
19378
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18672
19379
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
18673
19380
  {
@@ -18705,7 +19412,7 @@ function registerRevisionSecretsList(revisionSecrets) {
18705
19412
  console.log("No secrets attached to this revision.");
18706
19413
  return;
18707
19414
  }
18708
- console.log(renderTable(TABLE_COLUMNS24, rows));
19415
+ console.log(renderTable(TABLE_COLUMNS26, rows));
18709
19416
  console.log(`Showing ${rows.length}`);
18710
19417
  }, "listAction"));
18711
19418
  }
@@ -18874,7 +19581,7 @@ var MAX_LIMIT8 = 100;
18874
19581
  var SORT_ORDERS2 = ["asc", "desc"];
18875
19582
  var HAS_ISSUES_VALUES = ["true", "false"];
18876
19583
  var TITLE_PREVIEW_WIDTH = 50;
18877
- var TABLE_COLUMNS25 = [
19584
+ var TABLE_COLUMNS27 = [
18878
19585
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18879
19586
  { header: "RUN#", cell: /* @__PURE__ */ __name((row) => String(row.run_number), "cell") },
18880
19587
  { header: "STATUS", cell: /* @__PURE__ */ __name((row) => row.status, "cell") },
@@ -18971,7 +19678,7 @@ function registerRunsList(runs) {
18971
19678
  console.log("No runs found.");
18972
19679
  return;
18973
19680
  }
18974
- console.log(renderTable(TABLE_COLUMNS25, response.data));
19681
+ console.log(renderTable(TABLE_COLUMNS27, response.data));
18975
19682
  console.log(
18976
19683
  `Showing ${response.data.length} of ${response.total} \xB7 offset ${offset}`
18977
19684
  );
@@ -19081,7 +19788,7 @@ var DEFAULT_LIMIT9 = 20;
19081
19788
  var MIN_LIMIT9 = 1;
19082
19789
  var MAX_LIMIT9 = 50;
19083
19790
  var TEXT_PREVIEW_WIDTH2 = 60;
19084
- var TABLE_COLUMNS26 = [
19791
+ var TABLE_COLUMNS28 = [
19085
19792
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
19086
19793
  { header: "ROLE", cell: /* @__PURE__ */ __name((row) => row.role, "cell") },
19087
19794
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
@@ -19146,7 +19853,7 @@ function registerRunsMessages(runs) {
19146
19853
  console.log("No messages found.");
19147
19854
  return;
19148
19855
  }
19149
- console.log(renderTable(TABLE_COLUMNS26, response.messages));
19856
+ console.log(renderTable(TABLE_COLUMNS28, response.messages));
19150
19857
  const resolvedOffset = response.offset ?? offset;
19151
19858
  const summary = response.total === null || response.total === void 0 ? `Showing ${response.messages.length} \xB7 offset ${resolvedOffset}` : `Showing ${response.messages.length} of ${response.total} \xB7 offset ${resolvedOffset}`;
19152
19859
  console.log(summary);
@@ -19467,7 +20174,7 @@ function registerSandboxesCreate(sandboxes) {
19467
20174
  __name(registerSandboxesCreate, "registerSandboxesCreate");
19468
20175
 
19469
20176
  // src/commands/sandboxes/files.ts
19470
- var TABLE_COLUMNS27 = [
20177
+ var TABLE_COLUMNS29 = [
19471
20178
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
19472
20179
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
19473
20180
  { header: "PATH", cell: /* @__PURE__ */ __name((row) => row.path, "cell") },
@@ -19505,7 +20212,7 @@ function registerSandboxesFiles(sandboxes) {
19505
20212
  console.log("No files found.");
19506
20213
  return;
19507
20214
  }
19508
- console.log(renderTable(TABLE_COLUMNS27, files));
20215
+ console.log(renderTable(TABLE_COLUMNS29, files));
19509
20216
  console.log(`Showing ${files.length}`);
19510
20217
  }, "filesAction"));
19511
20218
  }
@@ -19846,7 +20553,7 @@ function registerSecretsGet(secrets) {
19846
20553
  __name(registerSecretsGet, "registerSecretsGet");
19847
20554
 
19848
20555
  // src/commands/secrets/list.ts
19849
- var TABLE_COLUMNS28 = [
20556
+ var TABLE_COLUMNS30 = [
19850
20557
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
19851
20558
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
19852
20559
  {
@@ -19886,7 +20593,7 @@ function registerSecretsList(secrets) {
19886
20593
  console.log("No secrets found.");
19887
20594
  return;
19888
20595
  }
19889
- console.log(renderTable(TABLE_COLUMNS28, rows));
20596
+ console.log(renderTable(TABLE_COLUMNS30, rows));
19890
20597
  console.log(`Showing ${rows.length}`);
19891
20598
  }, "listAction"));
19892
20599
  }
@@ -19997,7 +20704,7 @@ function registerSecrets(program) {
19997
20704
  __name(registerSecrets, "registerSecrets");
19998
20705
 
19999
20706
  // src/commands/skills/assignments.ts
20000
- var TABLE_COLUMNS29 = [
20707
+ var TABLE_COLUMNS31 = [
20001
20708
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
20002
20709
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") }
20003
20710
  ];
@@ -20025,7 +20732,7 @@ function registerSkillsAssignments(skills) {
20025
20732
  console.log("No assignments use this skill.");
20026
20733
  return;
20027
20734
  }
20028
- console.log(renderTable(TABLE_COLUMNS29, assignments));
20735
+ console.log(renderTable(TABLE_COLUMNS31, assignments));
20029
20736
  console.log(`Showing ${assignments.length} of ${total}`);
20030
20737
  }, "assignmentsAction"));
20031
20738
  }
@@ -20303,7 +21010,7 @@ function readContent2(options) {
20303
21010
  __name(readContent2, "readContent");
20304
21011
 
20305
21012
  // src/commands/skills/files.ts
20306
- var TABLE_COLUMNS30 = [
21013
+ var TABLE_COLUMNS32 = [
20307
21014
  { header: "PATH", cell: /* @__PURE__ */ __name((row) => row.path, "cell") },
20308
21015
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
20309
21016
  {
@@ -20342,7 +21049,7 @@ function registerSkillsFiles(skills) {
20342
21049
  console.log("No files found.");
20343
21050
  return;
20344
21051
  }
20345
- console.log(renderTable(TABLE_COLUMNS30, parsed.data.files));
21052
+ console.log(renderTable(TABLE_COLUMNS32, parsed.data.files));
20346
21053
  console.log(`Showing ${parsed.data.files.length}`);
20347
21054
  }, "filesAction"));
20348
21055
  }
@@ -20741,7 +21448,7 @@ __name(registerTeamGet, "registerTeamGet");
20741
21448
  var DEFAULT_LIMIT10 = 50;
20742
21449
  var MIN_LIMIT10 = 1;
20743
21450
  var MAX_LIMIT10 = 100;
20744
- var TABLE_COLUMNS31 = [
21451
+ var TABLE_COLUMNS33 = [
20745
21452
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
20746
21453
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name ?? "", "cell") },
20747
21454
  { header: "EMAIL", cell: /* @__PURE__ */ __name((row) => row.email, "cell") },
@@ -20789,7 +21496,7 @@ function registerTeamMembers(team) {
20789
21496
  console.log("No members found.");
20790
21497
  return;
20791
21498
  }
20792
- console.log(renderTable(TABLE_COLUMNS31, response.members));
21499
+ console.log(renderTable(TABLE_COLUMNS33, response.members));
20793
21500
  console.log(
20794
21501
  `Showing ${response.members.length} of ${response.total} \xB7 offset ${response.offset}`
20795
21502
  );
@@ -20856,7 +21563,7 @@ __name(fetchProfileSummary, "fetchProfileSummary");
20856
21563
  var version = resolveVersion();
20857
21564
  function resolveVersion() {
20858
21565
  if (true) {
20859
- return "0.8.0";
21566
+ return "0.9.0";
20860
21567
  }
20861
21568
  const packageJsonPath = join2(
20862
21569
  dirname2(fileURLToPath(import.meta.url)),
@@ -20921,7 +21628,7 @@ function checkForUpdate() {
20921
21628
  __name(checkForUpdate, "checkForUpdate");
20922
21629
  function loadPackageMeta() {
20923
21630
  if (true) {
20924
- return { name: "@duvoai/cli", version: "0.8.0" };
21631
+ return { name: "@duvoai/cli", version: "0.9.0" };
20925
21632
  }
20926
21633
  const require2 = createRequire(import.meta.url);
20927
21634
  const raw = require2("../package.json");