@dench.com/cli 3.8.5-staging.406 → 3.9.0-staging.415

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.
@@ -1346,6 +1346,59 @@ const crmFieldDoc = z
1346
1346
  .passthrough()
1347
1347
  .describe("A CRM field (column) document.");
1348
1348
 
1349
+ // Presentation metadata is separate from CRM field maps and optional on raw results.
1350
+ const referenceIdentity = {
1351
+ label: z.string(),
1352
+ href: z.string(),
1353
+ url: z.string().optional(),
1354
+ imageUrl: z.string().optional(),
1355
+ objectIcon: z.string().optional(),
1356
+ };
1357
+ const workspaceReference = z.discriminatedUnion("kind", [
1358
+ z.object({
1359
+ kind: z.literal("crm-entry"),
1360
+ objectName: z.string(),
1361
+ entryId: z.string(),
1362
+ ...referenceIdentity,
1363
+ }),
1364
+ z.object({
1365
+ kind: z.literal("crm-object"),
1366
+ objectName: z.string(),
1367
+ ...referenceIdentity,
1368
+ }),
1369
+ z.object({
1370
+ kind: z.literal("campaign"),
1371
+ sequenceId: z.string(),
1372
+ ...referenceIdentity,
1373
+ }),
1374
+ ]);
1375
+ const workspaceReferenceFields = {
1376
+ references: z.array(workspaceReference).max(100),
1377
+ referenceCoverage: z.object({
1378
+ returned: z.number().int().nonnegative(),
1379
+ available: z.number().int().nonnegative(),
1380
+ truncated: z.boolean(),
1381
+ }),
1382
+ };
1383
+ const optionalWorkspaceReferenceFields = {
1384
+ references: workspaceReferenceFields.references.optional(),
1385
+ referenceCoverage: workspaceReferenceFields.referenceCoverage.optional(),
1386
+ };
1387
+ const withReferencesRequest = {
1388
+ withReferences: z
1389
+ .boolean()
1390
+ .optional()
1391
+ .describe(
1392
+ "Include bounded navigation references. Array reads return {data, references, referenceCoverage}; existing result envelopes keep their fields.",
1393
+ ),
1394
+ };
1395
+ function referenceListResponse<T extends z.ZodTypeAny>(row: T) {
1396
+ return z.union([
1397
+ z.array(row),
1398
+ z.object({ data: z.array(row), ...workspaceReferenceFields }),
1399
+ ]);
1400
+ }
1401
+
1349
1402
  const crmEntryShape = z
1350
1403
  .object({
1351
1404
  id: z.string(),
@@ -1358,7 +1411,10 @@ const crmEntryShape = z
1358
1411
  })
1359
1412
  .describe("A materialized CRM entry (row).");
1360
1413
 
1361
- const entryWriteResponse = z.object({ entryId: z.string() });
1414
+ const entryWriteResponse = z.object({
1415
+ entryId: z.string(),
1416
+ ...optionalWorkspaceReferenceFields,
1417
+ });
1362
1418
 
1363
1419
  const commentActorShape = z
1364
1420
  .object({
@@ -1494,6 +1550,7 @@ const notificationPreferencesShape = z.object({
1494
1550
  });
1495
1551
 
1496
1552
  const crmBatchResults = z.object({
1553
+ ...optionalWorkspaceReferenceFields,
1497
1554
  results: z.array(
1498
1555
  z
1499
1556
  .object({
@@ -3283,6 +3340,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3283
3340
  "campaigns.accounts.linkedin": noInput,
3284
3341
  "campaigns.accounts.email": noInput,
3285
3342
  "campaigns.list": z.object({
3343
+ ...withReferencesRequest,
3286
3344
  includeArchived: z
3287
3345
  .boolean()
3288
3346
  .optional()
@@ -3882,7 +3940,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3882
3940
  "chat.follow": z.object({ threadId: z.string() }),
3883
3941
 
3884
3942
  // CRM objects
3885
- "crm.objects.list": noInput,
3943
+ "crm.objects.list": z.object(withReferencesRequest),
3886
3944
  "crm.objects.listCompact": noInput,
3887
3945
  "crm.objects.get": z.object({ name: z.string() }),
3888
3946
  "crm.objects.create": z.object({
@@ -4086,6 +4144,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
4086
4144
 
4087
4145
  // CRM entries
4088
4146
  "crm.entries.list": z.object({
4147
+ ...withReferencesRequest,
4089
4148
  objectName: z.string(),
4090
4149
  limit: z.coerce.number().int().min(1).max(1000).optional(),
4091
4150
  ...assigneeScopeRequest,
@@ -4473,6 +4532,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
4473
4532
 
4474
4533
  // CRM query
4475
4534
  "crm.query": z.object({
4535
+ ...withReferencesRequest,
4476
4536
  objectName: z.string(),
4477
4537
  dsl: z
4478
4538
  .object({
@@ -4497,6 +4557,17 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
4497
4557
  ),
4498
4558
  ...assigneeScopeRequest,
4499
4559
  }),
4560
+ "crm.queryPage": z.object({
4561
+ objectName: z.string(),
4562
+ dsl: z.record(z.string(), z.unknown()).optional(),
4563
+ dslJson: z.string().optional(),
4564
+ ...assigneeScopeRequest,
4565
+ ...withReferencesRequest,
4566
+ paginationOpts: z.object({
4567
+ numItems: z.number().int().min(1).max(100),
4568
+ cursor: z.string().nullable(),
4569
+ }),
4570
+ }),
4500
4571
  "crm.sql": z.object({
4501
4572
  objectName: z.string(),
4502
4573
  dsl: z.record(z.string(), z.unknown()).optional(),
@@ -4513,6 +4584,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
4513
4584
  ),
4514
4585
  }),
4515
4586
  "crm.search.page": z.object({
4587
+ ...withReferencesRequest,
4516
4588
  text: z.string().describe("Full-text query over readable entries."),
4517
4589
  objectName: z.string().optional(),
4518
4590
  select: z.array(z.string().min(1)).min(1).max(30).optional(),
@@ -4525,6 +4597,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
4525
4597
  ),
4526
4598
  }),
4527
4599
  "crm.search": z.object({
4600
+ ...withReferencesRequest,
4528
4601
  text: z.string().describe("Full-text query."),
4529
4602
  objectName: z.string().optional().describe("Omit to search org-wide."),
4530
4603
  limit: z.coerce.number().int().min(1).max(100).optional(),
@@ -4546,6 +4619,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
4546
4619
 
4547
4620
  // CRM batch + transactions
4548
4621
  "crm.batch": z.object({
4622
+ ...withReferencesRequest,
4549
4623
  ops: z.array(crmOp).max(200),
4550
4624
  idempotencyKey: z.string().optional(),
4551
4625
  }),
@@ -5260,7 +5334,10 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5260
5334
  previewRows: z.array(z.unknown()),
5261
5335
  title: z.string(),
5262
5336
  }),
5263
- "campaigns.drafts.create": z.object({ sequenceId: z.string() }),
5337
+ "campaigns.drafts.create": z.object({
5338
+ sequenceId: z.string(),
5339
+ ...optionalWorkspaceReferenceFields,
5340
+ }),
5264
5341
  "campaigns.audience.search": campaignSearchPage,
5265
5342
  "campaigns.people.addBatch": campaignBatchResult,
5266
5343
  "campaigns.people.addFromSearch": campaignSearchPage
@@ -5321,15 +5398,20 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5321
5398
  // Outreach campaigns (dench campaign)
5322
5399
  "campaigns.accounts.linkedin": z.array(campaignLinkedinSenderSummary),
5323
5400
  "campaigns.accounts.email": z.array(campaignMailboxSummary),
5324
- "campaigns.list": z.array(campaignRow),
5325
- "campaigns.create": z.object({ sequenceId: z.string() }),
5401
+ "campaigns.list": referenceListResponse(campaignRow),
5402
+ "campaigns.create": z.object({
5403
+ sequenceId: z.string(),
5404
+ ...optionalWorkspaceReferenceFields,
5405
+ }),
5326
5406
  "campaigns.duplicate": z.object({
5407
+ ...optionalWorkspaceReferenceFields,
5327
5408
  sequenceId: z
5328
5409
  .string()
5329
5410
  .describe("The new draft. Rename it with campaigns.update."),
5330
5411
  }),
5331
5412
  "campaigns.get": z
5332
5413
  .object({
5414
+ ...optionalWorkspaceReferenceFields,
5333
5415
  sequence: campaignRow,
5334
5416
  steps: z
5335
5417
  .array(campaignStepInput.passthrough())
@@ -5719,7 +5801,7 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5719
5801
  ),
5720
5802
 
5721
5803
  // CRM objects
5722
- "crm.objects.list": z.array(crmObjectDoc),
5804
+ "crm.objects.list": referenceListResponse(crmObjectDoc),
5723
5805
  "crm.objects.listCompact": z.array(
5724
5806
  z.object({
5725
5807
  _id: z.string(),
@@ -5732,8 +5814,13 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5732
5814
  archived: z.boolean(),
5733
5815
  }),
5734
5816
  ),
5735
- "crm.objects.get": crmObjectDoc.nullable(),
5736
- "crm.objects.create": z.object({ id: z.string() }),
5817
+ "crm.objects.get": crmObjectDoc
5818
+ .extend(optionalWorkspaceReferenceFields)
5819
+ .nullable(),
5820
+ "crm.objects.create": z.object({
5821
+ id: z.string(),
5822
+ ...optionalWorkspaceReferenceFields,
5823
+ }),
5737
5824
  "crm.objects.update": okResponse,
5738
5825
  "crm.objects.rename": okResponse,
5739
5826
  "crm.objects.delete": okResponse,
@@ -5833,7 +5920,7 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5833
5920
  }),
5834
5921
 
5835
5922
  // CRM entries + cells
5836
- "crm.entries.list": z.array(crmEntryShape),
5923
+ "crm.entries.list": referenceListResponse(crmEntryShape),
5837
5924
  "crm.views.entries": z
5838
5925
  .object({
5839
5926
  entries: z
@@ -5855,9 +5942,12 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5855
5942
  .describe(
5856
5943
  "The same payload the workspace table renders from (fields, statuses, savedViews, …).",
5857
5944
  ),
5858
- "crm.entries.get": crmEntryShape.nullable(),
5945
+ "crm.entries.get": crmEntryShape
5946
+ .extend(optionalWorkspaceReferenceFields)
5947
+ .nullable(),
5859
5948
  "crm.entries.create": entryWriteResponse,
5860
5949
  "crm.entries.createMany": z.object({
5950
+ ...optionalWorkspaceReferenceFields,
5861
5951
  results: z.array(z.object({ entryId: z.string() })),
5862
5952
  count: z.number(),
5863
5953
  }),
@@ -5939,11 +6029,11 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5939
6029
  "settings.sync.update": syncSettingsShape,
5940
6030
 
5941
6031
  // CRM query + search
5942
- "crm.query": z
5943
- .array(crmEntryShape)
5944
- .describe(
5945
- "Entry rows. With dsl.select the rows are flat {_id, ...selectedFields} objects instead.",
5946
- ),
6032
+ "crm.query": referenceListResponse(
6033
+ z.record(z.string(), z.unknown()),
6034
+ ).describe(
6035
+ "Entry rows. With dsl.select the rows are flat {_id, ...selectedFields} objects instead.",
6036
+ ),
5947
6037
  "crm.sql": z
5948
6038
  .array(crmEntryShape)
5949
6039
  .describe(
@@ -5957,6 +6047,7 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5957
6047
  }),
5958
6048
  ),
5959
6049
  "crm.search.page": z.object({
6050
+ ...optionalWorkspaceReferenceFields,
5960
6051
  page: z.array(
5961
6052
  z.object({ _id: z.string(), objectId: z.string(), data: fieldsMap }),
5962
6053
  ),
@@ -5966,7 +6057,7 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5966
6057
  selectedFields: z.array(z.string()).nullable(),
5967
6058
  completeness: z.literal("page"),
5968
6059
  }),
5969
- "crm.search": z.array(
6060
+ "crm.search": referenceListResponse(
5970
6061
  z
5971
6062
  .object({
5972
6063
  _id: z.string(),
@@ -6551,11 +6642,30 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
6551
6642
  // ---------------------------------------------------------------------------
6552
6643
 
6553
6644
  export type ConvexExtras = {
6645
+ withReferencesFunctionName?: string;
6554
6646
  fixedArgs?: Record<string, unknown>;
6555
6647
  transformArgs?: (input: Record<string, unknown>) => Record<string, unknown>;
6556
6648
  };
6557
6649
 
6558
6650
  export const convexExtras: Record<string, ConvexExtras> = {
6651
+ "campaigns.list": {
6652
+ withReferencesFunctionName:
6653
+ "functions/outreachSequences:listSequencesWithReferences",
6654
+ },
6655
+ "crm.objects.list": {
6656
+ withReferencesFunctionName: "functions/crm/objects:listWithReferences",
6657
+ },
6658
+ "crm.entries.list": {
6659
+ withReferencesFunctionName: "functions/crm/entries:listWithReferences",
6660
+ },
6661
+ "crm.query": {
6662
+ withReferencesFunctionName:
6663
+ "functions/crm/query:runQueryEntriesWithReferences",
6664
+ },
6665
+ "crm.search": {
6666
+ withReferencesFunctionName: "functions/crm/query:searchWithReferences",
6667
+ },
6668
+
6559
6669
  "campaigns.steps.replace": {
6560
6670
  transformArgs: ({ allowSingleBraces: _allowSingleBraces, ...input }) =>
6561
6671
  input,
@@ -164,7 +164,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
164
164
  {"id":"chat.new","group":"chat","summary":"Start a new chat-turn workflow.","cli":"dench chat new","method":"POST","path":"/chat/threads","permission":"ai:chat","pathParams":[]},
165
165
  {"id":"chat.send","group":"chat","summary":"Send a message to an existing chat thread.","cli":"dench chat send","method":"POST","path":"/chat/threads/{threadId}/messages","permission":"ai:chat","pathParams":["threadId"]},
166
166
  {"id":"chat.follow","group":"chat","summary":"Follow a live chat response stream.","cli":"dench chat follow","method":"GET","path":"/chat/threads/{threadId}/stream","permission":"ai:chat","pathParams":["threadId"]},
167
- {"id":"crm.objects.list","group":"crm","summary":"List CRM objects.","cli":"dench crm objects list","method":"GET","path":"/crm/objects","permission":"crm:read","pathParams":[]},
167
+ {"id":"crm.objects.list","group":"crm","summary":"List CRM objects.","cli":"dench crm objects list [--with-references]","method":"GET","path":"/crm/objects","permission":"crm:read","pathParams":[]},
168
168
  {"id":"crm.objects.listCompact","group":"crm","summary":"List names, display labels, visibility and workspace counts without internal state.","cli":"dench crm objects list --compact","method":"GET","path":"/crm/catalog/objects","permission":"crm:read","pathParams":[]},
169
169
  {"id":"crm.objects.get","group":"crm","summary":"Get a CRM object.","cli":"dench crm objects get","method":"GET","path":"/crm/objects/{name}","permission":"crm:read","pathParams":["name"]},
170
170
  {"id":"crm.objects.create","group":"crm","summary":"Create a CRM object.","cli":"dench crm objects create","method":"POST","path":"/crm/objects","permission":"crm:write","pathParams":[]},
@@ -200,7 +200,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
200
200
  {"id":"crm.fields.enum.remove","group":"crm","summary":"Remove an enum value.","cli":"dench crm fields enum-remove","method":"DELETE","path":"/crm/objects/{objectName}/fields/{fieldName}/enum/{value}","permission":"crm:write","pathParams":["objectName","fieldName","value"]},
201
201
  {"id":"crm.fields.enum.rename","group":"crm","summary":"Rename an enum value.","cli":"dench crm fields enum-rename","method":"POST","path":"/crm/objects/{objectName}/fields/{fieldName}/enum/{oldValue}/rename","permission":"crm:write","pathParams":["objectName","fieldName","oldValue"]},
202
202
  {"id":"crm.fields.enum.color","group":"crm","summary":"Set an enum color.","cli":"dench crm fields enum-set-color","method":"POST","path":"/crm/objects/{objectName}/fields/{fieldName}/enum/{value}/color","permission":"crm:write","pathParams":["objectName","fieldName","value"]},
203
- {"id":"crm.entries.list","group":"crm","summary":"List CRM entries.","cli":"dench crm entries list","method":"GET","path":"/crm/objects/{objectName}/entries","permission":"crm:read","pathParams":["objectName"]},
203
+ {"id":"crm.entries.list","group":"crm","summary":"List CRM entries.","cli":"dench crm entries list [--with-references]","method":"GET","path":"/crm/objects/{objectName}/entries","permission":"crm:read","pathParams":["objectName"]},
204
204
  {"id":"crm.entries.get","group":"crm","summary":"Get a CRM entry.","cli":"dench crm entries get","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}","permission":"crm:read","pathParams":["objectName","entryId"]},
205
205
  {"id":"crm.entries.syncHistory","group":"crm","summary":"Queue a full, paged pull of the record's email history from every connected mailbox; the ledger fills as pages land.","cli":"dench crm entries sync-history","method":"POST","path":"/crm/objects/{objectName}/entries/{entryId}/sync-history","permission":"crm:write","pathParams":["objectName","entryId"]},
206
206
  {"id":"crm.entries.syncHistoryStatus","group":"crm","summary":"Has Dench read this record's email history? One row per address per mailbox.","cli":"dench crm entries sync-history --status","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}/sync-history","permission":"crm:read","pathParams":["objectName","entryId"]},
@@ -258,11 +258,11 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
258
258
  {"id":"crm.cells.set","group":"crm","summary":"Set a CRM cell value.","cli":"dench crm cells set","method":"PUT","path":"/crm/objects/{objectName}/entries/{entryId}/fields/{fieldName}","permission":"crm:write","pathParams":["objectName","entryId","fieldName"]},
259
259
  {"id":"crm.cells.setMany","group":"crm","summary":"Set many CRM cell values.","cli":"dench crm cells set-many","method":"POST","path":"/crm/cells/batch","permission":"crm:write","pathParams":[]},
260
260
  {"id":"crm.cells.append","group":"crm","summary":"Append to a CRM cell.","cli":"dench crm cells append","method":"POST","path":"/crm/objects/{objectName}/entries/{entryId}/fields/{fieldName}/append","permission":"crm:write","pathParams":["objectName","entryId","fieldName"]},
261
- {"id":"crm.query","group":"crm","summary":"Query CRM entries.","cli":"dench crm query","method":"POST","path":"/crm/query","permission":"crm:read","pathParams":[]},
261
+ {"id":"crm.query","group":"crm","summary":"Query CRM entries.","cli":"dench crm query [--with-references]","method":"POST","path":"/crm/query","permission":"crm:read","pathParams":[]},
262
262
  {"id":"crm.queryPage","group":"crm","summary":"Scan CRM entries with a resumable cursor. Empty filtered pages can have more results.","cli":"dench crm query --page","method":"POST","path":"/crm/query/page","permission":"crm:read","pathParams":[]},
263
263
  {"id":"crm.sql","group":"crm","summary":"Run CRM SQL-compatible query.","cli":"dench crm sql","method":"POST","path":"/crm/sql","permission":"crm:read","pathParams":[]},
264
264
  {"id":"crm.aggregate","group":"crm","summary":"Aggregate CRM entries.","cli":"dench crm aggregate","method":"POST","path":"/crm/aggregate","permission":"crm:read","pathParams":[]},
265
- {"id":"crm.search","group":"crm","summary":"Search CRM entries.","cli":"dench crm search","method":"GET","path":"/crm/search","permission":"crm:read","pathParams":[]},
265
+ {"id":"crm.search","group":"crm","summary":"Search CRM entries.","cli":"dench crm search [--with-references]","method":"GET","path":"/crm/search","permission":"crm:read","pathParams":[]},
266
266
  {"id":"crm.search.page","group":"crm","summary":"Search bounded pages of readable CRM entries, optionally projecting cells. Always follow continueCursor until isDone.","cli":"dench crm search --page","method":"GET","path":"/crm/search/page","permission":"crm:read","pathParams":[]},
267
267
  {"id":"crm.statuses.list","group":"crm","summary":"List CRM statuses.","cli":"dench crm statuses list","method":"GET","path":"/crm/objects/{objectName}/statuses","permission":"crm:read","pathParams":["objectName"]},
268
268
  {"id":"crm.statuses.set","group":"crm","summary":"Set CRM statuses.","cli":"dench crm statuses set","method":"PUT","path":"/crm/objects/{objectName}/statuses","permission":"crm:write","pathParams":["objectName"]},
@@ -388,7 +388,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
388
388
  {"id":"email.sequence.enrollment.stop","group":"email","summary":"Stop one enrollment (no further steps send).","cli":"dench email sequence enrollment stop","method":"POST","path":"/email/sequences/enrollments/{enrollmentId}/stop","permission":"email:write","pathParams":["enrollmentId"]},
389
389
  {"id":"campaigns.accounts.linkedin","group":"campaigns","summary":"Discover LinkedIn sender ids, labels, availability and remaining capacity visible to the acting workspace member. Never returns provider credentials.","cli":"dench campaign accounts","method":"GET","path":"/campaigns/accounts/linkedin","permission":"campaigns:read","pathParams":[]},
390
390
  {"id":"campaigns.accounts.email","group":"campaigns","summary":"Discover the mailboxes the acting workspace member can send campaign email from: ids for emailMailboxAccountIds, status, warm-up state and when it is done, the daily cold-email cap and what is left today. Never returns provider credentials.","cli":"dench campaign accounts","method":"GET","path":"/campaigns/accounts/email","permission":"campaigns:read","pathParams":[]},
391
- {"id":"campaigns.list","group":"campaigns","summary":"List outreach campaigns (archived ones on request).","cli":"dench campaign list","method":"GET","path":"/campaigns","permission":"campaigns:read","pathParams":[]},
391
+ {"id":"campaigns.list","group":"campaigns","summary":"List outreach campaigns (archived ones on request).","cli":"dench campaign list [--with-references]","method":"GET","path":"/campaigns","permission":"campaigns:read","pathParams":[]},
392
392
  {"id":"campaigns.create","group":"campaigns","summary":"Create a draft outreach campaign with email and LinkedIn sender pools and a business-hours send window in the given timezone.","cli":"dench campaign create","method":"POST","path":"/campaigns","permission":"campaigns:write","pathParams":[]},
393
393
  {"id":"campaigns.duplicate","group":"campaigns","summary":"Copy a campaign into a new draft named \"<name> copy\": the same steps, schedule, sender pools and personalized lines, its lead source paused, and no people. To reach more than 300 people, duplicate and add the next page of the search to the copy.","cli":"dench campaign duplicate","method":"POST","path":"/campaigns/{sequenceId}/duplicate","permission":"campaigns:write","pathParams":["sequenceId"]},
394
394
  {"id":"campaigns.get","group":"campaigns","summary":"Read a campaign: settings, steps, people, their current step and hold, last touches, linked leads.","cli":"dench campaign get","method":"GET","path":"/campaigns/{sequenceId}","permission":"campaigns:read","pathParams":["sequenceId"]},
@@ -36,6 +36,7 @@ import {
36
36
  SEARCH_TEXT_FLAGS,
37
37
  usesSearchFlags,
38
38
  } from "./campaign-filters";
39
+ import { campaignReferenceResult } from "./campaign-reference";
39
40
  import {
40
41
  assertNoUnknownFlags,
41
42
  getFlag,
@@ -59,6 +60,7 @@ type Context = {
59
60
  jsonOutput: boolean;
60
61
  sessionToken?: string;
61
62
  host?: string;
63
+ workspaceSlug?: string;
62
64
  resolveCampaign: (reference: string) => Promise<CampaignRef>;
63
65
  };
64
66
 
@@ -1349,6 +1351,7 @@ async function addRecipients(
1349
1351
  }
1350
1352
 
1351
1353
  type CampaignSettings = {
1354
+ references?: unknown[];
1352
1355
  _id: string;
1353
1356
  name: string;
1354
1357
  status: string;
@@ -1373,7 +1376,10 @@ async function readCampaign(
1373
1376
  throw new Error(
1374
1377
  `Campaign ${sequenceId} could not be read back. Run dench campaign list to find it.`,
1375
1378
  );
1376
- return detail.sequence;
1379
+ return {
1380
+ ...detail.sequence,
1381
+ references: (detail as { references?: unknown[] }).references,
1382
+ };
1377
1383
  }
1378
1384
 
1379
1385
  function plural(count: number, one: string, many: string) {
@@ -1442,20 +1448,26 @@ async function draft(ctx: Context) {
1442
1448
  .join("; ");
1443
1449
  return print(
1444
1450
  ctx,
1445
- {
1451
+ campaignReferenceResult(
1452
+ ctx,
1453
+ {
1454
+ references: campaign.references,
1455
+ sequenceId,
1456
+ name: campaign.name,
1457
+ status: campaign.status,
1458
+ clientRequestId,
1459
+ timezone: campaign.timezone ?? timezone,
1460
+ sendSchedules,
1461
+ dailySendCap: campaign.dailySendCap ?? null,
1462
+ stopOnReply: campaign.stopOnReply !== false,
1463
+ ...(campaign.startsAt ? { startsAt: campaign.startsAt } : {}),
1464
+ ...(campaign.endsAt ? { endsAt: campaign.endsAt } : {}),
1465
+ emailMailboxAccountIds,
1466
+ linkedinAccountIds,
1467
+ },
1446
1468
  sequenceId,
1447
- name: campaign.name,
1448
- status: campaign.status,
1449
- clientRequestId,
1450
- timezone: campaign.timezone ?? timezone,
1451
- sendSchedules,
1452
- dailySendCap: campaign.dailySendCap ?? null,
1453
- stopOnReply: campaign.stopOnReply !== false,
1454
- ...(campaign.startsAt ? { startsAt: campaign.startsAt } : {}),
1455
- ...(campaign.endsAt ? { endsAt: campaign.endsAt } : {}),
1456
- emailMailboxAccountIds,
1457
- linkedinAccountIds,
1458
- },
1469
+ campaign.name,
1470
+ ),
1459
1471
  `Draft "${campaign.name}" [${sequenceId}] · ${windows || "no send window"} · ${plural(emailMailboxAccountIds.length, "mailbox", "mailboxes")}, ${plural(linkedinAccountIds.length, "LinkedIn account", "LinkedIn accounts")} · request id ${clientRequestId}`,
1460
1472
  );
1461
1473
  }
@@ -0,0 +1,39 @@
1
+ import {
2
+ boundWorkspaceReferences,
3
+ parseWorkspaceReference,
4
+ type WorkspaceReference,
5
+ withWorkspaceReferenceUrls,
6
+ workspaceReferenceHref,
7
+ } from "./workspace-references";
8
+
9
+ export function campaignReferenceResult(
10
+ context: { host?: string; workspaceSlug?: string },
11
+ result: Record<string, unknown>,
12
+ sequenceId: string,
13
+ label: string,
14
+ ) {
15
+ const inherited = Array.isArray(result.references)
16
+ ? result.references
17
+ .map(parseWorkspaceReference)
18
+ .find(
19
+ (ref) => ref?.kind === "campaign" && ref.sequenceId === sequenceId,
20
+ )
21
+ : undefined;
22
+ const identity = { kind: "campaign" as const, sequenceId };
23
+ const reference: WorkspaceReference = withWorkspaceReferenceUrls(
24
+ {
25
+ ...identity,
26
+ label,
27
+ href: workspaceReferenceHref(identity),
28
+ ...(inherited?.imageUrl ? { imageUrl: inherited.imageUrl } : {}),
29
+ ...(inherited?.objectIcon ? { objectIcon: inherited.objectIcon } : {}),
30
+ },
31
+ context,
32
+ );
33
+ return {
34
+ ...result,
35
+ ...boundWorkspaceReferences([reference]),
36
+ href: reference.href,
37
+ ...(reference.url ? { url: reference.url } : {}),
38
+ };
39
+ }
@@ -7,6 +7,8 @@ export type ConvexOperation = {
7
7
  kind: "convex";
8
8
  functionType: "query" | "mutation" | "action";
9
9
  functionName: string;
10
+ /** Explicit envelope variant; the legacy function keeps its array contract. */
11
+ withReferencesFunctionName?: string;
10
12
  auth: ApiAuthMode;
11
13
  tokenArg?: "sessionToken" | "apiKey";
12
14
  includeRunId?: boolean;
@@ -1293,7 +1295,7 @@ const rawApiOperations = [
1293
1295
  id: "crm.objects.list",
1294
1296
  group: "crm",
1295
1297
  summary: "List CRM objects.",
1296
- cli: "dench crm objects list",
1298
+ cli: "dench crm objects list [--with-references]",
1297
1299
  method: "GET",
1298
1300
  path: "/crm/objects",
1299
1301
  requestSchema: noBody,
@@ -1715,10 +1717,12 @@ const rawApiOperations = [
1715
1717
  id: "crm.entries.list",
1716
1718
  group: "crm",
1717
1719
  summary: "List CRM entries.",
1718
- cli: "dench crm entries list",
1720
+ cli: "dench crm entries list [--with-references]",
1719
1721
  method: "GET",
1720
1722
  path: "/crm/objects/{objectName}/entries",
1721
- requestSchema: noBody,
1723
+ requestSchema: z
1724
+ .object({ withReferences: z.boolean().optional() })
1725
+ .passthrough(),
1722
1726
  responseSchema: anyResponse,
1723
1727
  backend: convex("query", "functions/crm/entries:list"),
1724
1728
  }),
@@ -2604,7 +2608,7 @@ const rawApiOperations = [
2604
2608
  id: "crm.query",
2605
2609
  group: "crm",
2606
2610
  summary: "Query CRM entries.",
2607
- cli: "dench crm query",
2611
+ cli: "dench crm query [--with-references]",
2608
2612
  method: "POST",
2609
2613
  path: "/crm/query",
2610
2614
  requestSchema: anyObject,
@@ -2649,7 +2653,7 @@ const rawApiOperations = [
2649
2653
  id: "crm.search",
2650
2654
  group: "crm",
2651
2655
  summary: "Search CRM entries.",
2652
- cli: "dench crm search",
2656
+ cli: "dench crm search [--with-references]",
2653
2657
  method: "GET",
2654
2658
  path: "/crm/search",
2655
2659
  requestSchema: anyObject,
@@ -4275,7 +4279,7 @@ const rawApiOperations = [
4275
4279
  id: "campaigns.list",
4276
4280
  group: "campaigns",
4277
4281
  summary: "List outreach campaigns (archived ones on request).",
4278
- cli: "dench campaign list",
4282
+ cli: "dench campaign list [--with-references]",
4279
4283
  method: "GET",
4280
4284
  path: "/campaigns",
4281
4285
  requestSchema: anyObject,