@duvoai/cli 0.8.0 → 0.9.1

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) => {
@@ -4161,6 +4210,11 @@ var logoUrlResponseSchema = z25.object({
4161
4210
  var successSchema = z26.object({
4162
4211
  success: z26.boolean()
4163
4212
  });
4213
+ function normalizeUserMessageContent(content) {
4214
+ return content.trim();
4215
+ }
4216
+ __name(normalizeUserMessageContent, "normalizeUserMessageContent");
4217
+ __name2(normalizeUserMessageContent, "normalizeUserMessageContent");
4164
4218
  function stripHtml(input, options = {}) {
4165
4219
  const withNewlines = input.replace(/<br\s*\/?>/gi, "\n");
4166
4220
  const noTags = withNewlines.replace(/<[^>]+>/g, "");
@@ -5680,10 +5734,10 @@ var caseQueueSchema = z43.object({
5680
5734
  folder_id: z43.guid().nullable(),
5681
5735
  created_at: z43.string(),
5682
5736
  /**
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.
5737
+ * True when at least one case in this queue has ever had an approval
5738
+ * batch (open, resolved, or cancelled). The frontend uses this to relabel
5739
+ * the "Needs input" filter to "Needs input/approval" and to show the
5740
+ * per-case approvals column.
5687
5741
  */
5688
5742
  has_approvals: z43.boolean()
5689
5743
  });
@@ -7711,6 +7765,21 @@ function isLabelHue(value) {
7711
7765
  }
7712
7766
  __name(isLabelHue, "isLabelHue");
7713
7767
  __name2(isLabelHue, "isLabelHue");
7768
+ function imageMimeTypeToExtension(mimeType) {
7769
+ switch (mimeType) {
7770
+ case "image/jpeg":
7771
+ case "image/jpg":
7772
+ return "jpg";
7773
+ case "image/png":
7774
+ return "png";
7775
+ case "image/webp":
7776
+ return "webp";
7777
+ default:
7778
+ return "bin";
7779
+ }
7780
+ }
7781
+ __name(imageMimeTypeToExtension, "imageMimeTypeToExtension");
7782
+ __name2(imageMimeTypeToExtension, "imageMimeTypeToExtension");
7714
7783
  function generateMcpServerLabel(integrationType, instanceId) {
7715
7784
  if (!instanceId) {
7716
7785
  return integrationType;
@@ -10812,15 +10881,44 @@ function parseTypedField(input) {
10812
10881
  return { key, value: raw };
10813
10882
  }
10814
10883
  __name(parseTypedField, "parseTypedField");
10884
+ function assignField(result, key, value) {
10885
+ if (!key.endsWith("[]")) {
10886
+ if (Array.isArray(result[key])) {
10887
+ throw new Error(
10888
+ `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.`
10889
+ );
10890
+ }
10891
+ result[key] = value;
10892
+ return;
10893
+ }
10894
+ const base = key.slice(0, -2);
10895
+ if (base.length === 0) {
10896
+ throw new Error(
10897
+ `Invalid field "${key}=\u2026". Array key cannot be empty (e.g. -F "ids[]=42").`
10898
+ );
10899
+ }
10900
+ const existing = result[base];
10901
+ if (Array.isArray(existing)) {
10902
+ existing.push(value);
10903
+ return;
10904
+ }
10905
+ if (existing !== void 0) {
10906
+ throw new Error(
10907
+ `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.`
10908
+ );
10909
+ }
10910
+ result[base] = [value];
10911
+ }
10912
+ __name(assignField, "assignField");
10815
10913
  function buildFieldObject(stringFields = [], typedFields = []) {
10816
10914
  const result = {};
10817
10915
  for (const input of stringFields) {
10818
10916
  const { key, value } = parseStringField(input);
10819
- result[key] = value;
10917
+ assignField(result, key, value);
10820
10918
  }
10821
10919
  for (const input of typedFields) {
10822
10920
  const { key, value } = parseTypedField(input);
10823
- result[key] = value;
10921
+ assignField(result, key, value);
10824
10922
  }
10825
10923
  return result;
10826
10924
  }
@@ -11274,6 +11372,94 @@ function registerBuildIntegrationsList(buildIntegrations) {
11274
11372
  }
11275
11373
  __name(registerBuildIntegrationsList, "registerBuildIntegrationsList");
11276
11374
 
11375
+ // src/commands/build-integrations/queues-list.ts
11376
+ var TABLE_COLUMNS9 = [
11377
+ { header: "QUEUE ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11378
+ { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") }
11379
+ ];
11380
+ function registerBuildIntegrationsQueuesList(queues) {
11381
+ queues.command("list", { isDefault: true }).description(
11382
+ "List the case queues linked to a revision's case-queue integration slot"
11383
+ ).requiredOption("--agent <id>", "ID of the agent the revision belongs to").requiredOption("--revision <id>", "ID of the revision").requiredOption(
11384
+ "--integration <id>",
11385
+ "ID of the case-queue integration slot"
11386
+ ).option("--json", "output raw JSON from the API").action(/* @__PURE__ */ __name(async function listAction(options) {
11387
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
11388
+ const raw = await httpRequest({
11389
+ method: "GET",
11390
+ path: `/v1/agents/${encodeURIComponent(options.agent)}/revisions/${encodeURIComponent(options.revision)}/integrations/${encodeURIComponent(options.integration)}/queues`,
11391
+ apiKey,
11392
+ apiBaseUrl
11393
+ });
11394
+ if (options.json) {
11395
+ printJson(raw);
11396
+ return;
11397
+ }
11398
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
11399
+ if (!parsed.success) {
11400
+ throw new CliError(
11401
+ `Unexpected response from the API: ${parsed.error.message}`
11402
+ );
11403
+ }
11404
+ if (parsed.data.queues.length === 0) {
11405
+ console.log("No case queues linked to this integration slot.");
11406
+ return;
11407
+ }
11408
+ console.log(renderTable(TABLE_COLUMNS9, parsed.data.queues));
11409
+ console.log(`Showing ${parsed.data.queues.length}`);
11410
+ }, "listAction"));
11411
+ }
11412
+ __name(registerBuildIntegrationsQueuesList, "registerBuildIntegrationsQueuesList");
11413
+
11414
+ // src/commands/build-integrations/queues-set.ts
11415
+ var TABLE_COLUMNS10 = [
11416
+ { header: "QUEUE ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11417
+ { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") }
11418
+ ];
11419
+ function collectMultiOption2(value, previous) {
11420
+ return [...previous ?? [], value];
11421
+ }
11422
+ __name(collectMultiOption2, "collectMultiOption");
11423
+ function registerBuildIntegrationsQueuesSet(queues) {
11424
+ queues.command("set").description(
11425
+ "Replace the full set of case queues linked to a revision's case-queue integration slot. Omit --queue to unlink all."
11426
+ ).requiredOption("--agent <id>", "ID of the agent the revision belongs to").requiredOption("--revision <id>", "ID of the revision").requiredOption(
11427
+ "--integration <id>",
11428
+ "ID of the case-queue integration slot"
11429
+ ).option(
11430
+ "--queue <id>",
11431
+ "queue ID to link (repeat for multiple; omit to unlink all)",
11432
+ collectMultiOption2,
11433
+ []
11434
+ ).option("--json", "output raw JSON from the API").action(/* @__PURE__ */ __name(async function setAction(options) {
11435
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
11436
+ const raw = await httpRequest({
11437
+ method: "PUT",
11438
+ path: `/v1/agents/${encodeURIComponent(options.agent)}/revisions/${encodeURIComponent(options.revision)}/integrations/${encodeURIComponent(options.integration)}/queues`,
11439
+ apiKey,
11440
+ apiBaseUrl,
11441
+ body: { queue_ids: options.queue }
11442
+ });
11443
+ if (options.json) {
11444
+ printJson(raw);
11445
+ return;
11446
+ }
11447
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
11448
+ if (!parsed.success) {
11449
+ throw new CliError(
11450
+ `Unexpected response from the API: ${parsed.error.message}`
11451
+ );
11452
+ }
11453
+ console.log(
11454
+ green(`Linked ${parsed.data.queues.length} case queue(s) to the slot.`)
11455
+ );
11456
+ if (parsed.data.queues.length > 0) {
11457
+ console.log(renderTable(TABLE_COLUMNS10, parsed.data.queues));
11458
+ }
11459
+ }, "setAction"));
11460
+ }
11461
+ __name(registerBuildIntegrationsQueuesSet, "registerBuildIntegrationsQueuesSet");
11462
+
11277
11463
  // src/commands/build-integrations/remove.ts
11278
11464
  function registerBuildIntegrationsRemove(buildIntegrations) {
11279
11465
  buildIntegrations.command("remove <integration-id>").description(
@@ -11317,6 +11503,11 @@ function registerBuildIntegrations(program) {
11317
11503
  registerBuildIntegrationsConnectionsList(connections);
11318
11504
  registerBuildIntegrationsConnectionsPin(connections);
11319
11505
  registerBuildIntegrationsConnectionsUnpin(connections);
11506
+ const queues = buildIntegrations.command("queues").description(
11507
+ "Inspect and replace the case queues linked to a revision's case-queue integration slot"
11508
+ );
11509
+ registerBuildIntegrationsQueuesList(queues);
11510
+ registerBuildIntegrationsQueuesSet(queues);
11320
11511
  }
11321
11512
  __name(registerBuildIntegrations, "registerBuildIntegrations");
11322
11513
 
@@ -11341,6 +11532,7 @@ var publicBuildSchema2 = z71.object({
11341
11532
  id: z71.string(),
11342
11533
  agent_id: z71.string(),
11343
11534
  name: z71.string(),
11535
+ status: z71.string().nullish(),
11344
11536
  config: jsonValueSchema.nullish(),
11345
11537
  revision_number: z71.number().nullish(),
11346
11538
  created_at: z71.string().nullish(),
@@ -11451,7 +11643,7 @@ __name(registerBuildsGet, "registerBuildsGet");
11451
11643
  var DEFAULT_LIMIT2 = 20;
11452
11644
  var MIN_LIMIT2 = 1;
11453
11645
  var MAX_LIMIT2 = 100;
11454
- var TABLE_COLUMNS9 = [
11646
+ var TABLE_COLUMNS11 = [
11455
11647
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11456
11648
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
11457
11649
  {
@@ -11494,7 +11686,7 @@ function registerBuildsList(builds) {
11494
11686
  console.log("No revisions found.");
11495
11687
  return;
11496
11688
  }
11497
- console.log(renderTable(TABLE_COLUMNS9, response.builds));
11689
+ console.log(renderTable(TABLE_COLUMNS11, response.builds));
11498
11690
  const resolvedOffset = response.offset ?? offset;
11499
11691
  const total = response.total ?? null;
11500
11692
  const summary = total === null ? `Showing ${response.builds.length} \xB7 offset ${resolvedOffset}` : `Showing ${response.builds.length} of ${total} \xB7 offset ${resolvedOffset}`;
@@ -11965,7 +12157,7 @@ __name(fetchQueueCaseCount, "fetchQueueCaseCount");
11965
12157
 
11966
12158
  // src/commands/cases/create.ts
11967
12159
  import { readFile as readFile4 } from "node:fs/promises";
11968
- var TABLE_COLUMNS10 = [
12160
+ var TABLE_COLUMNS12 = [
11969
12161
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
11970
12162
  { header: "TITLE", cell: /* @__PURE__ */ __name((row) => row.title, "cell"), maxWidth: 50 },
11971
12163
  { header: "STATUS", cell: /* @__PURE__ */ __name((row) => row.status, "cell") }
@@ -12008,7 +12200,7 @@ function registerCasesCreate(cases) {
12008
12200
  )
12009
12201
  );
12010
12202
  console.log();
12011
- console.log(renderTable(TABLE_COLUMNS10, addedCases));
12203
+ console.log(renderTable(TABLE_COLUMNS12, addedCases));
12012
12204
  }, "createAction"));
12013
12205
  }
12014
12206
  __name(registerCasesCreate, "registerCasesCreate");
@@ -12243,7 +12435,7 @@ __name(registerCasesGet, "registerCasesGet");
12243
12435
 
12244
12436
  // src/commands/cases/labels.ts
12245
12437
  var MAX_LABELS_PER_REQUEST = 50;
12246
- var TABLE_COLUMNS11 = [
12438
+ var TABLE_COLUMNS13 = [
12247
12439
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
12248
12440
  { header: "KEY", cell: /* @__PURE__ */ __name((row) => row.key, "cell") },
12249
12441
  { header: "VALUE", cell: /* @__PURE__ */ __name((row) => row.value, "cell") },
@@ -12273,7 +12465,7 @@ function registerCasesLabels(cases) {
12273
12465
  console.log("No labels assigned.");
12274
12466
  return;
12275
12467
  }
12276
- console.log(renderTable(TABLE_COLUMNS11, parsed.data.labels));
12468
+ console.log(renderTable(TABLE_COLUMNS13, parsed.data.labels));
12277
12469
  console.log(`Showing ${parsed.data.labels.length}`);
12278
12470
  }, "listAction"));
12279
12471
  labels.command("assign <case-id>").description(
@@ -12313,24 +12505,40 @@ function registerCasesLabels(cases) {
12313
12505
  );
12314
12506
  }
12315
12507
  console.log(green("Labels assigned!"));
12316
- console.log(renderTable(TABLE_COLUMNS11, parsed.data.labels));
12508
+ console.log(renderTable(TABLE_COLUMNS13, parsed.data.labels));
12317
12509
  }, "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(
12510
+ labels.command("unlink <case-id>").description(
12511
+ "Remove one or more labels from a case (by --label-id or --label key=value, for symmetry with assign)"
12512
+ ).requiredOption("--queue <id>", "ID of the queue the case belongs to").option(
12319
12513
  "--label-id <id>",
12320
- "label ID to unlink (repeatable, at least one required)",
12514
+ "label ID to unlink (repeatable)",
12515
+ collectRepeatable,
12516
+ []
12517
+ ).option(
12518
+ "--label <kv>",
12519
+ 'label as "key=value" or "value" for tag-only (repeatable); resolved to an ID against the labels currently on the case',
12321
12520
  collectRepeatable,
12322
12521
  []
12323
12522
  ).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");
12523
+ const directIds = options.labelId ?? [];
12524
+ const labelInputs = options.label ?? [];
12525
+ if (directIds.length === 0 && labelInputs.length === 0) {
12526
+ throw new CliError("Pass at least one --label-id or --label");
12327
12527
  }
12528
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
12529
+ const resolvedIds = labelInputs.length > 0 ? await resolveLabelIdsFromCase(
12530
+ caseId,
12531
+ options.queue,
12532
+ labelInputs,
12533
+ apiKey,
12534
+ apiBaseUrl
12535
+ ) : [];
12536
+ const labelIds = [.../* @__PURE__ */ new Set([...directIds, ...resolvedIds])];
12328
12537
  if (labelIds.length > MAX_LABELS_PER_REQUEST) {
12329
12538
  throw new CliError(
12330
- `Pass at most ${MAX_LABELS_PER_REQUEST} --label-id values per request`
12539
+ `Pass at most ${MAX_LABELS_PER_REQUEST} labels per request`
12331
12540
  );
12332
12541
  }
12333
- const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
12334
12542
  const raw = await httpRequest({
12335
12543
  method: "POST",
12336
12544
  path: `/v1/queues/${encodeURIComponent(options.queue)}/cases/${encodeURIComponent(caseId)}/labels/unlink`,
@@ -12359,6 +12567,33 @@ function collectRepeatable(value, previous) {
12359
12567
  return [...previous, value];
12360
12568
  }
12361
12569
  __name(collectRepeatable, "collectRepeatable");
12570
+ async function resolveLabelIdsFromCase(caseId, queueId, labelInputs, apiKey, apiBaseUrl) {
12571
+ const raw = await httpRequest({
12572
+ method: "GET",
12573
+ path: `/v1/queues/${encodeURIComponent(queueId)}/cases/${encodeURIComponent(caseId)}/labels`,
12574
+ apiKey,
12575
+ apiBaseUrl
12576
+ });
12577
+ const parsed = caseLabelsListResponseSchema.safeParse(raw);
12578
+ if (!parsed.success) {
12579
+ throw new CliError(
12580
+ `Unexpected response from the API: ${parsed.error.message}`
12581
+ );
12582
+ }
12583
+ return labelInputs.map((input) => {
12584
+ const { key, value } = parseLabel(input);
12585
+ const match = parsed.data.labels.find(
12586
+ (label) => label.key === key && label.value === value
12587
+ );
12588
+ if (!match) {
12589
+ throw new CliError(
12590
+ `No label "${input}" is currently assigned to this case.`
12591
+ );
12592
+ }
12593
+ return match.id;
12594
+ });
12595
+ }
12596
+ __name(resolveLabelIdsFromCase, "resolveLabelIdsFromCase");
12362
12597
  function parseLabel(input) {
12363
12598
  const eq = input.indexOf("=");
12364
12599
  if (eq === -1) {
@@ -12388,7 +12623,7 @@ var MIN_LIMIT3 = 1;
12388
12623
  var MAX_LIMIT3 = 100;
12389
12624
  var SORT_FIELDS = ["created_at", "updated_at", "postponed_to"];
12390
12625
  var SORT_ORDERS = ["asc", "desc"];
12391
- var TABLE_COLUMNS12 = [
12626
+ var TABLE_COLUMNS14 = [
12392
12627
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
12393
12628
  { header: "TITLE", cell: /* @__PURE__ */ __name((row) => row.title, "cell"), maxWidth: 50 },
12394
12629
  { header: "STATUS", cell: /* @__PURE__ */ __name((row) => row.status, "cell") },
@@ -12461,7 +12696,7 @@ function registerCasesList(cases) {
12461
12696
  console.log("No cases found.");
12462
12697
  return;
12463
12698
  }
12464
- console.log(renderTable(TABLE_COLUMNS12, response.cases));
12699
+ console.log(renderTable(TABLE_COLUMNS14, response.cases));
12465
12700
  const resolvedOffset = response.offset ?? offset;
12466
12701
  const total = response.total ?? null;
12467
12702
  const summary = total === null ? `Showing ${response.cases.length} \xB7 offset ${resolvedOffset}` : `Showing ${response.cases.length} of ${total} \xB7 offset ${resolvedOffset}`;
@@ -13850,7 +14085,7 @@ __name(registerClarityGet, "registerClarityGet");
13850
14085
  var DEFAULT_LIMIT5 = 20;
13851
14086
  var MIN_LIMIT5 = 1;
13852
14087
  var MAX_LIMIT5 = 100;
13853
- var TABLE_COLUMNS13 = [
14088
+ var TABLE_COLUMNS15 = [
13854
14089
  {
13855
14090
  header: "ID",
13856
14091
  cell: /* @__PURE__ */ __name((row) => row.id, "cell")
@@ -13911,7 +14146,7 @@ function registerClarityListExtraCaptureRequests(clarity) {
13911
14146
  console.log("No extra-capture requests found.");
13912
14147
  return;
13913
14148
  }
13914
- console.log(renderTable(TABLE_COLUMNS13, response.requests));
14149
+ console.log(renderTable(TABLE_COLUMNS15, response.requests));
13915
14150
  console.log(
13916
14151
  `Showing ${response.requests.length} of ${response.total} \xB7 offset ${response.offset}`
13917
14152
  );
@@ -15615,7 +15850,7 @@ function registerConnectionsGet(connections) {
15615
15850
  __name(registerConnectionsGet, "registerConnectionsGet");
15616
15851
 
15617
15852
  // src/commands/connections/list.ts
15618
- var TABLE_COLUMNS14 = [
15853
+ var TABLE_COLUMNS16 = [
15619
15854
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
15620
15855
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
15621
15856
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
@@ -15651,7 +15886,7 @@ function registerConnectionsList(connections) {
15651
15886
  console.log("No connections found.");
15652
15887
  return;
15653
15888
  }
15654
- console.log(renderTable(TABLE_COLUMNS14, rows));
15889
+ console.log(renderTable(TABLE_COLUMNS16, rows));
15655
15890
  console.log(`Showing ${rows.length}`);
15656
15891
  }, "listAction"));
15657
15892
  }
@@ -15670,7 +15905,7 @@ var probeResponseSchema = z77.object({
15670
15905
  error: z77.string().optional()
15671
15906
  });
15672
15907
  var DESCRIPTION_MAX_WIDTH = 80;
15673
- var TABLE_COLUMNS15 = [
15908
+ var TABLE_COLUMNS17 = [
15674
15909
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
15675
15910
  {
15676
15911
  header: "DESCRIPTION",
@@ -15726,7 +15961,7 @@ function registerConnectionsProbe(connections) {
15726
15961
  console.log("No tools found.");
15727
15962
  return;
15728
15963
  }
15729
- console.log(renderTable(TABLE_COLUMNS15, tools));
15964
+ console.log(renderTable(TABLE_COLUMNS17, tools));
15730
15965
  console.log(
15731
15966
  `Showing ${tools.length} tool${tools.length === 1 ? "" : "s"}`
15732
15967
  );
@@ -16111,7 +16346,7 @@ function registerCredentialsGet(credentials) {
16111
16346
  __name(registerCredentialsGet, "registerCredentialsGet");
16112
16347
 
16113
16348
  // src/commands/credentials/list.ts
16114
- var TABLE_COLUMNS16 = [
16349
+ var TABLE_COLUMNS18 = [
16115
16350
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
16116
16351
  { header: "DOMAIN", cell: /* @__PURE__ */ __name((row) => row.domain, "cell") },
16117
16352
  {
@@ -16166,7 +16401,7 @@ function registerCredentialsList(credentials) {
16166
16401
  console.log("No credentials found.");
16167
16402
  return;
16168
16403
  }
16169
- console.log(renderTable(TABLE_COLUMNS16, rows));
16404
+ console.log(renderTable(TABLE_COLUMNS18, rows));
16170
16405
  console.log(`Showing ${rows.length}`);
16171
16406
  }, "listAction"));
16172
16407
  }
@@ -16426,7 +16661,7 @@ function registerFilesDownloadUrl(files) {
16426
16661
  __name(registerFilesDownloadUrl, "registerFilesDownloadUrl");
16427
16662
 
16428
16663
  // src/commands/files/list.ts
16429
- var TABLE_COLUMNS17 = [
16664
+ var TABLE_COLUMNS19 = [
16430
16665
  { header: "PATH", cell: /* @__PURE__ */ __name((row) => row.path, "cell") },
16431
16666
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
16432
16667
  {
@@ -16465,7 +16700,7 @@ function registerFilesList(files) {
16465
16700
  console.log("No files found.");
16466
16701
  return;
16467
16702
  }
16468
- console.log(renderTable(TABLE_COLUMNS17, parsed.data.files));
16703
+ console.log(renderTable(TABLE_COLUMNS19, parsed.data.files));
16469
16704
  console.log(`Showing ${parsed.data.files.length}`);
16470
16705
  }, "listAction"));
16471
16706
  }
@@ -16715,7 +16950,7 @@ function registerIntegrationsCustom(integrations) {
16715
16950
  __name(registerIntegrationsCustom, "registerIntegrationsCustom");
16716
16951
 
16717
16952
  // src/commands/integrations/list.ts
16718
- var TABLE_COLUMNS18 = [
16953
+ var TABLE_COLUMNS20 = [
16719
16954
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
16720
16955
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
16721
16956
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
@@ -16749,7 +16984,7 @@ function registerIntegrationsList(integrations) {
16749
16984
  console.log("No integrations found.");
16750
16985
  return;
16751
16986
  }
16752
- console.log(renderTable(TABLE_COLUMNS18, rows));
16987
+ console.log(renderTable(TABLE_COLUMNS20, rows));
16753
16988
  console.log(`Showing ${rows.length}`);
16754
16989
  }, "listAction"));
16755
16990
  }
@@ -17576,7 +17811,7 @@ function parseHeaders(raw) {
17576
17811
  return result;
17577
17812
  }
17578
17813
  __name(parseHeaders, "parseHeaders");
17579
- var TABLE_COLUMNS19 = [
17814
+ var TABLE_COLUMNS21 = [
17580
17815
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
17581
17816
  { header: "DESCRIPTION", cell: /* @__PURE__ */ __name((row) => row.description, "cell") }
17582
17817
  ];
@@ -17630,7 +17865,7 @@ function registerOauthMcpProbe(mcp) {
17630
17865
  console.log("No tools advertised by this MCP server.");
17631
17866
  return;
17632
17867
  }
17633
- console.log(renderTable(TABLE_COLUMNS19, rows));
17868
+ console.log(renderTable(TABLE_COLUMNS21, rows));
17634
17869
  console.log(`Showing ${rows.length}`);
17635
17870
  }, "probeMcpAction"));
17636
17871
  }
@@ -17705,7 +17940,7 @@ function registerOauth(program) {
17705
17940
  __name(registerOauth, "registerOauth");
17706
17941
 
17707
17942
  // src/commands/plugins/list.ts
17708
- var TABLE_COLUMNS20 = [
17943
+ var TABLE_COLUMNS22 = [
17709
17944
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
17710
17945
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
17711
17946
  {
@@ -17739,7 +17974,7 @@ function registerPluginsList(plugins) {
17739
17974
  console.log("No plugins found.");
17740
17975
  return;
17741
17976
  }
17742
- console.log(renderTable(TABLE_COLUMNS20, parsed.data.plugins));
17977
+ console.log(renderTable(TABLE_COLUMNS22, parsed.data.plugins));
17743
17978
  console.log(`Showing ${parsed.data.plugins.length}`);
17744
17979
  }, "listAction"));
17745
17980
  }
@@ -17937,7 +18172,7 @@ function registerProfiles(program) {
17937
18172
  __name(registerProfiles, "registerProfiles");
17938
18173
 
17939
18174
  // src/commands/queue-labels/index.ts
17940
- var TABLE_COLUMNS21 = [
18175
+ var TABLE_COLUMNS23 = [
17941
18176
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
17942
18177
  { header: "KEY", cell: /* @__PURE__ */ __name((row) => row.key, "cell") },
17943
18178
  { header: "VALUE", cell: /* @__PURE__ */ __name((row) => row.value, "cell") },
@@ -17991,7 +18226,7 @@ function registerList3(labels) {
17991
18226
  console.log("No labels defined on this queue.");
17992
18227
  return;
17993
18228
  }
17994
- console.log(renderTable(TABLE_COLUMNS21, parsed.data.labels));
18229
+ console.log(renderTable(TABLE_COLUMNS23, parsed.data.labels));
17995
18230
  console.log(`Showing ${parsed.data.labels.length}`);
17996
18231
  }, "listAction"));
17997
18232
  }
@@ -18064,12 +18299,15 @@ function registerUpdate(labels) {
18064
18299
  "Pass at least one of --key, --value, or --color-hue."
18065
18300
  );
18066
18301
  }
18302
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
18067
18303
  if (body.value === void 0) {
18068
- throw new CliError(
18069
- "--value is required by the API; pass --value <text> to keep or change it."
18304
+ body.value = await fetchCurrentLabelValue(
18305
+ options.queue,
18306
+ labelId,
18307
+ apiKey,
18308
+ apiBaseUrl
18070
18309
  );
18071
18310
  }
18072
- const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
18073
18311
  const raw = await httpRequest({
18074
18312
  method: "PATCH",
18075
18313
  path: `/v1/queues/${encodeURIComponent(options.queue)}/labels/${encodeURIComponent(labelId)}`,
@@ -18132,6 +18370,38 @@ function registerDelete(labels) {
18132
18370
  }, "deleteAction"));
18133
18371
  }
18134
18372
  __name(registerDelete, "registerDelete");
18373
+ var LABEL_PAGE_SIZE = 1e3;
18374
+ async function fetchCurrentLabelValue(queueId, labelId, apiKey, apiBaseUrl) {
18375
+ for (let offset = 0; ; offset += LABEL_PAGE_SIZE) {
18376
+ const raw = await httpRequest({
18377
+ method: "GET",
18378
+ path: `/v1/queues/${encodeURIComponent(queueId)}/labels`,
18379
+ apiKey,
18380
+ apiBaseUrl,
18381
+ searchParams: new URLSearchParams({
18382
+ limit: String(LABEL_PAGE_SIZE),
18383
+ offset: String(offset)
18384
+ })
18385
+ });
18386
+ const parsed = caseLabelsWithCountsListResponseSchema.safeParse(raw);
18387
+ if (!parsed.success) {
18388
+ throw new CliError(
18389
+ `Unexpected response from the API: ${parsed.error.message}`
18390
+ );
18391
+ }
18392
+ const match = parsed.data.labels.find((label) => label.id === labelId);
18393
+ if (match) {
18394
+ return match.value;
18395
+ }
18396
+ if (parsed.data.labels.length < LABEL_PAGE_SIZE) {
18397
+ break;
18398
+ }
18399
+ }
18400
+ throw new CliError(
18401
+ `Label "${labelId}" not found on queue "${queueId}". Pass --value to set it explicitly.`
18402
+ );
18403
+ }
18404
+ __name(fetchCurrentLabelValue, "fetchCurrentLabelValue");
18135
18405
  function parseColorHue(raw) {
18136
18406
  if (raw === void 0) {
18137
18407
  return void 0;
@@ -18348,11 +18618,361 @@ function registerQueuesGet(queues) {
18348
18618
  }
18349
18619
  __name(registerQueuesGet, "registerQueuesGet");
18350
18620
 
18621
+ // src/commands/shared/resolve-queue-link.ts
18622
+ var ROLE_INTEGRATION_TYPE = {
18623
+ producer: "case-queue-producer",
18624
+ consumer: "case-queue-consumer"
18625
+ };
18626
+ function parseRole(raw) {
18627
+ if (raw === "producer" || raw === "consumer") {
18628
+ return raw;
18629
+ }
18630
+ throw new CliError('--role must be "producer" or "consumer"');
18631
+ }
18632
+ __name(parseRole, "parseRole");
18633
+ var REVISION_PAGE_SIZE = 100;
18634
+ var SLOT_SETTLE_BACKOFF_MS = [250, 500, 1e3, 2e3];
18635
+ function delay(ms) {
18636
+ return new Promise((resolve) => {
18637
+ setTimeout(resolve, ms);
18638
+ });
18639
+ }
18640
+ __name(delay, "delay");
18641
+ function queuesPath(agentId, revisionId, integrationId) {
18642
+ return `/v1/agents/${encodeURIComponent(agentId)}/revisions/${encodeURIComponent(revisionId)}/integrations/${encodeURIComponent(integrationId)}/queues`;
18643
+ }
18644
+ __name(queuesPath, "queuesPath");
18645
+ async function resolveLiveRevisionId(agentId, ctx) {
18646
+ for (let offset = 0; ; offset += REVISION_PAGE_SIZE) {
18647
+ const searchParams = new URLSearchParams({
18648
+ limit: String(REVISION_PAGE_SIZE),
18649
+ offset: String(offset)
18650
+ });
18651
+ const raw = await httpRequest({
18652
+ method: "GET",
18653
+ path: `/v1/agents/${encodeURIComponent(agentId)}/revisions`,
18654
+ searchParams,
18655
+ apiKey: ctx.apiKey,
18656
+ apiBaseUrl: ctx.apiBaseUrl
18657
+ });
18658
+ const parsed = listBuildsResponseSchema2.safeParse(raw);
18659
+ if (!parsed.success) {
18660
+ throw new CliError(
18661
+ `Unexpected response from the API: ${parsed.error.message}`
18662
+ );
18663
+ }
18664
+ const live = parsed.data.builds.find(
18665
+ (build) => build.status === BUILD_STATUS.LIVE
18666
+ );
18667
+ if (live) {
18668
+ return live.id;
18669
+ }
18670
+ const fetched = parsed.data.builds.length;
18671
+ const total = parsed.data.total ?? offset + fetched;
18672
+ if (fetched === 0 || offset + fetched >= total) {
18673
+ break;
18674
+ }
18675
+ }
18676
+ throw new CliError(
18677
+ `No live revision found for agent ${agentId}. Pass --revision <id> to target a specific revision.`
18678
+ );
18679
+ }
18680
+ __name(resolveLiveRevisionId, "resolveLiveRevisionId");
18681
+ async function resolveCatalogIntegrationId(type, ctx) {
18682
+ const raw = await httpRequest({
18683
+ method: "GET",
18684
+ path: "/v1/integrations",
18685
+ apiKey: ctx.apiKey,
18686
+ apiBaseUrl: ctx.apiBaseUrl
18687
+ });
18688
+ const parsed = integrationsListResponseSchema2.safeParse(raw);
18689
+ if (!parsed.success) {
18690
+ throw new CliError(
18691
+ `Unexpected response from the API: ${parsed.error.message}`
18692
+ );
18693
+ }
18694
+ const match = parsed.data.integrations.find(
18695
+ (integration) => integration.type === type
18696
+ );
18697
+ if (!match) {
18698
+ throw new CliError(
18699
+ `No "${type}" integration found in the team catalog. Run \`duvo integrations list\` to inspect it.`
18700
+ );
18701
+ }
18702
+ return match.id;
18703
+ }
18704
+ __name(resolveCatalogIntegrationId, "resolveCatalogIntegrationId");
18705
+ async function listSlotIntegrationIds(agentId, revisionId, ctx) {
18706
+ const raw = await httpRequest({
18707
+ method: "GET",
18708
+ path: `/v1/agents/${encodeURIComponent(agentId)}/revisions/${encodeURIComponent(revisionId)}/integrations`,
18709
+ apiKey: ctx.apiKey,
18710
+ apiBaseUrl: ctx.apiBaseUrl
18711
+ });
18712
+ const parsed = listBuildIntegrationsResponseSchema2.safeParse(raw);
18713
+ if (!parsed.success) {
18714
+ throw new CliError(
18715
+ `Unexpected response from the API: ${parsed.error.message}`
18716
+ );
18717
+ }
18718
+ return parsed.data.integrations.map((config) => config.integration_id).filter((id) => typeof id === "string" && id.length > 0);
18719
+ }
18720
+ __name(listSlotIntegrationIds, "listSlotIntegrationIds");
18721
+ async function isIntegrationAttached(agentId, revisionId, integrationId, ctx) {
18722
+ const ids = await listSlotIntegrationIds(agentId, revisionId, ctx);
18723
+ return ids.includes(integrationId);
18724
+ }
18725
+ __name(isIntegrationAttached, "isIntegrationAttached");
18726
+ async function ensureIntegrationAttached(agentId, revisionId, integrationId, ctx) {
18727
+ if (await isIntegrationAttached(agentId, revisionId, integrationId, ctx)) {
18728
+ return;
18729
+ }
18730
+ for (let attempt = 0; attempt < SLOT_SETTLE_BACKOFF_MS.length; attempt++) {
18731
+ const raw = await httpRequest({
18732
+ method: "POST",
18733
+ path: `/v1/agents/${encodeURIComponent(agentId)}/revisions/${encodeURIComponent(revisionId)}/integrations`,
18734
+ apiKey: ctx.apiKey,
18735
+ apiBaseUrl: ctx.apiBaseUrl,
18736
+ body: { integration_ids: [integrationId] }
18737
+ });
18738
+ const parsed = attachBuildIntegrationsResponseSchema.safeParse(raw);
18739
+ if (!parsed.success) {
18740
+ throw new CliError(
18741
+ `Unexpected response from the API: ${parsed.error.message}`
18742
+ );
18743
+ }
18744
+ if (await isIntegrationAttached(agentId, revisionId, integrationId, ctx)) {
18745
+ return;
18746
+ }
18747
+ if (attempt < SLOT_SETTLE_BACKOFF_MS.length - 1) {
18748
+ await delay(SLOT_SETTLE_BACKOFF_MS[attempt]);
18749
+ }
18750
+ }
18751
+ throw new CliError(
18752
+ `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.`
18753
+ );
18754
+ }
18755
+ __name(ensureIntegrationAttached, "ensureIntegrationAttached");
18756
+ async function getLinkedQueueIds(agentId, revisionId, integrationId, ctx) {
18757
+ const raw = await httpRequest({
18758
+ method: "GET",
18759
+ path: queuesPath(agentId, revisionId, integrationId),
18760
+ apiKey: ctx.apiKey,
18761
+ apiBaseUrl: ctx.apiBaseUrl
18762
+ });
18763
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
18764
+ if (!parsed.success) {
18765
+ throw new CliError(
18766
+ `Unexpected response from the API: ${parsed.error.message}`
18767
+ );
18768
+ }
18769
+ return parsed.data.queues.map((queue) => queue.id);
18770
+ }
18771
+ __name(getLinkedQueueIds, "getLinkedQueueIds");
18772
+ async function createCaseTrigger(agentId, queueId, enabled, concurrency, ctx) {
18773
+ const body = {
18774
+ case_queue_id: queueId,
18775
+ enabled
18776
+ };
18777
+ if (concurrency !== void 0) {
18778
+ body.concurrency = concurrency;
18779
+ }
18780
+ const raw = await httpRequest({
18781
+ method: "POST",
18782
+ path: `/v1/agents/${encodeURIComponent(agentId)}/case-triggers`,
18783
+ apiKey: ctx.apiKey,
18784
+ apiBaseUrl: ctx.apiBaseUrl,
18785
+ body
18786
+ });
18787
+ const parsed = caseTriggerResponseSchema.safeParse(raw);
18788
+ if (!parsed.success) {
18789
+ throw new CliError(
18790
+ `Unexpected response from the API: ${parsed.error.message}`
18791
+ );
18792
+ }
18793
+ return parsed.data.case_trigger;
18794
+ }
18795
+ __name(createCaseTrigger, "createCaseTrigger");
18796
+ async function findCaseTriggerForQueue(agentId, queueId, ctx) {
18797
+ const raw = await httpRequest({
18798
+ method: "GET",
18799
+ path: `/v1/agents/${encodeURIComponent(agentId)}/case-triggers`,
18800
+ apiKey: ctx.apiKey,
18801
+ apiBaseUrl: ctx.apiBaseUrl
18802
+ });
18803
+ const parsed = caseTriggersListResponseSchema.safeParse(raw);
18804
+ if (!parsed.success) {
18805
+ throw new CliError(
18806
+ `Unexpected response from the API: ${parsed.error.message}`
18807
+ );
18808
+ }
18809
+ return parsed.data.case_triggers.find(
18810
+ (trigger) => trigger.case_queue_id === queueId
18811
+ ) ?? null;
18812
+ }
18813
+ __name(findCaseTriggerForQueue, "findCaseTriggerForQueue");
18814
+ async function deleteCaseTrigger(agentId, triggerId, ctx) {
18815
+ const raw = await httpRequest({
18816
+ method: "DELETE",
18817
+ path: `/v1/agents/${encodeURIComponent(agentId)}/case-triggers/${encodeURIComponent(triggerId)}`,
18818
+ apiKey: ctx.apiKey,
18819
+ apiBaseUrl: ctx.apiBaseUrl
18820
+ });
18821
+ const parsed = deleteCaseTriggerResponseSchema.safeParse(raw);
18822
+ if (!parsed.success) {
18823
+ throw new CliError(
18824
+ `Unexpected response from the API: ${parsed.error.message}`
18825
+ );
18826
+ }
18827
+ }
18828
+ __name(deleteCaseTrigger, "deleteCaseTrigger");
18829
+ async function replaceLinkedQueues(agentId, revisionId, integrationId, queueIds, ctx) {
18830
+ const raw = await httpRequest({
18831
+ method: "PUT",
18832
+ path: queuesPath(agentId, revisionId, integrationId),
18833
+ apiKey: ctx.apiKey,
18834
+ apiBaseUrl: ctx.apiBaseUrl,
18835
+ body: { queue_ids: [...queueIds] }
18836
+ });
18837
+ const parsed = buildIntegrationQueuesResponseSchema.safeParse(raw);
18838
+ if (!parsed.success) {
18839
+ throw new CliError(
18840
+ `Unexpected response from the API: ${parsed.error.message}`
18841
+ );
18842
+ }
18843
+ return parsed.data.queues.map((queue) => queue.id);
18844
+ }
18845
+ __name(replaceLinkedQueues, "replaceLinkedQueues");
18846
+
18847
+ // src/commands/queues/link-agent.ts
18848
+ var POSITIVE_INT4 = /^\d+$/;
18849
+ function parseConcurrency3(raw) {
18850
+ if (!POSITIVE_INT4.test(raw)) {
18851
+ throw new CliError("--concurrency must be a positive integer");
18852
+ }
18853
+ const value = Number(raw);
18854
+ if (value <= 0) {
18855
+ throw new CliError("--concurrency must be a positive integer");
18856
+ }
18857
+ return value;
18858
+ }
18859
+ __name(parseConcurrency3, "parseConcurrency");
18860
+ function registerQueuesLinkAgent(queues) {
18861
+ queues.command("link-agent <queue-id>").description(
18862
+ "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."
18863
+ ).requiredOption("--agent <id>", "ID of the agent to link").requiredOption(
18864
+ "--role <role>",
18865
+ 'role to link as: "producer" (creates cases) or "consumer" (handles cases)'
18866
+ ).option(
18867
+ "--revision <id>",
18868
+ "target a specific revision (default: the agent's live revision)"
18869
+ ).option(
18870
+ "--enable-trigger",
18871
+ "for consumers, create the case trigger enabled (default: disabled)"
18872
+ ).option(
18873
+ "--concurrency <n>",
18874
+ "for consumers, max concurrent runs on the case trigger (omit for unlimited)"
18875
+ ).option("--json", "output raw JSON").action(/* @__PURE__ */ __name(async function linkAgentAction(queueId, options) {
18876
+ const trimmedQueueId = trimRequired(queueId, "<queue-id>");
18877
+ const trimmedAgentId = trimRequired(options.agent, "--agent");
18878
+ const role = parseRole(options.role);
18879
+ if (role === "producer" && options.enableTrigger === true) {
18880
+ throw new CliError(
18881
+ "--enable-trigger only applies to consumers (producers have no case trigger)"
18882
+ );
18883
+ }
18884
+ if (role === "producer" && options.concurrency !== void 0) {
18885
+ throw new CliError(
18886
+ "--concurrency only applies to consumers (producers have no case trigger)"
18887
+ );
18888
+ }
18889
+ const concurrency = options.concurrency !== void 0 ? parseConcurrency3(options.concurrency) : void 0;
18890
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
18891
+ const ctx = { apiKey, apiBaseUrl };
18892
+ const revisionId = options.revision !== void 0 ? trimRequired(options.revision, "--revision") : await resolveLiveRevisionId(trimmedAgentId, ctx);
18893
+ const integrationId = await resolveCatalogIntegrationId(
18894
+ ROLE_INTEGRATION_TYPE[role],
18895
+ ctx
18896
+ );
18897
+ await ensureIntegrationAttached(
18898
+ trimmedAgentId,
18899
+ revisionId,
18900
+ integrationId,
18901
+ ctx
18902
+ );
18903
+ const existingQueues = await getLinkedQueueIds(
18904
+ trimmedAgentId,
18905
+ revisionId,
18906
+ integrationId,
18907
+ ctx
18908
+ );
18909
+ const desiredQueues = existingQueues.includes(trimmedQueueId) ? existingQueues : [...existingQueues, trimmedQueueId];
18910
+ const linkedQueues = await replaceLinkedQueues(
18911
+ trimmedAgentId,
18912
+ revisionId,
18913
+ integrationId,
18914
+ desiredQueues,
18915
+ ctx
18916
+ );
18917
+ let trigger = null;
18918
+ if (role === "consumer") {
18919
+ const existingTrigger = await findCaseTriggerForQueue(
18920
+ trimmedAgentId,
18921
+ trimmedQueueId,
18922
+ ctx
18923
+ );
18924
+ trigger = existingTrigger ?? await createCaseTrigger(
18925
+ trimmedAgentId,
18926
+ trimmedQueueId,
18927
+ options.enableTrigger === true,
18928
+ concurrency,
18929
+ ctx
18930
+ );
18931
+ }
18932
+ if (options.json) {
18933
+ printJson({
18934
+ revision_id: revisionId,
18935
+ integration_id: integrationId,
18936
+ linked_queue_ids: linkedQueues,
18937
+ case_trigger: trigger
18938
+ });
18939
+ return;
18940
+ }
18941
+ console.log(
18942
+ green(
18943
+ `Linked agent ${trimmedAgentId} to queue ${trimmedQueueId} as ${role}.`
18944
+ )
18945
+ );
18946
+ const entries = [
18947
+ { label: "Revision", value: revisionId },
18948
+ { label: "Integration", value: integrationId },
18949
+ { label: "Linked queues", value: String(linkedQueues.length) }
18950
+ ];
18951
+ if (trigger !== null) {
18952
+ entries.push({ label: "Case trigger", value: trigger.id });
18953
+ entries.push({
18954
+ label: "Trigger enabled",
18955
+ value: trigger.enabled ? "yes" : "no"
18956
+ });
18957
+ }
18958
+ console.log(renderKeyValue(entries));
18959
+ if (role === "consumer" && trigger !== null && !trigger.enabled) {
18960
+ console.log("");
18961
+ console.log(
18962
+ yellow(
18963
+ "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`."
18964
+ )
18965
+ );
18966
+ }
18967
+ }, "linkAgentAction"));
18968
+ }
18969
+ __name(registerQueuesLinkAgent, "registerQueuesLinkAgent");
18970
+
18351
18971
  // src/commands/queues/list.ts
18352
18972
  var DEFAULT_LIMIT7 = 20;
18353
18973
  var MIN_LIMIT7 = 1;
18354
18974
  var MAX_LIMIT7 = 100;
18355
- var TABLE_COLUMNS22 = [
18975
+ var TABLE_COLUMNS24 = [
18356
18976
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18357
18977
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
18358
18978
  {
@@ -18396,7 +19016,7 @@ function registerQueuesList(queues) {
18396
19016
  console.log("No queues found.");
18397
19017
  return;
18398
19018
  }
18399
- console.log(renderTable(TABLE_COLUMNS22, response.queues));
19019
+ console.log(renderTable(TABLE_COLUMNS24, response.queues));
18400
19020
  const resolvedOffset = response.offset ?? offset;
18401
19021
  const total = response.total ?? null;
18402
19022
  const summary = total === null ? `Showing ${response.queues.length} \xB7 offset ${resolvedOffset}` : `Showing ${response.queues.length} of ${total} \xB7 offset ${resolvedOffset}`;
@@ -18405,6 +19025,96 @@ function registerQueuesList(queues) {
18405
19025
  }
18406
19026
  __name(registerQueuesList, "registerQueuesList");
18407
19027
 
19028
+ // src/commands/queues/unlink-agent.ts
19029
+ function registerQueuesUnlinkAgent(queues) {
19030
+ queues.command("unlink-agent <queue-id>").description(
19031
+ "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."
19032
+ ).requiredOption("--agent <id>", "ID of the agent to unlink").requiredOption("--role <role>", 'role to unlink: "producer" or "consumer"').option(
19033
+ "--revision <id>",
19034
+ "target a specific revision (default: the agent's live revision)"
19035
+ ).option("--json", "output raw JSON").action(/* @__PURE__ */ __name(async function unlinkAgentAction(queueId, options) {
19036
+ const trimmedQueueId = trimRequired(queueId, "<queue-id>");
19037
+ const trimmedAgentId = trimRequired(options.agent, "--agent");
19038
+ const role = parseRole(options.role);
19039
+ const { apiKey, apiBaseUrl } = await requireAuthenticatedConfig(this);
19040
+ const ctx = { apiKey, apiBaseUrl };
19041
+ const revisionId = options.revision !== void 0 ? trimRequired(options.revision, "--revision") : await resolveLiveRevisionId(trimmedAgentId, ctx);
19042
+ const integrationId = await resolveCatalogIntegrationId(
19043
+ ROLE_INTEGRATION_TYPE[role],
19044
+ ctx
19045
+ );
19046
+ let queueUnlinked = false;
19047
+ const attached = await isIntegrationAttached(
19048
+ trimmedAgentId,
19049
+ revisionId,
19050
+ integrationId,
19051
+ ctx
19052
+ );
19053
+ if (attached) {
19054
+ const existingQueues = await getLinkedQueueIds(
19055
+ trimmedAgentId,
19056
+ revisionId,
19057
+ integrationId,
19058
+ ctx
19059
+ );
19060
+ if (existingQueues.includes(trimmedQueueId)) {
19061
+ await replaceLinkedQueues(
19062
+ trimmedAgentId,
19063
+ revisionId,
19064
+ integrationId,
19065
+ existingQueues.filter((id) => id !== trimmedQueueId),
19066
+ ctx
19067
+ );
19068
+ queueUnlinked = true;
19069
+ }
19070
+ }
19071
+ let triggerRemoved = false;
19072
+ if (role === "consumer") {
19073
+ const trigger = await findCaseTriggerForQueue(
19074
+ trimmedAgentId,
19075
+ trimmedQueueId,
19076
+ ctx
19077
+ );
19078
+ if (trigger !== null) {
19079
+ await deleteCaseTrigger(trimmedAgentId, trigger.id, ctx);
19080
+ triggerRemoved = true;
19081
+ }
19082
+ }
19083
+ if (options.json) {
19084
+ printJson({
19085
+ revision_id: revisionId,
19086
+ integration_id: integrationId,
19087
+ queue_unlinked: queueUnlinked,
19088
+ case_trigger_removed: triggerRemoved
19089
+ });
19090
+ return;
19091
+ }
19092
+ if (!queueUnlinked && !triggerRemoved) {
19093
+ console.log(
19094
+ `Agent ${trimmedAgentId} was not linked to queue ${trimmedQueueId} as ${role}; nothing to do.`
19095
+ );
19096
+ return;
19097
+ }
19098
+ console.log(
19099
+ green(
19100
+ `Unlinked agent ${trimmedAgentId} from queue ${trimmedQueueId} as ${role}.`
19101
+ )
19102
+ );
19103
+ console.log(
19104
+ renderKeyValue([
19105
+ { label: "Revision", value: revisionId },
19106
+ { label: "Integration", value: integrationId },
19107
+ { label: "Queue unlinked", value: queueUnlinked ? "yes" : "no" },
19108
+ {
19109
+ label: "Case trigger removed",
19110
+ value: triggerRemoved ? "yes" : "no"
19111
+ }
19112
+ ])
19113
+ );
19114
+ }, "unlinkAgentAction"));
19115
+ }
19116
+ __name(registerQueuesUnlinkAgent, "registerQueuesUnlinkAgent");
19117
+
18408
19118
  // src/commands/queues/update.ts
18409
19119
  function registerQueuesUpdate(queues) {
18410
19120
  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 +19204,8 @@ function registerQueues(program) {
18494
19204
  registerQueuesUpdate(queues);
18495
19205
  registerQueuesDelete(queues);
18496
19206
  registerQueuesAgents(queues);
19207
+ registerQueuesLinkAgent(queues);
19208
+ registerQueuesUnlinkAgent(queues);
18497
19209
  }
18498
19210
  __name(registerQueues, "registerQueues");
18499
19211
 
@@ -18558,7 +19270,7 @@ function registerRevisionLoginsDetach(revisionLogins) {
18558
19270
  __name(registerRevisionLoginsDetach, "registerRevisionLoginsDetach");
18559
19271
 
18560
19272
  // src/commands/revision-logins/list.ts
18561
- var TABLE_COLUMNS23 = [
19273
+ var TABLE_COLUMNS25 = [
18562
19274
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18563
19275
  { header: "DOMAIN", cell: /* @__PURE__ */ __name((row) => row.domain, "cell") },
18564
19276
  {
@@ -18594,7 +19306,7 @@ function registerRevisionLoginsList(revisionLogins) {
18594
19306
  console.log("No browser logins attached to this revision.");
18595
19307
  return;
18596
19308
  }
18597
- console.log(renderTable(TABLE_COLUMNS23, rows));
19309
+ console.log(renderTable(TABLE_COLUMNS25, rows));
18598
19310
  console.log(`Showing ${rows.length}`);
18599
19311
  }, "listAction"));
18600
19312
  }
@@ -18667,7 +19379,7 @@ function registerRevisionSecretsDetach(revisionSecrets) {
18667
19379
  __name(registerRevisionSecretsDetach, "registerRevisionSecretsDetach");
18668
19380
 
18669
19381
  // src/commands/revision-secrets/list.ts
18670
- var TABLE_COLUMNS24 = [
19382
+ var TABLE_COLUMNS26 = [
18671
19383
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18672
19384
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
18673
19385
  {
@@ -18705,7 +19417,7 @@ function registerRevisionSecretsList(revisionSecrets) {
18705
19417
  console.log("No secrets attached to this revision.");
18706
19418
  return;
18707
19419
  }
18708
- console.log(renderTable(TABLE_COLUMNS24, rows));
19420
+ console.log(renderTable(TABLE_COLUMNS26, rows));
18709
19421
  console.log(`Showing ${rows.length}`);
18710
19422
  }, "listAction"));
18711
19423
  }
@@ -18874,7 +19586,7 @@ var MAX_LIMIT8 = 100;
18874
19586
  var SORT_ORDERS2 = ["asc", "desc"];
18875
19587
  var HAS_ISSUES_VALUES = ["true", "false"];
18876
19588
  var TITLE_PREVIEW_WIDTH = 50;
18877
- var TABLE_COLUMNS25 = [
19589
+ var TABLE_COLUMNS27 = [
18878
19590
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
18879
19591
  { header: "RUN#", cell: /* @__PURE__ */ __name((row) => String(row.run_number), "cell") },
18880
19592
  { header: "STATUS", cell: /* @__PURE__ */ __name((row) => row.status, "cell") },
@@ -18971,7 +19683,7 @@ function registerRunsList(runs) {
18971
19683
  console.log("No runs found.");
18972
19684
  return;
18973
19685
  }
18974
- console.log(renderTable(TABLE_COLUMNS25, response.data));
19686
+ console.log(renderTable(TABLE_COLUMNS27, response.data));
18975
19687
  console.log(
18976
19688
  `Showing ${response.data.length} of ${response.total} \xB7 offset ${offset}`
18977
19689
  );
@@ -19081,7 +19793,7 @@ var DEFAULT_LIMIT9 = 20;
19081
19793
  var MIN_LIMIT9 = 1;
19082
19794
  var MAX_LIMIT9 = 50;
19083
19795
  var TEXT_PREVIEW_WIDTH2 = 60;
19084
- var TABLE_COLUMNS26 = [
19796
+ var TABLE_COLUMNS28 = [
19085
19797
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
19086
19798
  { header: "ROLE", cell: /* @__PURE__ */ __name((row) => row.role, "cell") },
19087
19799
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
@@ -19146,7 +19858,7 @@ function registerRunsMessages(runs) {
19146
19858
  console.log("No messages found.");
19147
19859
  return;
19148
19860
  }
19149
- console.log(renderTable(TABLE_COLUMNS26, response.messages));
19861
+ console.log(renderTable(TABLE_COLUMNS28, response.messages));
19150
19862
  const resolvedOffset = response.offset ?? offset;
19151
19863
  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
19864
  console.log(summary);
@@ -19467,7 +20179,7 @@ function registerSandboxesCreate(sandboxes) {
19467
20179
  __name(registerSandboxesCreate, "registerSandboxesCreate");
19468
20180
 
19469
20181
  // src/commands/sandboxes/files.ts
19470
- var TABLE_COLUMNS27 = [
20182
+ var TABLE_COLUMNS29 = [
19471
20183
  { header: "TYPE", cell: /* @__PURE__ */ __name((row) => row.type, "cell") },
19472
20184
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
19473
20185
  { header: "PATH", cell: /* @__PURE__ */ __name((row) => row.path, "cell") },
@@ -19505,7 +20217,7 @@ function registerSandboxesFiles(sandboxes) {
19505
20217
  console.log("No files found.");
19506
20218
  return;
19507
20219
  }
19508
- console.log(renderTable(TABLE_COLUMNS27, files));
20220
+ console.log(renderTable(TABLE_COLUMNS29, files));
19509
20221
  console.log(`Showing ${files.length}`);
19510
20222
  }, "filesAction"));
19511
20223
  }
@@ -19846,7 +20558,7 @@ function registerSecretsGet(secrets) {
19846
20558
  __name(registerSecretsGet, "registerSecretsGet");
19847
20559
 
19848
20560
  // src/commands/secrets/list.ts
19849
- var TABLE_COLUMNS28 = [
20561
+ var TABLE_COLUMNS30 = [
19850
20562
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
19851
20563
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
19852
20564
  {
@@ -19886,7 +20598,7 @@ function registerSecretsList(secrets) {
19886
20598
  console.log("No secrets found.");
19887
20599
  return;
19888
20600
  }
19889
- console.log(renderTable(TABLE_COLUMNS28, rows));
20601
+ console.log(renderTable(TABLE_COLUMNS30, rows));
19890
20602
  console.log(`Showing ${rows.length}`);
19891
20603
  }, "listAction"));
19892
20604
  }
@@ -19997,7 +20709,7 @@ function registerSecrets(program) {
19997
20709
  __name(registerSecrets, "registerSecrets");
19998
20710
 
19999
20711
  // src/commands/skills/assignments.ts
20000
- var TABLE_COLUMNS29 = [
20712
+ var TABLE_COLUMNS31 = [
20001
20713
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
20002
20714
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") }
20003
20715
  ];
@@ -20025,7 +20737,7 @@ function registerSkillsAssignments(skills) {
20025
20737
  console.log("No assignments use this skill.");
20026
20738
  return;
20027
20739
  }
20028
- console.log(renderTable(TABLE_COLUMNS29, assignments));
20740
+ console.log(renderTable(TABLE_COLUMNS31, assignments));
20029
20741
  console.log(`Showing ${assignments.length} of ${total}`);
20030
20742
  }, "assignmentsAction"));
20031
20743
  }
@@ -20303,7 +21015,7 @@ function readContent2(options) {
20303
21015
  __name(readContent2, "readContent");
20304
21016
 
20305
21017
  // src/commands/skills/files.ts
20306
- var TABLE_COLUMNS30 = [
21018
+ var TABLE_COLUMNS32 = [
20307
21019
  { header: "PATH", cell: /* @__PURE__ */ __name((row) => row.path, "cell") },
20308
21020
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name, "cell") },
20309
21021
  {
@@ -20342,7 +21054,7 @@ function registerSkillsFiles(skills) {
20342
21054
  console.log("No files found.");
20343
21055
  return;
20344
21056
  }
20345
- console.log(renderTable(TABLE_COLUMNS30, parsed.data.files));
21057
+ console.log(renderTable(TABLE_COLUMNS32, parsed.data.files));
20346
21058
  console.log(`Showing ${parsed.data.files.length}`);
20347
21059
  }, "filesAction"));
20348
21060
  }
@@ -20741,7 +21453,7 @@ __name(registerTeamGet, "registerTeamGet");
20741
21453
  var DEFAULT_LIMIT10 = 50;
20742
21454
  var MIN_LIMIT10 = 1;
20743
21455
  var MAX_LIMIT10 = 100;
20744
- var TABLE_COLUMNS31 = [
21456
+ var TABLE_COLUMNS33 = [
20745
21457
  { header: "ID", cell: /* @__PURE__ */ __name((row) => row.id, "cell") },
20746
21458
  { header: "NAME", cell: /* @__PURE__ */ __name((row) => row.name ?? "", "cell") },
20747
21459
  { header: "EMAIL", cell: /* @__PURE__ */ __name((row) => row.email, "cell") },
@@ -20789,7 +21501,7 @@ function registerTeamMembers(team) {
20789
21501
  console.log("No members found.");
20790
21502
  return;
20791
21503
  }
20792
- console.log(renderTable(TABLE_COLUMNS31, response.members));
21504
+ console.log(renderTable(TABLE_COLUMNS33, response.members));
20793
21505
  console.log(
20794
21506
  `Showing ${response.members.length} of ${response.total} \xB7 offset ${response.offset}`
20795
21507
  );
@@ -20856,7 +21568,7 @@ __name(fetchProfileSummary, "fetchProfileSummary");
20856
21568
  var version = resolveVersion();
20857
21569
  function resolveVersion() {
20858
21570
  if (true) {
20859
- return "0.8.0";
21571
+ return "0.9.1";
20860
21572
  }
20861
21573
  const packageJsonPath = join2(
20862
21574
  dirname2(fileURLToPath(import.meta.url)),
@@ -20921,7 +21633,7 @@ function checkForUpdate() {
20921
21633
  __name(checkForUpdate, "checkForUpdate");
20922
21634
  function loadPackageMeta() {
20923
21635
  if (true) {
20924
- return { name: "@duvoai/cli", version: "0.8.0" };
21636
+ return { name: "@duvoai/cli", version: "0.9.1" };
20925
21637
  }
20926
21638
  const require2 = createRequire(import.meta.url);
20927
21639
  const raw = require2("../package.json");