@dench.com/cli 3.6.6-staging.386 → 3.7.0-staging.387

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.
@@ -57,6 +57,48 @@ const nativeEmailCreate = z.object({
57
57
  requestId: z.string(),
58
58
  ...nativeEmailContent,
59
59
  });
60
+ const inboxHeader = z.object({
61
+ messageHandle: z.string(),
62
+ threadHandle: z.string(),
63
+ mailboxId: z.string(),
64
+ from: nativeEmailRecipient,
65
+ to: z.array(nativeEmailRecipient),
66
+ subject: z.string(),
67
+ at: z.number(),
68
+ dateSource: z.enum(["sentAt", "createdAt"]),
69
+ direction: z.enum(["inbound", "outbound"]),
70
+ status: z.string(),
71
+ hasAttachments: z.boolean(),
72
+ headersTruncated: z.boolean(),
73
+ });
74
+ const inboxCoverage = z.object({
75
+ source: z.literal("synced"),
76
+ scope: z.literal("mailbox_linked_messages"),
77
+ mailboxId: z.string(),
78
+ mailboxEmail: z.string(),
79
+ mailboxStatus: z.string(),
80
+ ingestionMode: z.string(),
81
+ phase: z.string(),
82
+ syncStatus: z.string().nullable(),
83
+ lastPolledAt: z.number().nullable(),
84
+ lastBackfillCompletedAt: z.number().nullable(),
85
+ oldestSeenAt: z.number().nullable(),
86
+ backfillStatus: z.string().nullable(),
87
+ backfillCapped: z.boolean(),
88
+ providerComplete: z.literal(false),
89
+ privacyLimited: z.boolean(),
90
+ });
91
+ const inboxPaging = {
92
+ limit: z.coerce.number().int().min(1).max(50).optional(),
93
+ cursor: z.string().optional(),
94
+ };
95
+ const inboxPage = z.object({
96
+ page: z.array(inboxHeader),
97
+ continueCursor: z.string().nullable(),
98
+ isDone: z.boolean(),
99
+ coverage: inboxCoverage,
100
+ order: z.literal("sentAt_desc_then_undated"),
101
+ });
60
102
  const nativeEmailReceipt = z
61
103
  .object({
62
104
  ok: z.boolean(),
@@ -2118,7 +2160,7 @@ const composioToolRun = z.object({
2118
2160
  tool_slug: z
2119
2161
  .string()
2120
2162
  .optional()
2121
- .describe("Tool slug, e.g. GMAIL_FETCH_EMAILS."),
2163
+ .describe("Tool slug, e.g. SLACK_LIST_CHANNELS."),
2122
2164
  arguments: z
2123
2165
  .record(z.string(), z.unknown())
2124
2166
  .optional()
@@ -3392,8 +3434,34 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3392
3434
  )
3393
3435
  .optional(),
3394
3436
  }),
3437
+ "email.inbox.search": z.object({
3438
+ ...inboxPaging,
3439
+ mailbox: z.string().optional(),
3440
+ from: z.string().min(1).max(500).optional(),
3441
+ to: z.string().min(1).max(500).optional(),
3442
+ subject: z.string().min(1).max(500).optional(),
3443
+ after: z.coerce
3444
+ .number()
3445
+ .optional()
3446
+ .describe(
3447
+ "Inclusive Unix milliseconds; missing sentAt falls back to createdAt.",
3448
+ ),
3449
+ before: z.coerce
3450
+ .number()
3451
+ .optional()
3452
+ .describe("Exclusive Unix milliseconds."),
3453
+ direction: z.enum(["inbound", "outbound"]).optional(),
3454
+ }),
3455
+ "email.inbox.thread": z.object({ ...inboxPaging, threadHandle: z.string() }),
3456
+ "email.inbox.message": z.object({
3457
+ messageHandle: z.string(),
3458
+ format: z.enum(["text", "html"]).optional(),
3459
+ maxBytes: z.coerce.number().int().min(1).max(100000).optional(),
3460
+ }),
3395
3461
  "email.drafts.create": nativeEmailCreate,
3396
- "email.native.send": nativeEmailCreate,
3462
+ "email.native.send": nativeEmailCreate.extend({
3463
+ to: z.array(nativeEmailRecipient).min(1),
3464
+ }),
3397
3465
  "email.drafts.get": z.object({ draftId: z.string() }),
3398
3466
  "email.drafts.senders": z.object({ organizationId: z.string().optional() }),
3399
3467
  "email.drafts.update": z.object({
@@ -3536,6 +3604,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3536
3604
 
3537
3605
  // CRM objects
3538
3606
  "crm.objects.list": noInput,
3607
+ "crm.objects.listCompact": noInput,
3539
3608
  "crm.objects.get": z.object({ name: z.string() }),
3540
3609
  "crm.objects.create": z.object({
3541
3610
  name: z.string().describe("Object name (lowercased, singular)."),
@@ -3631,6 +3700,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
3631
3700
 
3632
3701
  // CRM fields
3633
3702
  "crm.fields.list": z.object({ objectName: z.string() }),
3703
+ "crm.fields.listCompact": z.object({ objectName: z.string() }),
3634
3704
  "crm.fields.create": z.object({
3635
3705
  objectName: z.string(),
3636
3706
  name: z.string(),
@@ -4163,6 +4233,18 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
4163
4233
  "Number field to total per bucket (e.g. Amount grouped by Stage). Exact at any table size.",
4164
4234
  ),
4165
4235
  }),
4236
+ "crm.search.page": z.object({
4237
+ text: z.string().describe("Full-text query over readable entries."),
4238
+ objectName: z.string().optional(),
4239
+ select: z.array(z.string().min(1)).min(1).max(30).optional(),
4240
+ limit: z.coerce.number().int().min(1).max(100).optional(),
4241
+ cursor: z
4242
+ .string()
4243
+ .optional()
4244
+ .describe(
4245
+ "Use the preceding continueCursor, with the same query and projection.",
4246
+ ),
4247
+ }),
4166
4248
  "crm.search": z.object({
4167
4249
  text: z.string().describe("Full-text query."),
4168
4250
  objectName: z.string().optional().describe("Omit to search org-wide."),
@@ -5157,6 +5239,24 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5157
5239
 
5158
5240
  // Workspace orientation
5159
5241
  "workspace.context.sections": z.object({}).passthrough(),
5242
+ "email.inbox.search": inboxPage,
5243
+ "email.inbox.thread": inboxPage,
5244
+ "email.inbox.message": z.object({
5245
+ header: inboxHeader,
5246
+ coverage: inboxCoverage,
5247
+ body: z.object({
5248
+ format: z.enum(["text", "html"]),
5249
+ content: z.string(),
5250
+ truncated: z.boolean(),
5251
+ totalBytes: z.number(),
5252
+ }),
5253
+ recipients: z.object({
5254
+ to: z.array(nativeEmailRecipient),
5255
+ cc: z.array(nativeEmailRecipient),
5256
+ bcc: z.array(nativeEmailRecipient),
5257
+ truncated: z.boolean(),
5258
+ }),
5259
+ }),
5160
5260
  "email.drafts.create": nativeEmailReceipt,
5161
5261
  "email.drafts.get": nativeEmailReceipt,
5162
5262
  "email.drafts.update": nativeEmailReceipt,
@@ -5283,6 +5383,18 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5283
5383
 
5284
5384
  // CRM objects
5285
5385
  "crm.objects.list": z.array(crmObjectDoc),
5386
+ "crm.objects.listCompact": z.array(
5387
+ z.object({
5388
+ _id: z.string(),
5389
+ name: z.string(),
5390
+ displayName: z.string(),
5391
+ description: z.string().optional(),
5392
+ entryCount: z.number().nullable(),
5393
+ countScope: z.enum(["workspace", "private_unknown"]),
5394
+ visibility: z.enum(["workspace", "owner_only", "owner_plus_members"]),
5395
+ archived: z.boolean(),
5396
+ }),
5397
+ ),
5286
5398
  "crm.objects.get": crmObjectDoc.nullable(),
5287
5399
  "crm.objects.create": z.object({ id: z.string() }),
5288
5400
  "crm.objects.update": okResponse,
@@ -5337,6 +5449,20 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5337
5449
 
5338
5450
  // CRM fields
5339
5451
  "crm.fields.list": z.array(crmFieldDoc),
5452
+ "crm.fields.listCompact": z.array(
5453
+ z.object({
5454
+ _id: z.string(),
5455
+ name: z.string(),
5456
+ type: z.string(),
5457
+ required: z.boolean(),
5458
+ readOnly: z.boolean(),
5459
+ defaultValue: z.string().optional(),
5460
+ enumValues: z.array(z.string()).optional(),
5461
+ enumMultiple: z.boolean(),
5462
+ relatedObjectId: z.string().optional(),
5463
+ relationshipType: z.string().optional(),
5464
+ }),
5465
+ ),
5340
5466
  "crm.fields.create": z.object({ id: z.string() }),
5341
5467
  "crm.fields.update": okResponse,
5342
5468
  "crm.fields.delete": okResponse,
@@ -5493,6 +5619,16 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
5493
5619
  sumNumber: z.number().optional(),
5494
5620
  }),
5495
5621
  ),
5622
+ "crm.search.page": z.object({
5623
+ page: z.array(
5624
+ z.object({ _id: z.string(), objectId: z.string(), data: fieldsMap }),
5625
+ ),
5626
+ isDone: z.boolean(),
5627
+ continueCursor: z.string().nullable(),
5628
+ returnedCount: z.number(),
5629
+ selectedFields: z.array(z.string()).nullable(),
5630
+ completeness: z.literal("page"),
5631
+ }),
5496
5632
  "crm.search": z.array(
5497
5633
  z
5498
5634
  .object({
@@ -6217,6 +6353,12 @@ export const convexExtras: Record<string, ConvexExtras> = {
6217
6353
  // ---------------------------------------------------------------------------
6218
6354
 
6219
6355
  export const requestExamples: Record<string, unknown> = {
6356
+ "email.native.send": {
6357
+ requestId: "send-intro-001",
6358
+ to: [{ email: "ada@example.com", name: "Ada" }],
6359
+ subject: "Introduction",
6360
+ textBody: "Hello Ada",
6361
+ },
6220
6362
  "campaigns.accounts.linkedin": {},
6221
6363
  "campaigns.create": {
6222
6364
  name: "Founder outreach",
@@ -6470,11 +6612,11 @@ export const requestExamples: Record<string, unknown> = {
6470
6612
  input_images_base64: ["iVBORw0KGgoAAAANSUhEUg..."],
6471
6613
  },
6472
6614
  "tool.search": {
6473
- queries: [{ use_case: "fetch the 10 most recent emails" }],
6615
+ queries: [{ use_case: "list Slack channels" }],
6474
6616
  },
6475
6617
  "tool.run": {
6476
- tool_slug: "GMAIL_FETCH_EMAILS",
6477
- arguments: { max_results: 10 },
6618
+ tool_slug: "SLACK_LIST_CHANNELS",
6619
+ arguments: { limit: 10 },
6478
6620
  },
6479
6621
  "crm.people.search": {
6480
6622
  titles: ["Head of Sales"],
@@ -8019,18 +8161,18 @@ export const responseExamples: Record<string, unknown> = {
8019
8161
  error: null,
8020
8162
  results: [
8021
8163
  {
8022
- use_case: "fetch recent emails",
8023
- primary_tool_slugs: ["GMAIL_FETCH_EMAILS"],
8024
- toolkits: ["gmail"],
8164
+ use_case: "list Slack channels",
8165
+ primary_tool_slugs: ["SLACK_LIST_CHANNELS"],
8166
+ toolkits: ["slack"],
8025
8167
  },
8026
8168
  ],
8027
8169
  toolkit_connection_statuses: [
8028
- { toolkit: "gmail", has_active_connection: true },
8170
+ { toolkit: "slack", has_active_connection: true },
8029
8171
  ],
8030
8172
  tool_schemas: {
8031
- GMAIL_FETCH_EMAILS: {
8032
- toolkit: "gmail",
8033
- tool_slug: "GMAIL_FETCH_EMAILS",
8173
+ SLACK_LIST_CHANNELS: {
8174
+ toolkit: "slack",
8175
+ tool_slug: "SLACK_LIST_CHANNELS",
8034
8176
  execution_ref: "eyJzZXNzaW9uIjoi...",
8035
8177
  execution_ref_version: 1,
8036
8178
  },
@@ -8039,11 +8181,11 @@ export const responseExamples: Record<string, unknown> = {
8039
8181
  execution_ref_version: 1,
8040
8182
  },
8041
8183
  "tool.run": {
8042
- data: { messages: [{ id: "msg01", subject: "Quick intro" }] },
8184
+ data: { channels: [{ id: "C_EXAMPLE", name: "general" }] },
8043
8185
  error: null,
8044
8186
  log_id: "log_exec_01",
8045
8187
  execution_mode: "gateway_tool_router",
8046
- tool_slug: "GMAIL_FETCH_EMAILS",
8188
+ tool_slug: "SLACK_LIST_CHANNELS",
8047
8189
  tool_router_session_id: "trs_abc123",
8048
8190
  },
8049
8191
  "tool.disconnect": { id: "ca_github01", deleted: true },
@@ -141,6 +141,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
141
141
  {"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"]},
142
142
  {"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"]},
143
143
  {"id":"crm.objects.list","group":"crm","summary":"List CRM objects.","cli":"dench crm objects list","method":"GET","path":"/crm/objects","permission":"crm:read","pathParams":[]},
144
+ {"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":[]},
144
145
  {"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"]},
145
146
  {"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":[]},
146
147
  {"id":"crm.objects.update","group":"crm","summary":"Update a CRM object.","cli":"dench crm objects update","method":"PATCH","path":"/crm/objects/{name}","permission":"crm:write","pathParams":["name"]},
@@ -165,6 +166,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
165
166
  {"id":"crm.sidebar.groups.move-item","group":"crm","summary":"Move a sidebar row into a group (groupId set) or back to the top level (groupId null). Open to any member.","cli":"dench crm sidebar group add","method":"POST","path":"/crm/sidebar/groups/move","permission":"crm:write","pathParams":[]},
166
167
  {"id":"crm.sidebar.pins.set","group":"crm","summary":"Pin (pinned true) or unpin a Library row — or a whole group — on the home-level Pinned shortcut strip (the \"tab on the left\"). Does not move the row inside Library. Workspace-shared; open to any member.","cli":"dench crm sidebar pin","method":"POST","path":"/crm/sidebar/pins","permission":"crm:write","pathParams":[]},
167
168
  {"id":"crm.fields.list","group":"crm","summary":"List fields for a CRM object, including read-only calculation metadata.","cli":"dench crm fields list","method":"GET","path":"/crm/objects/{objectName}/fields","permission":"crm:read","pathParams":["objectName"]},
169
+ {"id":"crm.fields.listCompact","group":"crm","summary":"List field names, types and effective constraints without indexing or enrichment internals.","cli":"dench crm fields list --compact","method":"GET","path":"/crm/catalog/objects/{objectName}/fields","permission":"crm:read","pathParams":["objectName"]},
168
170
  {"id":"crm.fields.create","group":"crm","summary":"Create a CRM field or read-only campaign activity boolean column.","cli":"dench crm fields create","method":"POST","path":"/crm/objects/{objectName}/fields","permission":"crm:write","pathParams":["objectName"]},
169
171
  {"id":"crm.fields.update","group":"crm","summary":"Update a CRM field or change/explicitly clear an existing campaign calculation.","cli":"dench crm fields update","method":"PATCH","path":"/crm/objects/{objectName}/fields/{fieldName}","permission":"crm:write","pathParams":["objectName","fieldName"]},
170
172
  {"id":"crm.fields.delete","group":"crm","summary":"Delete a CRM field.","cli":"dench crm fields delete","method":"DELETE","path":"/crm/objects/{objectName}/fields/{fieldName}","permission":"crm:write","pathParams":["objectName","fieldName"]},
@@ -237,6 +239,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
237
239
  {"id":"crm.sql","group":"crm","summary":"Run CRM SQL-compatible query.","cli":"dench crm sql","method":"POST","path":"/crm/sql","permission":"crm:read","pathParams":[]},
238
240
  {"id":"crm.aggregate","group":"crm","summary":"Aggregate CRM entries.","cli":"dench crm aggregate","method":"POST","path":"/crm/aggregate","permission":"crm:read","pathParams":[]},
239
241
  {"id":"crm.search","group":"crm","summary":"Search CRM entries.","cli":"dench crm search","method":"GET","path":"/crm/search","permission":"crm:read","pathParams":[]},
242
+ {"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":[]},
240
243
  {"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"]},
241
244
  {"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"]},
242
245
  {"id":"crm.batch","group":"crm","summary":"Apply a CRM batch.","cli":"dench crm batch","method":"POST","path":"/crm/batch","permission":"crm:write","pathParams":[]},
@@ -315,6 +318,9 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
315
318
  {"id":"email.identity.disable","group":"email","summary":"Soft-disable a sender (or every sender on a domain) for the workspace; sends are blocked until restored.","cli":"dench email identity disable","method":"POST","path":"/email/sending-identities/{identity}/disable","permission":"email:write","pathParams":["identity"]},
316
319
  {"id":"email.identity.restore","group":"email","summary":"Restore a previously disabled sender for the workspace.","cli":"dench email identity restore","method":"POST","path":"/email/sending-identities/{identity}/restore","permission":"email:write","pathParams":["identity"]},
317
320
  {"id":"email.identity.delete","group":"email","summary":"Permanently delete a sender (or every sender on a domain) from the workspace; re-adding it requires verification from scratch.","cli":"dench email identity delete","method":"DELETE","path":"/email/sending-identities/{identity}","permission":"email:write","pathParams":["identity"]},
321
+ {"id":"email.inbox.search","group":"email","summary":"Search authorized synced mailbox headers with bounded cursor pagination. isDone covers stored records, not provider completeness.","cli":"dench email inbox search","method":"GET","path":"/email/inbox/search","permission":"email:read","pathParams":[]},
322
+ {"id":"email.inbox.thread","group":"email","summary":"Read a bounded page of synced headers using an exact thread handle from inbox search; bodies remain on demand.","cli":"dench email inbox thread","method":"GET","path":"/email/inbox/threads/{threadHandle}","permission":"email:read","pathParams":["threadHandle"]},
323
+ {"id":"email.inbox.message","group":"email","summary":"Read a bounded stored body using an exact message handle from inbox search; rechecks mailbox and record privacy.","cli":"dench email inbox message","method":"GET","path":"/email/inbox/messages/{messageHandle}","permission":"email:read","pathParams":["messageHandle"]},
318
324
  {"id":"email.drafts.create","group":"email","summary":"Create an editable Dench email draft and synchronize its selected native mailbox asynchronously. Use a stable requestId for retries.","cli":"dench email draft create","method":"POST","path":"/email/drafts","permission":"email:write","pathParams":[]},
319
325
  {"id":"email.drafts.get","group":"email","summary":"Read an email draft, eligible senders and provider sync status.","cli":"dench email draft show","method":"GET","path":"/email/drafts/{draftId}","permission":"email:read","pathParams":["draftId"]},
320
326
  {"id":"email.drafts.senders","group":"email","summary":"List native mailboxes this caller may use.","cli":"dench email draft senders","method":"GET","path":"/email/drafts/senders","permission":"email:read","pathParams":[]},
@@ -322,8 +328,8 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
322
328
  {"id":"email.drafts.send","group":"email","summary":"Send the current Dench draft revision through its native mailbox. Repeated requests share a durable receipt.","cli":"dench email draft send","method":"POST","path":"/email/drafts/{draftId}/send","permission":"email:write","pathParams":["draftId"]},
323
329
  {"id":"email.drafts.upload","group":"email","summary":"Upload attachment bytes authorized against this draft before adding its storage reference.","cli":"dench email draft attach","method":"POST","path":"/email/drafts/{draftId}/attachments","permission":"email:write","pathParams":["draftId"]},
324
330
  {"id":"email.drafts.signature","group":"email","summary":"Load the selected mailbox signature for explicit visible insertion.","cli":"dench email draft signature","method":"GET","path":"/email/drafts/{draftId}/signature","permission":"email:read","pathParams":["draftId"]},
325
- {"id":"email.native.send","group":"email","summary":"Send through a native Gmail or Outlook mailbox with a durable request receipt. Only use for an explicit send request.","cli":"dench email send --mailbox","method":"POST","path":"/email/native/send","permission":"email:write","pathParams":[]},
326
- {"id":"email.send","group":"email","summary":"Send one raw email (no campaign): to/cc/bcc, custom from-name and reply-to, optional reply threading via replyToMessageId.","cli":"dench email send","method":"POST","path":"/email/send","permission":"email:write","pathParams":[]},
331
+ {"id":"email.native.send","group":"email","summary":"Send through a native Gmail or Outlook mailbox with a durable request receipt. Only use for an explicit send request.","cli":"dench email send [--mailbox <id/email>]","method":"POST","path":"/email/native/send","permission":"email:write","pathParams":[]},
332
+ {"id":"email.send","group":"email","summary":"Send one raw email (no campaign): to/cc/bcc, custom from-name and reply-to, optional reply threading via replyToMessageId.","cli":"dench email send --from <verified-email>","method":"POST","path":"/email/send","permission":"email:write","pathParams":[]},
327
333
  {"id":"email.message.get","group":"email","summary":"Read a raw email message's status and open/click counters.","cli":"dench email message status","method":"GET","path":"/email/messages/{messageId}","permission":"email:read","pathParams":["messageId"]},
328
334
  {"id":"email.message.list","group":"email","summary":"List recent raw email messages for the workspace.","cli":"dench email message list","method":"GET","path":"/email/messages","permission":"email:read","pathParams":[]},
329
335
  {"id":"email.template.create","group":"email","summary":"Create a managed email campaign template.","cli":"dench email template create","method":"POST","path":"/email/templates","permission":"email:write","pathParams":[]},
@@ -459,7 +465,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
459
465
  {"id":"agentConfig.user.show","group":"agent-config","summary":"Read USER.md.","cli":"dench user show","method":"GET","path":"/agent-config/user","permission":"agent-config:read","pathParams":[]},
460
466
  {"id":"agentConfig.user.edit","group":"agent-config","summary":"Update USER.md.","cli":"dench user edit","method":"PUT","path":"/agent-config/user","permission":"agent-config:write","pathParams":[]},
461
467
  {"id":"agentConfig.tools.show","group":"agent-config","summary":"Read tool notes.","cli":"dench tools show","method":"GET","path":"/agent-config/tools","permission":"agent-config:read","pathParams":[]},
462
- {"id":"agentConfig.tools.edit","group":"agent-config","summary":"Update tool notes.","cli":"dench tools notes-edit","method":"PUT","path":"/agent-config/tools","permission":"agent-config:write","pathParams":[]},
468
+ {"id":"agentConfig.tools.edit","group":"agent-config","summary":"Update tool notes.","cli":"dench tools edit","method":"PUT","path":"/agent-config/tools","permission":"agent-config:write","pathParams":[]},
463
469
  {"id":"agentConfig.mem.show","group":"agent-config","summary":"Read aggregate memory.","cli":"dench mem show","method":"GET","path":"/agent-config/mem","permission":"agent-config:read","pathParams":[]},
464
470
  {"id":"agentConfig.mem.edit","group":"agent-config","summary":"Update aggregate memory.","cli":"dench mem set","method":"PUT","path":"/agent-config/mem","permission":"agent-config:write","pathParams":[]},
465
471
  {"id":"agentConfig.bootstrap.show","group":"agent-config","summary":"Read bootstrap state.","cli":"dench bootstrap status","method":"GET","path":"/agent-config/bootstrap","permission":"agent-config:read","pathParams":[]},
@@ -0,0 +1,17 @@
1
+ import bundle from "./references.generated.json";
2
+
3
+ export const referenceRevision = bundle.revision;
4
+ const files: Record<string, { sha256: string; content: string }> = bundle.files;
5
+ export function getBundledReference(path: string): string | undefined {
6
+ return Object.hasOwn(files, path) ? files[path].content : undefined;
7
+ }
8
+ export function bundledReferenceIndex() {
9
+ return {
10
+ revision: referenceRevision,
11
+ references: Object.entries(files).map(([path, file]) => ({
12
+ path,
13
+ sha256: file.sha256,
14
+ bytes: new TextEncoder().encode(file.content).byteLength,
15
+ })),
16
+ };
17
+ }
package/lib/cli-args.ts CHANGED
@@ -27,7 +27,15 @@
27
27
  * the caller adapts.
28
28
  */
29
29
 
30
- export class CliArgError extends Error {}
30
+ export class CliArgError extends Error {
31
+ readonly code = "invalid_arguments";
32
+ }
33
+
34
+ /** Help is a local operation, even after otherwise valid mutation arguments. */
35
+ export function isHelpRequest(args: readonly string[]): boolean {
36
+ if (args.includes("--help") || args.includes("-h")) return true;
37
+ return args[0] === "help" || args[1] === "help";
38
+ }
31
39
 
32
40
  export function shift(args: string[], expected: string): string {
33
41
  const value = args.shift();
@@ -35,10 +43,28 @@ export function shift(args: string[], expected: string): string {
35
43
  return value;
36
44
  }
37
45
 
46
+ /** A standalone short option cannot supply another flag's value. Descending
47
+ * --sort uses -Field (including single-letter fields); negative numbers remain
48
+ * ordinary values. Longer dash-prefixed text remains supported. */
49
+ export function missingFlagValue(
50
+ value: string | undefined,
51
+ name: string,
52
+ ): boolean {
53
+ return (
54
+ value === undefined ||
55
+ value.startsWith("--") ||
56
+ value === "-h" ||
57
+ (name !== "--sort" && /^-[a-zA-Z]$/.test(value))
58
+ );
59
+ }
60
+
38
61
  export function getFlag(args: string[], name: string): string | undefined {
39
62
  const idx = args.indexOf(name);
40
63
  if (idx === -1) return undefined;
41
64
  const value = args[idx + 1];
65
+ if (missingFlagValue(value, name)) {
66
+ throw new CliArgError(`${name} needs a value`);
67
+ }
42
68
  args.splice(idx, 2);
43
69
  return value;
44
70
  }
@@ -141,9 +167,10 @@ export function assertNoUnknownFlags(
141
167
  // some shells to terminate option parsing) is allowed.
142
168
  for (const token of args) {
143
169
  if (token === "--") continue;
144
- if (token.startsWith("--")) {
170
+ if (/^--?[^\d.]/.test(token)) {
171
+ const path = command.replace(/^dench\s+/, "");
145
172
  throw new CliArgError(
146
- `Unknown flag for ${command}: ${token}. Run \`dench ${command.split(" ")[0]} help\` for the full flag list.`,
173
+ `Unknown flag for ${path}: ${token}. Run \`dench ${path} --help\` for usage.`,
147
174
  );
148
175
  }
149
176
  }
@@ -0,0 +1,237 @@
1
+ import { CliArgError, missingFlagValue } from "./cli-args";
2
+
3
+ type PositionalArity = readonly [min: number, max: number | null];
4
+
5
+ type CommandArguments = {
6
+ command: string;
7
+ values: string[];
8
+ switches: string[];
9
+ positionals: PositionalArity;
10
+ flagPositionals: Record<string, PositionalArity>;
11
+ };
12
+ const spec = (
13
+ command: string,
14
+ positionals: PositionalArity,
15
+ values = "",
16
+ switches = "",
17
+ flagPositionals: Record<string, PositionalArity> = {},
18
+ ): CommandArguments => ({
19
+ command,
20
+ positionals,
21
+ flagPositionals,
22
+ values: values.split(" ").filter(Boolean),
23
+ switches: switches.split(" ").filter(Boolean),
24
+ });
25
+
26
+ const browserSpec = (
27
+ verb: string,
28
+ positionals: PositionalArity,
29
+ flagPositionals: Record<string, PositionalArity> = {},
30
+ ): CommandArguments =>
31
+ spec(
32
+ `browser ${verb}`,
33
+ positionals,
34
+ "--thread --selector --text --label --value --key --target-id --targetId --pixels --timeout-ms --timeoutMs --x --y --output --url --script",
35
+ "--full-page",
36
+ flagPositionals,
37
+ );
38
+
39
+ /** CLI spellings for dispatchers that do not reject leftovers themselves.
40
+ * Positional ranges follow the actual handlers; null deliberately allows free text
41
+ * or multiple IDs. Flag alternatives cover positional inputs replaced by flags.
42
+ * Shared by pre-dispatch validation and help; API property names are not flags. */
43
+ export const COMMAND_ARGUMENTS = [
44
+ spec(
45
+ "signin",
46
+ [0, 0],
47
+ "--kind --name --email --org-name --org --timeout-ms --otp-code",
48
+ "--new-workspace --no-open",
49
+ ),
50
+ spec("logout", [0, 1], "", "--all"),
51
+ spec("use", [1, 1]),
52
+ spec("backend set", [1, 1]),
53
+ spec("backend clear", [0, 0]),
54
+ ...["install", "uninstall", "refresh"].map((verb) =>
55
+ spec(
56
+ `ads ${verb}`,
57
+ [0, 0],
58
+ "--target",
59
+ "--no-open --apply --color --no-color",
60
+ ),
61
+ ),
62
+ spec(
63
+ "ads payout setup",
64
+ [0, 0],
65
+ "--target",
66
+ "--no-open --apply --color --no-color",
67
+ ),
68
+ spec(
69
+ "ads payout onboard",
70
+ [0, 0],
71
+ "--target",
72
+ "--no-open --apply --color --no-color",
73
+ ),
74
+ spec(
75
+ "ads withdraw",
76
+ [0, 0],
77
+ "--amount --idempotency-key --target",
78
+ "--no-open --apply --color --no-color",
79
+ ),
80
+ spec(
81
+ "chat new",
82
+ [1, null],
83
+ "--model --file-context --visibility --title",
84
+ "--yolo --follow",
85
+ ),
86
+ spec("chat send", [2, null], "--model", "--yolo --follow"),
87
+ spec("chat rename", [2, null], "", "--from-stdin", {
88
+ "--from-stdin": [0, 0],
89
+ }),
90
+ spec("chat delete", [1, null], "--ids", "--yes -y --include-automated", {
91
+ "--ids": [0, null],
92
+ }),
93
+ spec("apps create", [1, 1], "--name --icon --description", "", {
94
+ "--name": [0, 1],
95
+ }),
96
+ spec(
97
+ "apps publish",
98
+ [1, 1],
99
+ "--slug --name --description --icon --visibility --version",
100
+ ),
101
+ ...["install", "uninstall", "enable", "disable", "pin", "unpin"].map((verb) =>
102
+ spec(`apps ${verb}`, [1, 1]),
103
+ ),
104
+ spec("apps webhooks register", [2, 2], "", "--rotate"),
105
+ spec("apps webhooks delete", [2, 2]),
106
+ spec("apps validate", [1, 1]),
107
+ spec(
108
+ "members me",
109
+ [0, 0],
110
+ "--name --tagline --timezone --image-url --avatar",
111
+ "--clear-avatar --auto-timezone",
112
+ ),
113
+ spec(
114
+ "cron create",
115
+ [1, 1],
116
+ "--prompt --description --overlap --target --type --at --cron --tz --every --every-ms --connection --event --toolkit --trigger-config --object --where --where-logic",
117
+ "--disabled --delete-after-run --no-secret --signed",
118
+ ),
119
+ spec(
120
+ "cron update",
121
+ [1, 1],
122
+ "--name --prompt --description --overlap --enabled --at --cron --tz --every --every-ms",
123
+ ),
124
+ spec("cron run", [1, 1], "", "--wait"),
125
+ spec("cron secret", [1, 1], "", "--off"),
126
+ ...["delete", "enable", "disable"].map((verb) =>
127
+ spec(`cron ${verb}`, [1, 1]),
128
+ ),
129
+ spec("files rm", [1, 1], "", "--recursive -r"),
130
+ spec("files mv", [2, 2]),
131
+ spec("files write", [1, 1]),
132
+ spec("stage", [1, 2], "", "--clean"),
133
+ ...["identity", "organisation", "user", "tools", "mem"].flatMap((group) =>
134
+ ["edit", "set"].map((verb) =>
135
+ spec(`${group} ${verb}`, [0, 0], "--content", "--stdin"),
136
+ ),
137
+ ),
138
+ spec("mem append", [1, null]),
139
+ spec("model default", [0, 1]),
140
+ spec("model thread", [2, 2]),
141
+ spec("bootstrap complete", [0, 0]),
142
+ spec("bootstrap reopen", [0, 0]),
143
+ spec("bootstrap template", [0, 0], "--content", "--stdin"),
144
+ spec(
145
+ "memory save",
146
+ [2, null],
147
+ "--kind --tags --sensitivity --scope --visibility",
148
+ ),
149
+ spec("project create", [1, 1], "--repo --root"),
150
+ spec("approval request", [1, 1], "--reason --risk --agent"),
151
+ spec("approval approve", [1, 1], "--evidence"),
152
+ spec("approval reject", [1, 1], "--evidence"),
153
+ spec("billing topup", [1, 1], "--amount", "--no-open", {
154
+ "--amount": [0, 0],
155
+ }),
156
+ spec("upgrade", [0, 0], "--tier --cycle --billing-cycle", "--no-open"),
157
+ spec("tool run", [1, 1], "--args --account --approval"),
158
+ spec("tool connect", [1, 1], "--callback-url"),
159
+ spec("tool disconnect", [1, 1]),
160
+ spec(
161
+ "browser session create",
162
+ [0, 0],
163
+ "--context-id --contextId --width --height",
164
+ ),
165
+ spec("browser session stop", [1, 1]),
166
+ // Value flags replace the joined positional input. The clickAt handler
167
+ // instead lets its first/second positionals override --x/--y individually.
168
+ browserSpec("open", [0, null], { "--url": [0, 0] }),
169
+ browserSpec("state", [0, 0]),
170
+ browserSpec("inspect", [0, 0]),
171
+ browserSpec("navigate", [0, null], { "--url": [0, 0] }),
172
+ browserSpec("click", [0, 0]),
173
+ browserSpec("clickAt", [0, 2]),
174
+ browserSpec("click-at", [0, 2]),
175
+ browserSpec("fill", [0, 0]),
176
+ browserSpec("type", [0, null], { "--value": [0, 0] }),
177
+ browserSpec("key", [0, null], { "--key": [0, 0] }),
178
+ browserSpec("scroll", [0, 1]),
179
+ browserSpec("wait", [0, 0]),
180
+ browserSpec("evaluate", [0, null], { "--script": [0, 0] }),
181
+ browserSpec("tabs", [0, 0]),
182
+ browserSpec("newTab", [0, null], { "--url": [0, 0] }),
183
+ browserSpec("new-tab", [0, null], { "--url": [0, 0] }),
184
+ ...["activateTab", "activate-tab", "closeTab", "close-tab"].map((verb) =>
185
+ browserSpec(verb, [0, 1], { "--target-id": [0, 0], "--targetId": [0, 0] }),
186
+ ),
187
+ browserSpec("screenshot", [0, 0]),
188
+ browserSpec("stop", [0, 0]),
189
+ ];
190
+
191
+ export function assertDeclaredCommandFlags(argv: readonly string[]): void {
192
+ const args = argv.filter((arg) => arg !== "--json");
193
+ if (["routine", "routines"].includes(args[0])) args[0] = "cron";
194
+ if (["ad", "adline"].includes(args[0])) args[0] = "ads";
195
+ if (args[0] === "agent" && ["new", "send"].includes(args[1]))
196
+ args[0] = "chat";
197
+ if (args[0] === "crm" && args[1] === "members") args.shift();
198
+ const contract = COMMAND_ARGUMENTS.find(({ command }) =>
199
+ command.split(" ").every((part, index) => args[index] === part),
200
+ );
201
+ if (!contract) return; // Domain parsers keep their own consumed-argument validation.
202
+ const positionals: string[] = [];
203
+ const flags = new Set<string>();
204
+ for (let i = contract.command.split(" ").length; i < args.length; i++) {
205
+ const arg = args[i];
206
+ if (!/^--?[^\d.]/.test(arg)) {
207
+ positionals.push(arg);
208
+ continue;
209
+ }
210
+ flags.add(arg);
211
+ if (contract.switches.includes(arg)) continue;
212
+ if (contract.values.includes(arg)) {
213
+ const value = args[++i];
214
+ if (missingFlagValue(value, arg))
215
+ throw new CliArgError(`Missing value for ${arg}`);
216
+ continue;
217
+ }
218
+ throw new CliArgError(
219
+ `Unknown flag for ${contract.command}: ${arg}. Run \`dench ${contract.command} --help\` for usage.`,
220
+ );
221
+ }
222
+ const [min, max] =
223
+ Object.entries(contract.flagPositionals).find(([flag]) =>
224
+ flags.has(flag),
225
+ )?.[1] ?? contract.positionals;
226
+ if (positionals.length < min || (max !== null && positionals.length > max)) {
227
+ const expected =
228
+ max === null
229
+ ? `at least ${min}`
230
+ : min === max
231
+ ? String(min)
232
+ : `${min}–${max}`;
233
+ throw new CliArgError(
234
+ `${positionals.length < min ? "Missing" : "Unexpected"} positional argument for ${contract.command}: expected ${expected}, received ${positionals.length}. Run \`dench ${contract.command} --help\` for usage.`,
235
+ );
236
+ }
237
+ }