@dench.com/cli 2.8.7-staging.189 → 2.8.8-staging.192
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/crm-comments.ts +303 -0
- package/crm.ts +46 -7
- package/dench.ts +44 -0
- package/lib/api-schemas.ts +325 -1
- package/lib/command-registry.ts +303 -0
- package/lib/enrichment-gateway.ts +21 -1
- package/notifications.ts +262 -0
- package/package.json +3 -1
package/lib/api-schemas.ts
CHANGED
|
@@ -545,6 +545,73 @@ const crmEntryShape = z
|
|
|
545
545
|
|
|
546
546
|
const entryWriteResponse = z.object({ entryId: z.string() });
|
|
547
547
|
|
|
548
|
+
const commentActorShape = z
|
|
549
|
+
.object({
|
|
550
|
+
kind: z.string(),
|
|
551
|
+
label: z.string(),
|
|
552
|
+
displayName: z.string().optional(),
|
|
553
|
+
imageUrl: z.string().optional(),
|
|
554
|
+
userId: z.string().optional(),
|
|
555
|
+
isDench: z.boolean(),
|
|
556
|
+
})
|
|
557
|
+
.passthrough();
|
|
558
|
+
|
|
559
|
+
const commentShape = z
|
|
560
|
+
.object({
|
|
561
|
+
_id: z.string(),
|
|
562
|
+
entryId: z.string(),
|
|
563
|
+
parentCommentId: z.string().optional(),
|
|
564
|
+
rootCommentId: z.string().optional(),
|
|
565
|
+
depth: z.number(),
|
|
566
|
+
body: z.string().describe("Markdown."),
|
|
567
|
+
actor: commentActorShape,
|
|
568
|
+
mentionedUserIds: z.array(z.string()),
|
|
569
|
+
mentionsDench: z.boolean(),
|
|
570
|
+
replyCount: z.number(),
|
|
571
|
+
reactionSummary: z.array(
|
|
572
|
+
z.object({ emoji: z.string(), count: z.number() }),
|
|
573
|
+
),
|
|
574
|
+
viewerReactions: z.array(z.string()),
|
|
575
|
+
editedAt: z.number().optional(),
|
|
576
|
+
deletedAt: z.number().optional(),
|
|
577
|
+
denchStatus: z.enum(["pending", "failed", "done"]).optional(),
|
|
578
|
+
canEdit: z.boolean(),
|
|
579
|
+
createdAt: z.number(),
|
|
580
|
+
})
|
|
581
|
+
.passthrough()
|
|
582
|
+
.describe("A comment on a CRM entry.");
|
|
583
|
+
|
|
584
|
+
const commentPage = z.object({
|
|
585
|
+
page: z.array(commentShape),
|
|
586
|
+
isDone: z.boolean(),
|
|
587
|
+
continueCursor: z.string(),
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const notificationShape = z
|
|
591
|
+
.object({
|
|
592
|
+
_id: z.string(),
|
|
593
|
+
kind: z.string(),
|
|
594
|
+
title: z.string(),
|
|
595
|
+
body: z.string().optional(),
|
|
596
|
+
link: z.string().optional(),
|
|
597
|
+
readAt: z.number().optional(),
|
|
598
|
+
archivedAt: z.number().optional(),
|
|
599
|
+
createdAt: z.number(),
|
|
600
|
+
})
|
|
601
|
+
.passthrough();
|
|
602
|
+
|
|
603
|
+
const notificationPreferencesShape = z.object({
|
|
604
|
+
inAppEnabled: z.boolean(),
|
|
605
|
+
emailEnabled: z.boolean(),
|
|
606
|
+
perKind: z.record(
|
|
607
|
+
z.string(),
|
|
608
|
+
z.object({ inApp: z.boolean().optional(), email: z.boolean().optional() }),
|
|
609
|
+
),
|
|
610
|
+
timezone: z.string().nullable(),
|
|
611
|
+
digestWindowMinutes: z.number(),
|
|
612
|
+
maxEmailsPerDay: z.number(),
|
|
613
|
+
});
|
|
614
|
+
|
|
548
615
|
const crmBatchResults = z.object({
|
|
549
616
|
results: z.array(
|
|
550
617
|
z
|
|
@@ -1593,7 +1660,9 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
1593
1660
|
icon: z
|
|
1594
1661
|
.string()
|
|
1595
1662
|
.optional()
|
|
1596
|
-
.describe(
|
|
1663
|
+
.describe(
|
|
1664
|
+
"Hugeicons (or legacy Lucide) icon name, e.g. user-multiple, target-01.",
|
|
1665
|
+
),
|
|
1597
1666
|
}),
|
|
1598
1667
|
"crm.objects.update": z.object({
|
|
1599
1668
|
name: z.string(),
|
|
@@ -1804,6 +1873,92 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
1804
1873
|
entryIds: z.array(z.string()).max(200),
|
|
1805
1874
|
}),
|
|
1806
1875
|
|
|
1876
|
+
// CRM comments
|
|
1877
|
+
"crm.comments.list": z.object({
|
|
1878
|
+
objectName: z.string(),
|
|
1879
|
+
entryId: z.string(),
|
|
1880
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
1881
|
+
cursor: z
|
|
1882
|
+
.string()
|
|
1883
|
+
.optional()
|
|
1884
|
+
.describe("continueCursor from the prior page."),
|
|
1885
|
+
replies: z.coerce
|
|
1886
|
+
.number()
|
|
1887
|
+
.int()
|
|
1888
|
+
.min(0)
|
|
1889
|
+
.max(50)
|
|
1890
|
+
.optional()
|
|
1891
|
+
.describe("Preview replies per root (default 2)."),
|
|
1892
|
+
}),
|
|
1893
|
+
"crm.comments.replies": z.object({
|
|
1894
|
+
commentId: z.string().describe("Root comment id."),
|
|
1895
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
1896
|
+
cursor: z.string().optional(),
|
|
1897
|
+
}),
|
|
1898
|
+
"crm.comments.get": z.object({ commentId: z.string() }),
|
|
1899
|
+
"crm.comments.create": z.object({
|
|
1900
|
+
objectName: z.string(),
|
|
1901
|
+
entryId: z.string(),
|
|
1902
|
+
body: z
|
|
1903
|
+
.string()
|
|
1904
|
+
.min(1)
|
|
1905
|
+
.describe(
|
|
1906
|
+
"Markdown. Mention a member with [@Name](/?member=<userId>); mention Dench with [@Dench](/?member=dench).",
|
|
1907
|
+
),
|
|
1908
|
+
parentCommentId: z
|
|
1909
|
+
.string()
|
|
1910
|
+
.optional()
|
|
1911
|
+
.describe("Reply under this comment (any depth)."),
|
|
1912
|
+
mentionUserIds: z
|
|
1913
|
+
.array(z.string())
|
|
1914
|
+
.optional()
|
|
1915
|
+
.describe("Extra members to notify without writing mention links."),
|
|
1916
|
+
mentionDench: z
|
|
1917
|
+
.boolean()
|
|
1918
|
+
.optional()
|
|
1919
|
+
.describe("Summon Dench to reply in the thread."),
|
|
1920
|
+
}),
|
|
1921
|
+
"crm.comments.update": z.object({
|
|
1922
|
+
commentId: z.string(),
|
|
1923
|
+
body: z.string().min(1),
|
|
1924
|
+
}),
|
|
1925
|
+
"crm.comments.delete": z.object({ commentId: z.string() }),
|
|
1926
|
+
"crm.comments.react": z.object({
|
|
1927
|
+
commentId: z.string(),
|
|
1928
|
+
emoji: z.string().describe("A single emoji, e.g. 👍."),
|
|
1929
|
+
}),
|
|
1930
|
+
"crm.watch.get": z.object({ objectName: z.string(), entryId: z.string() }),
|
|
1931
|
+
"crm.watch.set": z.object({
|
|
1932
|
+
objectName: z.string(),
|
|
1933
|
+
entryId: z.string(),
|
|
1934
|
+
watching: z
|
|
1935
|
+
.boolean()
|
|
1936
|
+
.optional()
|
|
1937
|
+
.describe("Defaults to true; false unfollows."),
|
|
1938
|
+
}),
|
|
1939
|
+
|
|
1940
|
+
// CRM tasks
|
|
1941
|
+
"crm.tasks.mine": z.object({
|
|
1942
|
+
limit: z.coerce.number().int().min(1).max(500).optional(),
|
|
1943
|
+
}),
|
|
1944
|
+
"crm.tasks.forEntry": z.object({
|
|
1945
|
+
objectName: z.string(),
|
|
1946
|
+
entryId: z.string(),
|
|
1947
|
+
}),
|
|
1948
|
+
"crm.tasks.createForEntry": z.object({
|
|
1949
|
+
objectName: z.string(),
|
|
1950
|
+
entryId: z.string(),
|
|
1951
|
+
name: z.string().min(1),
|
|
1952
|
+
assigneeUserId: z.string().optional(),
|
|
1953
|
+
dueDateKey: z
|
|
1954
|
+
.string()
|
|
1955
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
1956
|
+
.optional()
|
|
1957
|
+
.describe("YYYY-MM-DD"),
|
|
1958
|
+
}),
|
|
1959
|
+
"crm.tasks.complete": z.object({ entryId: z.string() }),
|
|
1960
|
+
"crm.tasks.reopen": z.object({ entryId: z.string() }),
|
|
1961
|
+
|
|
1807
1962
|
// CRM cells
|
|
1808
1963
|
"crm.cells.get": z.object({
|
|
1809
1964
|
objectName: z.string(),
|
|
@@ -2265,6 +2420,39 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
2265
2420
|
.string()
|
|
2266
2421
|
.describe("Human name for the account. Empty clears it."),
|
|
2267
2422
|
}),
|
|
2423
|
+
|
|
2424
|
+
// Notifications
|
|
2425
|
+
"notifications.list": z.object({
|
|
2426
|
+
filter: z.enum(["inbox", "unread", "archived", "all"]).optional(),
|
|
2427
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
2428
|
+
}),
|
|
2429
|
+
"notifications.unreadCount": noInput,
|
|
2430
|
+
"notifications.read": z.object({
|
|
2431
|
+
notificationId: z.string(),
|
|
2432
|
+
read: z
|
|
2433
|
+
.boolean()
|
|
2434
|
+
.optional()
|
|
2435
|
+
.describe("Defaults to true; false marks unread."),
|
|
2436
|
+
}),
|
|
2437
|
+
"notifications.readAll": noInput,
|
|
2438
|
+
"notifications.archive": z.object({ notificationId: z.string() }),
|
|
2439
|
+
"notifications.preferences.get": noInput,
|
|
2440
|
+
"notifications.preferences.update": z.object({
|
|
2441
|
+
inAppEnabled: z.boolean().optional(),
|
|
2442
|
+
emailEnabled: z.boolean().optional(),
|
|
2443
|
+
perKind: z
|
|
2444
|
+
.record(
|
|
2445
|
+
z.string(),
|
|
2446
|
+
z.object({
|
|
2447
|
+
inApp: z.boolean().optional(),
|
|
2448
|
+
email: z.boolean().optional(),
|
|
2449
|
+
}),
|
|
2450
|
+
)
|
|
2451
|
+
.optional(),
|
|
2452
|
+
timezone: z.string().optional(),
|
|
2453
|
+
digestWindowMinutes: z.number().int().optional(),
|
|
2454
|
+
maxEmailsPerDay: z.number().int().optional(),
|
|
2455
|
+
}),
|
|
2268
2456
|
};
|
|
2269
2457
|
|
|
2270
2458
|
// ---------------------------------------------------------------------------
|
|
@@ -2552,6 +2740,54 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
|
|
|
2552
2740
|
}),
|
|
2553
2741
|
"crm.cells.append": entryWriteResponse,
|
|
2554
2742
|
|
|
2743
|
+
// CRM comments + tasks
|
|
2744
|
+
"crm.comments.list": commentPage.extend({
|
|
2745
|
+
page: z.array(commentShape.extend({ replies: z.array(commentShape) })),
|
|
2746
|
+
}),
|
|
2747
|
+
"crm.comments.replies": commentPage,
|
|
2748
|
+
"crm.comments.get": commentShape.extend({
|
|
2749
|
+
objectName: z.string(),
|
|
2750
|
+
entryLabel: z.string(),
|
|
2751
|
+
}),
|
|
2752
|
+
"crm.comments.create": z.object({
|
|
2753
|
+
id: z.string(),
|
|
2754
|
+
rootCommentId: z.string().optional(),
|
|
2755
|
+
depth: z.number(),
|
|
2756
|
+
mentionedUserIds: z.array(z.string()),
|
|
2757
|
+
mentionsDench: z.boolean(),
|
|
2758
|
+
}),
|
|
2759
|
+
"crm.comments.update": z.object({
|
|
2760
|
+
ok: z.boolean(),
|
|
2761
|
+
newlyMentioned: z.array(z.string()),
|
|
2762
|
+
}),
|
|
2763
|
+
"crm.comments.delete": z.object({ ok: z.boolean(), tombstoned: z.boolean() }),
|
|
2764
|
+
"crm.comments.react": z.object({
|
|
2765
|
+
reacted: z.boolean(),
|
|
2766
|
+
reactionSummary: z.array(
|
|
2767
|
+
z.object({ emoji: z.string(), count: z.number() }),
|
|
2768
|
+
),
|
|
2769
|
+
}),
|
|
2770
|
+
"crm.watch.get": z.object({
|
|
2771
|
+
watching: z.boolean(),
|
|
2772
|
+
reason: z.string().optional(),
|
|
2773
|
+
watcherCount: z.number(),
|
|
2774
|
+
}),
|
|
2775
|
+
"crm.watch.set": z.object({ watching: z.boolean() }),
|
|
2776
|
+
"crm.tasks.mine": z.array(z.object({}).passthrough()),
|
|
2777
|
+
"crm.tasks.forEntry": z.array(z.object({}).passthrough()),
|
|
2778
|
+
"crm.tasks.createForEntry": z.object({}).passthrough(),
|
|
2779
|
+
"crm.tasks.complete": z.object({}).passthrough(),
|
|
2780
|
+
"crm.tasks.reopen": z.object({}).passthrough(),
|
|
2781
|
+
|
|
2782
|
+
// Notifications
|
|
2783
|
+
"notifications.list": z.array(notificationShape),
|
|
2784
|
+
"notifications.unreadCount": z.number(),
|
|
2785
|
+
"notifications.read": okResponse,
|
|
2786
|
+
"notifications.readAll": z.object({ ok: z.boolean(), patched: z.number() }),
|
|
2787
|
+
"notifications.archive": okResponse,
|
|
2788
|
+
"notifications.preferences.get": notificationPreferencesShape.nullable(),
|
|
2789
|
+
"notifications.preferences.update": okResponse,
|
|
2790
|
+
|
|
2555
2791
|
// CRM query + search
|
|
2556
2792
|
"crm.query": z
|
|
2557
2793
|
.array(crmEntryShape)
|
|
@@ -3147,6 +3383,21 @@ export const requestExamples: Record<string, unknown> = {
|
|
|
3147
3383
|
Status: "New",
|
|
3148
3384
|
},
|
|
3149
3385
|
},
|
|
3386
|
+
"crm.comments.create": {
|
|
3387
|
+
body: "Kickoff went well — [@Bob](/?member=user_bob) can you send the proposal? [@Dench](/?member=dench) what's the ARR here?",
|
|
3388
|
+
},
|
|
3389
|
+
"crm.comments.update": { body: "Kickoff went well — proposal sent." },
|
|
3390
|
+
"crm.watch.set": { watching: true },
|
|
3391
|
+
"crm.tasks.createForEntry": {
|
|
3392
|
+
name: "Send proposal",
|
|
3393
|
+
assigneeUserId: "user_bob",
|
|
3394
|
+
dueDateKey: "2026-09-05",
|
|
3395
|
+
},
|
|
3396
|
+
"notifications.read": { read: true },
|
|
3397
|
+
"notifications.preferences.update": {
|
|
3398
|
+
emailEnabled: true,
|
|
3399
|
+
perKind: { entry_activity: { email: false } },
|
|
3400
|
+
},
|
|
3150
3401
|
"crm.entries.createMany": {
|
|
3151
3402
|
entries: [
|
|
3152
3403
|
{ Name: "Jane", Email: "jane@example.com" },
|
|
@@ -3696,6 +3947,79 @@ export const responseExamples: Record<string, unknown> = {
|
|
|
3696
3947
|
ok: true,
|
|
3697
3948
|
enumColors: ["#94a3b8", "#3b82f6", "#22c55e", "#ef4444"],
|
|
3698
3949
|
},
|
|
3950
|
+
"crm.comments.list": {
|
|
3951
|
+
page: [
|
|
3952
|
+
{
|
|
3953
|
+
_id: "cmt_01",
|
|
3954
|
+
entryId: "ent_lead99",
|
|
3955
|
+
depth: 0,
|
|
3956
|
+
body: "Kickoff went well — [@Bob](/?member=user_bob) can you send the proposal?",
|
|
3957
|
+
actor: {
|
|
3958
|
+
kind: "user",
|
|
3959
|
+
label: "Ada",
|
|
3960
|
+
displayName: "Ada Lovelace",
|
|
3961
|
+
userId: "user_ada",
|
|
3962
|
+
isDench: false,
|
|
3963
|
+
},
|
|
3964
|
+
mentionedUserIds: ["user_bob"],
|
|
3965
|
+
mentionsDench: false,
|
|
3966
|
+
replyCount: 1,
|
|
3967
|
+
reactionSummary: [{ emoji: "👍", count: 2 }],
|
|
3968
|
+
viewerReactions: ["👍"],
|
|
3969
|
+
canEdit: true,
|
|
3970
|
+
createdAt: 1765526400000,
|
|
3971
|
+
replies: [
|
|
3972
|
+
{
|
|
3973
|
+
_id: "cmt_02",
|
|
3974
|
+
entryId: "ent_lead99",
|
|
3975
|
+
parentCommentId: "cmt_01",
|
|
3976
|
+
rootCommentId: "cmt_01",
|
|
3977
|
+
depth: 1,
|
|
3978
|
+
body: "On it.",
|
|
3979
|
+
actor: {
|
|
3980
|
+
kind: "user",
|
|
3981
|
+
label: "Bob",
|
|
3982
|
+
displayName: "Bob Smith",
|
|
3983
|
+
userId: "user_bob",
|
|
3984
|
+
isDench: false,
|
|
3985
|
+
},
|
|
3986
|
+
mentionedUserIds: [],
|
|
3987
|
+
mentionsDench: false,
|
|
3988
|
+
replyCount: 0,
|
|
3989
|
+
reactionSummary: [],
|
|
3990
|
+
viewerReactions: [],
|
|
3991
|
+
canEdit: false,
|
|
3992
|
+
createdAt: 1765530000000,
|
|
3993
|
+
},
|
|
3994
|
+
],
|
|
3995
|
+
},
|
|
3996
|
+
],
|
|
3997
|
+
isDone: true,
|
|
3998
|
+
continueCursor: "",
|
|
3999
|
+
},
|
|
4000
|
+
"crm.comments.create": {
|
|
4001
|
+
id: "cmt_03",
|
|
4002
|
+
rootCommentId: "cmt_01",
|
|
4003
|
+
depth: 1,
|
|
4004
|
+
mentionedUserIds: ["user_bob"],
|
|
4005
|
+
mentionsDench: true,
|
|
4006
|
+
},
|
|
4007
|
+
"crm.comments.react": {
|
|
4008
|
+
reacted: true,
|
|
4009
|
+
reactionSummary: [{ emoji: "👍", count: 3 }],
|
|
4010
|
+
},
|
|
4011
|
+
"crm.watch.get": { watching: true, reason: "commented", watcherCount: 3 },
|
|
4012
|
+
"notifications.list": [
|
|
4013
|
+
{
|
|
4014
|
+
_id: "ntf_01",
|
|
4015
|
+
kind: "mention",
|
|
4016
|
+
title: "Ada Lovelace mentioned you in Ramp",
|
|
4017
|
+
body: "Kickoff went well — @Bob can you send the proposal?",
|
|
4018
|
+
link: "/acme?entry=company%3Aent_lead99&comment=cmt_01",
|
|
4019
|
+
createdAt: 1765526400000,
|
|
4020
|
+
},
|
|
4021
|
+
],
|
|
4022
|
+
"notifications.unreadCount": 3,
|
|
3699
4023
|
"crm.entries.list": [
|
|
3700
4024
|
{
|
|
3701
4025
|
id: "ent_lead99",
|
package/lib/command-registry.ts
CHANGED
|
@@ -79,6 +79,24 @@ function op(operation: ApiOperation): ApiOperation {
|
|
|
79
79
|
return operation;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/** `?limit=&cursor=` → Convex `paginationOpts`. */
|
|
83
|
+
function paginationOptsFrom(
|
|
84
|
+
input: Record<string, unknown>,
|
|
85
|
+
defaultLimit: number,
|
|
86
|
+
): { numItems: number; cursor: string | null } {
|
|
87
|
+
const limit =
|
|
88
|
+
typeof input.limit === "number"
|
|
89
|
+
? Math.min(Math.max(Math.floor(input.limit), 1), 100)
|
|
90
|
+
: defaultLimit;
|
|
91
|
+
return {
|
|
92
|
+
numItems: limit,
|
|
93
|
+
cursor:
|
|
94
|
+
typeof input.cursor === "string" && input.cursor.length > 0
|
|
95
|
+
? input.cursor
|
|
96
|
+
: null,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
82
100
|
function convex(
|
|
83
101
|
functionType: ConvexOperation["functionType"],
|
|
84
102
|
functionName: string,
|
|
@@ -1042,6 +1060,198 @@ const rawApiOperations = [
|
|
|
1042
1060
|
},
|
|
1043
1061
|
},
|
|
1044
1062
|
}),
|
|
1063
|
+
|
|
1064
|
+
// CRM comments — threaded conversation on a record, merged with history
|
|
1065
|
+
// into the record's Activity feed. `@Dench` in a body summons the agent.
|
|
1066
|
+
op({
|
|
1067
|
+
id: "crm.comments.list",
|
|
1068
|
+
group: "crm",
|
|
1069
|
+
summary:
|
|
1070
|
+
"List comments on a CRM entry (roots newest first, with preview replies).",
|
|
1071
|
+
cli: "dench crm comments list",
|
|
1072
|
+
method: "GET",
|
|
1073
|
+
path: "/crm/objects/{objectName}/entries/{entryId}/comments",
|
|
1074
|
+
requestSchema: noBody,
|
|
1075
|
+
responseSchema: anyResponse,
|
|
1076
|
+
backend: {
|
|
1077
|
+
...convex("query", "functions/crm/comments:listForEntry"),
|
|
1078
|
+
transformArgs: (input) => ({
|
|
1079
|
+
objectName: input.objectName,
|
|
1080
|
+
entryId: input.entryId,
|
|
1081
|
+
previewReplies:
|
|
1082
|
+
typeof input.replies === "number"
|
|
1083
|
+
? Math.min(Math.max(Math.floor(input.replies), 0), 50)
|
|
1084
|
+
: undefined,
|
|
1085
|
+
paginationOpts: paginationOptsFrom(input, 20),
|
|
1086
|
+
}),
|
|
1087
|
+
},
|
|
1088
|
+
}),
|
|
1089
|
+
op({
|
|
1090
|
+
id: "crm.comments.replies",
|
|
1091
|
+
group: "crm",
|
|
1092
|
+
summary: "List every reply under a root comment, oldest first.",
|
|
1093
|
+
cli: "dench crm comments replies",
|
|
1094
|
+
method: "GET",
|
|
1095
|
+
path: "/crm/comments/{commentId}/replies",
|
|
1096
|
+
requestSchema: noBody,
|
|
1097
|
+
responseSchema: anyResponse,
|
|
1098
|
+
backend: {
|
|
1099
|
+
...convex("query", "functions/crm/comments:listReplies"),
|
|
1100
|
+
transformArgs: (input) => ({
|
|
1101
|
+
rootCommentId: input.commentId,
|
|
1102
|
+
paginationOpts: paginationOptsFrom(input, 50),
|
|
1103
|
+
}),
|
|
1104
|
+
},
|
|
1105
|
+
}),
|
|
1106
|
+
op({
|
|
1107
|
+
id: "crm.comments.get",
|
|
1108
|
+
group: "crm",
|
|
1109
|
+
summary: "Get one comment with its entry label.",
|
|
1110
|
+
cli: "dench crm comments get",
|
|
1111
|
+
method: "GET",
|
|
1112
|
+
path: "/crm/comments/{commentId}",
|
|
1113
|
+
requestSchema: noBody,
|
|
1114
|
+
responseSchema: anyResponse,
|
|
1115
|
+
backend: convex("query", "functions/crm/comments:get"),
|
|
1116
|
+
}),
|
|
1117
|
+
op({
|
|
1118
|
+
id: "crm.comments.create",
|
|
1119
|
+
group: "crm",
|
|
1120
|
+
summary:
|
|
1121
|
+
"Comment on a CRM entry (markdown). Mention members with [@Name](/?member=<userId>); mention Dench with [@Dench](/?member=dench) or mentionDench.",
|
|
1122
|
+
cli: "dench crm comments create",
|
|
1123
|
+
method: "POST",
|
|
1124
|
+
path: "/crm/objects/{objectName}/entries/{entryId}/comments",
|
|
1125
|
+
requestSchema: anyObject,
|
|
1126
|
+
responseSchema: anyResponse,
|
|
1127
|
+
backend: convex("mutation", "functions/crm/comments:create", "bearer", {
|
|
1128
|
+
includeRunId: true,
|
|
1129
|
+
}),
|
|
1130
|
+
}),
|
|
1131
|
+
op({
|
|
1132
|
+
id: "crm.comments.update",
|
|
1133
|
+
group: "crm",
|
|
1134
|
+
summary: "Edit a comment you wrote.",
|
|
1135
|
+
cli: "dench crm comments update",
|
|
1136
|
+
method: "PATCH",
|
|
1137
|
+
path: "/crm/comments/{commentId}",
|
|
1138
|
+
requestSchema: anyObject,
|
|
1139
|
+
responseSchema: anyResponse,
|
|
1140
|
+
backend: convex("mutation", "functions/crm/comments:edit"),
|
|
1141
|
+
}),
|
|
1142
|
+
op({
|
|
1143
|
+
id: "crm.comments.delete",
|
|
1144
|
+
group: "crm",
|
|
1145
|
+
summary: "Delete a comment (kept as a tombstone when it has replies).",
|
|
1146
|
+
cli: "dench crm comments delete",
|
|
1147
|
+
method: "DELETE",
|
|
1148
|
+
path: "/crm/comments/{commentId}",
|
|
1149
|
+
requestSchema: noBody,
|
|
1150
|
+
responseSchema: anyResponse,
|
|
1151
|
+
backend: convex("mutation", "functions/crm/comments:remove"),
|
|
1152
|
+
}),
|
|
1153
|
+
op({
|
|
1154
|
+
id: "crm.comments.react",
|
|
1155
|
+
group: "crm",
|
|
1156
|
+
summary: "Toggle an emoji reaction on a comment.",
|
|
1157
|
+
cli: "dench crm comments react",
|
|
1158
|
+
method: "PUT",
|
|
1159
|
+
path: "/crm/comments/{commentId}/reactions/{emoji}",
|
|
1160
|
+
requestSchema: noBody,
|
|
1161
|
+
responseSchema: anyResponse,
|
|
1162
|
+
backend: convex("mutation", "functions/crm/comments:toggleReaction"),
|
|
1163
|
+
}),
|
|
1164
|
+
op({
|
|
1165
|
+
id: "crm.watch.get",
|
|
1166
|
+
group: "crm",
|
|
1167
|
+
summary: "Whether you follow a CRM entry's activity.",
|
|
1168
|
+
cli: "dench crm watch status",
|
|
1169
|
+
method: "GET",
|
|
1170
|
+
path: "/crm/objects/{objectName}/entries/{entryId}/watch",
|
|
1171
|
+
requestSchema: noBody,
|
|
1172
|
+
responseSchema: anyResponse,
|
|
1173
|
+
backend: {
|
|
1174
|
+
...convex("query", "functions/crm/comments:getWatchState"),
|
|
1175
|
+
transformArgs: (input) => ({ entryId: input.entryId }),
|
|
1176
|
+
},
|
|
1177
|
+
}),
|
|
1178
|
+
op({
|
|
1179
|
+
id: "crm.watch.set",
|
|
1180
|
+
group: "crm",
|
|
1181
|
+
summary: "Follow or unfollow a CRM entry's activity.",
|
|
1182
|
+
cli: "dench crm watch",
|
|
1183
|
+
method: "PUT",
|
|
1184
|
+
path: "/crm/objects/{objectName}/entries/{entryId}/watch",
|
|
1185
|
+
requestSchema: anyObject,
|
|
1186
|
+
responseSchema: anyResponse,
|
|
1187
|
+
backend: {
|
|
1188
|
+
...convex("mutation", "functions/crm/comments:setWatching"),
|
|
1189
|
+
transformArgs: (input) => ({
|
|
1190
|
+
entryId: input.entryId,
|
|
1191
|
+
watching: input.watching !== false,
|
|
1192
|
+
}),
|
|
1193
|
+
},
|
|
1194
|
+
}),
|
|
1195
|
+
|
|
1196
|
+
// CRM tasks — conveniences over the protected `task` object. Tasks are
|
|
1197
|
+
// still ordinary entries (`crm.entries.*` works on them); these add the
|
|
1198
|
+
// "mine", "for this record", complete/reopen shortcuts the app uses.
|
|
1199
|
+
op({
|
|
1200
|
+
id: "crm.tasks.mine",
|
|
1201
|
+
group: "crm",
|
|
1202
|
+
summary: "Your open tasks (plus ones completed today).",
|
|
1203
|
+
cli: "dench crm tasks mine",
|
|
1204
|
+
method: "GET",
|
|
1205
|
+
path: "/crm/tasks/mine",
|
|
1206
|
+
requestSchema: noBody,
|
|
1207
|
+
responseSchema: anyResponse,
|
|
1208
|
+
backend: convex("query", "functions/crm/tasks:listMyOpenTasks"),
|
|
1209
|
+
}),
|
|
1210
|
+
op({
|
|
1211
|
+
id: "crm.tasks.forEntry",
|
|
1212
|
+
group: "crm",
|
|
1213
|
+
summary: "Tasks linked to a CRM entry.",
|
|
1214
|
+
cli: "dench crm tasks list",
|
|
1215
|
+
method: "GET",
|
|
1216
|
+
path: "/crm/objects/{objectName}/entries/{entryId}/tasks",
|
|
1217
|
+
requestSchema: noBody,
|
|
1218
|
+
responseSchema: anyResponse,
|
|
1219
|
+
backend: convex("query", "functions/crm/tasks:listEntryTasks"),
|
|
1220
|
+
}),
|
|
1221
|
+
op({
|
|
1222
|
+
id: "crm.tasks.createForEntry",
|
|
1223
|
+
group: "crm",
|
|
1224
|
+
summary: "Create a task linked to a CRM entry.",
|
|
1225
|
+
cli: "dench crm tasks create",
|
|
1226
|
+
method: "POST",
|
|
1227
|
+
path: "/crm/objects/{objectName}/entries/{entryId}/tasks",
|
|
1228
|
+
requestSchema: anyObject,
|
|
1229
|
+
responseSchema: anyResponse,
|
|
1230
|
+
backend: convex("mutation", "functions/crm/tasks:createEntryTask"),
|
|
1231
|
+
}),
|
|
1232
|
+
op({
|
|
1233
|
+
id: "crm.tasks.complete",
|
|
1234
|
+
group: "crm",
|
|
1235
|
+
summary: "Mark a task done.",
|
|
1236
|
+
cli: "dench crm tasks complete",
|
|
1237
|
+
method: "POST",
|
|
1238
|
+
path: "/crm/tasks/{entryId}/complete",
|
|
1239
|
+
requestSchema: noBody,
|
|
1240
|
+
responseSchema: anyResponse,
|
|
1241
|
+
backend: convex("mutation", "functions/crm/tasks:completeTask"),
|
|
1242
|
+
}),
|
|
1243
|
+
op({
|
|
1244
|
+
id: "crm.tasks.reopen",
|
|
1245
|
+
group: "crm",
|
|
1246
|
+
summary: "Reopen a completed task.",
|
|
1247
|
+
cli: "dench crm tasks reopen",
|
|
1248
|
+
method: "POST",
|
|
1249
|
+
path: "/crm/tasks/{entryId}/reopen",
|
|
1250
|
+
requestSchema: noBody,
|
|
1251
|
+
responseSchema: anyResponse,
|
|
1252
|
+
backend: convex("mutation", "functions/crm/tasks:reopenTask"),
|
|
1253
|
+
}),
|
|
1254
|
+
|
|
1045
1255
|
op({
|
|
1046
1256
|
id: "crm.entries.create",
|
|
1047
1257
|
group: "crm",
|
|
@@ -2981,6 +3191,99 @@ const rawApiOperations = [
|
|
|
2981
3191
|
responseSchema: anyResponse,
|
|
2982
3192
|
backend: convex("query", "functions/agentConfig:getAgentConfig"),
|
|
2983
3193
|
}),
|
|
3194
|
+
|
|
3195
|
+
// Notifications — the signed-in member's inbox (the bell). Agent sessions
|
|
3196
|
+
// read their owner's inbox; an org API key with no person behind it is
|
|
3197
|
+
// refused by the backend.
|
|
3198
|
+
op({
|
|
3199
|
+
id: "notifications.list",
|
|
3200
|
+
group: "notifications",
|
|
3201
|
+
summary: "List your notifications (inbox, unread, archived, or all).",
|
|
3202
|
+
cli: "dench notifications list",
|
|
3203
|
+
method: "GET",
|
|
3204
|
+
path: "/notifications",
|
|
3205
|
+
requestSchema: noBody,
|
|
3206
|
+
responseSchema: anyResponse,
|
|
3207
|
+
backend: convex("query", "functions/notifications:listMyNotifications"),
|
|
3208
|
+
}),
|
|
3209
|
+
op({
|
|
3210
|
+
id: "notifications.unreadCount",
|
|
3211
|
+
group: "notifications",
|
|
3212
|
+
summary: "How many unread notifications you have.",
|
|
3213
|
+
cli: "dench notifications count",
|
|
3214
|
+
method: "GET",
|
|
3215
|
+
path: "/notifications/unread-count",
|
|
3216
|
+
requestSchema: noBody,
|
|
3217
|
+
responseSchema: anyResponse,
|
|
3218
|
+
backend: convex("query", "functions/notifications:unreadCount"),
|
|
3219
|
+
}),
|
|
3220
|
+
op({
|
|
3221
|
+
id: "notifications.read",
|
|
3222
|
+
group: "notifications",
|
|
3223
|
+
summary: "Mark a notification read (or unread).",
|
|
3224
|
+
cli: "dench notifications read",
|
|
3225
|
+
method: "PATCH",
|
|
3226
|
+
path: "/notifications/{notificationId}",
|
|
3227
|
+
requestSchema: anyObject,
|
|
3228
|
+
responseSchema: anyResponse,
|
|
3229
|
+
backend: {
|
|
3230
|
+
...convex("mutation", "functions/notifications:setNotificationRead"),
|
|
3231
|
+
transformArgs: (input) => ({
|
|
3232
|
+
notificationId: input.notificationId,
|
|
3233
|
+
read: input.read !== false,
|
|
3234
|
+
}),
|
|
3235
|
+
},
|
|
3236
|
+
}),
|
|
3237
|
+
op({
|
|
3238
|
+
id: "notifications.readAll",
|
|
3239
|
+
group: "notifications",
|
|
3240
|
+
summary: "Mark every inbox notification read.",
|
|
3241
|
+
cli: "dench notifications read-all",
|
|
3242
|
+
method: "POST",
|
|
3243
|
+
path: "/notifications/read-all",
|
|
3244
|
+
requestSchema: noBody,
|
|
3245
|
+
responseSchema: anyResponse,
|
|
3246
|
+
backend: convex("mutation", "functions/notifications:markAllRead"),
|
|
3247
|
+
}),
|
|
3248
|
+
op({
|
|
3249
|
+
id: "notifications.archive",
|
|
3250
|
+
group: "notifications",
|
|
3251
|
+
summary: "Archive a notification.",
|
|
3252
|
+
cli: "dench notifications archive",
|
|
3253
|
+
method: "POST",
|
|
3254
|
+
path: "/notifications/{notificationId}/archive",
|
|
3255
|
+
requestSchema: noBody,
|
|
3256
|
+
responseSchema: anyResponse,
|
|
3257
|
+
backend: convex("mutation", "functions/notifications:archiveNotification"),
|
|
3258
|
+
}),
|
|
3259
|
+
op({
|
|
3260
|
+
id: "notifications.preferences.get",
|
|
3261
|
+
group: "notifications",
|
|
3262
|
+
summary: "Read your notification preferences.",
|
|
3263
|
+
cli: "dench notifications prefs",
|
|
3264
|
+
method: "GET",
|
|
3265
|
+
path: "/notifications/preferences",
|
|
3266
|
+
requestSchema: noBody,
|
|
3267
|
+
responseSchema: anyResponse,
|
|
3268
|
+
backend: convex(
|
|
3269
|
+
"query",
|
|
3270
|
+
"functions/notifications:getMyNotificationPreferences",
|
|
3271
|
+
),
|
|
3272
|
+
}),
|
|
3273
|
+
op({
|
|
3274
|
+
id: "notifications.preferences.update",
|
|
3275
|
+
group: "notifications",
|
|
3276
|
+
summary: "Update your notification preferences.",
|
|
3277
|
+
cli: "dench notifications prefs --set",
|
|
3278
|
+
method: "PATCH",
|
|
3279
|
+
path: "/notifications/preferences",
|
|
3280
|
+
requestSchema: anyObject,
|
|
3281
|
+
responseSchema: anyResponse,
|
|
3282
|
+
backend: convex(
|
|
3283
|
+
"mutation",
|
|
3284
|
+
"functions/notifications:updateMyNotificationPreferences",
|
|
3285
|
+
),
|
|
3286
|
+
}),
|
|
2984
3287
|
] as const satisfies readonly ApiOperation[];
|
|
2985
3288
|
|
|
2986
3289
|
/**
|