@ornncompute/cli 0.2.19 → 0.2.20
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/package.json +1 -1
- package/vendor/capabilities/capabilities/staff-gtm.d.ts +171 -0
- package/vendor/capabilities/capabilities/staff-gtm.js +616 -0
- package/vendor/capabilities/capability.d.ts +1 -1
- package/vendor/capabilities/capability.js +1 -0
- package/vendor/capabilities/catalog.js +2 -0
- package/vendor/capabilities/index.d.ts +2 -1
- package/vendor/capabilities/index.js +2 -1
package/package.json
CHANGED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const gtmCompanyListInput: z.ZodObject<{
|
|
3
|
+
q: z.ZodOptional<z.ZodString>;
|
|
4
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
export declare const gtmCompanyListCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
7
|
+
export declare const gtmCompanyWriteInput: z.ZodObject<{
|
|
8
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
9
|
+
name: z.ZodString;
|
|
10
|
+
ownerTags: z.ZodDefault<z.ZodString>;
|
|
11
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
12
|
+
companyId: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export declare const gtmCompanyWriteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
15
|
+
export declare const gtmDealListInput: z.ZodObject<{
|
|
16
|
+
q: z.ZodOptional<z.ZodString>;
|
|
17
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export declare const gtmDealListCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
20
|
+
export declare const gtmDealWriteInput: z.ZodObject<{
|
|
21
|
+
listingId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<"">]>>;
|
|
22
|
+
currentBid: z.ZodOptional<z.ZodString>;
|
|
23
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
24
|
+
customer: z.ZodString;
|
|
25
|
+
owner: z.ZodString;
|
|
26
|
+
supplier: z.ZodString;
|
|
27
|
+
gpuType: z.ZodString;
|
|
28
|
+
dealId: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
}, z.core.$strip>;
|
|
30
|
+
export declare const gtmDealWriteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
31
|
+
export declare const gtmCommentListInput: z.ZodObject<{
|
|
32
|
+
dealId: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
companyId: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
}, z.core.$strip>;
|
|
35
|
+
export declare const gtmCommentListCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
36
|
+
export declare const gtmCommentWriteInput: z.ZodObject<{
|
|
37
|
+
dealId: z.ZodOptional<z.ZodNumber>;
|
|
38
|
+
companyId: z.ZodOptional<z.ZodNumber>;
|
|
39
|
+
commentId: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
parentId: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
body: z.ZodString;
|
|
42
|
+
bid: z.ZodDefault<z.ZodBoolean>;
|
|
43
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
export declare const gtmCommentWriteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
46
|
+
export declare const gtmMentionDismissInput: z.ZodObject<{
|
|
47
|
+
commentId: z.ZodNumber;
|
|
48
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
export declare const gtmMentionDismissCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
51
|
+
export declare const gtmWorkspaceShowInput: z.ZodObject<{}, z.core.$strip>;
|
|
52
|
+
export declare const gtmWorkspaceShowCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
53
|
+
export declare const gtmTaskListInput: z.ZodObject<{
|
|
54
|
+
q: z.ZodOptional<z.ZodString>;
|
|
55
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
}, z.core.$strip>;
|
|
57
|
+
export declare const gtmTaskListCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
58
|
+
export declare const gtmTaskWriteInput: z.ZodObject<{
|
|
59
|
+
taskId: z.ZodOptional<z.ZodNumber>;
|
|
60
|
+
title: z.ZodString;
|
|
61
|
+
body: z.ZodDefault<z.ZodString>;
|
|
62
|
+
companyId: z.ZodNumber;
|
|
63
|
+
dealId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
64
|
+
assignee: z.ZodString;
|
|
65
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
66
|
+
open: "open";
|
|
67
|
+
done: "done";
|
|
68
|
+
}>>;
|
|
69
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
70
|
+
}, z.core.$strip>;
|
|
71
|
+
export declare const gtmTaskWriteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
72
|
+
export declare const gtmCompanyDeleteInput: z.ZodObject<{
|
|
73
|
+
[x: string]: z.ZodNumber | z.ZodOptional<z.ZodBoolean>;
|
|
74
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
export declare const gtmCompanyDeleteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
77
|
+
export declare const gtmDealDeleteInput: z.ZodObject<{
|
|
78
|
+
[x: string]: z.ZodNumber | z.ZodOptional<z.ZodBoolean>;
|
|
79
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
export declare const gtmDealDeleteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
82
|
+
export declare const gtmCommentDeleteInput: z.ZodObject<{
|
|
83
|
+
[x: string]: z.ZodNumber | z.ZodOptional<z.ZodBoolean>;
|
|
84
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
85
|
+
}, z.core.$strip>;
|
|
86
|
+
export declare const gtmCommentDeleteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
87
|
+
export declare const gtmTaskDeleteInput: z.ZodObject<{
|
|
88
|
+
[x: string]: z.ZodNumber | z.ZodOptional<z.ZodBoolean>;
|
|
89
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
export declare const gtmTaskDeleteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
92
|
+
/**
|
|
93
|
+
* Listing verbs. GTM never touches a Commerce table directly: these call
|
|
94
|
+
* Commerce's own listing routes through the gateway, exactly as the staff app
|
|
95
|
+
* does. Nothing here writes a gtm_ row, and nothing in the CRM constrains a
|
|
96
|
+
* listing.
|
|
97
|
+
*/
|
|
98
|
+
export declare const gtmListingListInput: z.ZodObject<{
|
|
99
|
+
gpuType: z.ZodOptional<z.ZodString>;
|
|
100
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
}, z.core.$strip>;
|
|
102
|
+
export declare const gtmListingListCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
103
|
+
export declare const gtmListingUpdateInput: z.ZodObject<{
|
|
104
|
+
listingId: z.ZodString;
|
|
105
|
+
buyNowRatePerGpuHour: z.ZodOptional<z.ZodString>;
|
|
106
|
+
costPerGpuHour: z.ZodOptional<z.ZodString>;
|
|
107
|
+
monthToMonth: z.ZodOptional<z.ZodBoolean>;
|
|
108
|
+
allowsDepositPlan: z.ZodOptional<z.ZodBoolean>;
|
|
109
|
+
downPaymentPct: z.ZodOptional<z.ZodString>;
|
|
110
|
+
capacityNodes: z.ZodOptional<z.ZodNumber>;
|
|
111
|
+
state: z.ZodOptional<z.ZodEnum<{
|
|
112
|
+
delisted: "delisted";
|
|
113
|
+
draft: "draft";
|
|
114
|
+
listed: "listed";
|
|
115
|
+
}>>;
|
|
116
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
117
|
+
}, z.core.$strip>;
|
|
118
|
+
export declare const gtmListingUpdateCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
119
|
+
export declare const gtmTaskActivityListInput: z.ZodObject<{
|
|
120
|
+
taskId: z.ZodNumber;
|
|
121
|
+
}, z.core.$strip>;
|
|
122
|
+
export declare const gtmTaskActivityListCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
123
|
+
export declare const gtmTaskActivityWriteInput: z.ZodObject<{
|
|
124
|
+
taskId: z.ZodNumber;
|
|
125
|
+
body: z.ZodString;
|
|
126
|
+
parentId: z.ZodOptional<z.ZodNumber>;
|
|
127
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
128
|
+
}, z.core.$strip>;
|
|
129
|
+
export declare const gtmTaskActivityWriteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
130
|
+
export declare const gtmCellCommentListInput: z.ZodObject<{
|
|
131
|
+
companyId: z.ZodNumber;
|
|
132
|
+
field: z.ZodEnum<{
|
|
133
|
+
name: "name";
|
|
134
|
+
notes: "notes";
|
|
135
|
+
owner_tags: "owner_tags";
|
|
136
|
+
tickets: "tickets";
|
|
137
|
+
}>;
|
|
138
|
+
}, z.core.$strip>;
|
|
139
|
+
export declare const gtmCellCommentListCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
140
|
+
export declare const gtmCellCommentWriteInput: z.ZodObject<{
|
|
141
|
+
companyId: z.ZodOptional<z.ZodNumber>;
|
|
142
|
+
field: z.ZodOptional<z.ZodEnum<{
|
|
143
|
+
name: "name";
|
|
144
|
+
notes: "notes";
|
|
145
|
+
owner_tags: "owner_tags";
|
|
146
|
+
tickets: "tickets";
|
|
147
|
+
}>>;
|
|
148
|
+
commentId: z.ZodOptional<z.ZodNumber>;
|
|
149
|
+
parentId: z.ZodOptional<z.ZodNumber>;
|
|
150
|
+
body: z.ZodString;
|
|
151
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
152
|
+
}, z.core.$strip>;
|
|
153
|
+
export declare const gtmCellCommentWriteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
154
|
+
export declare const gtmCellCommentDeleteInput: z.ZodObject<{
|
|
155
|
+
[x: string]: z.ZodNumber | z.ZodOptional<z.ZodBoolean>;
|
|
156
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
157
|
+
}, z.core.$strip>;
|
|
158
|
+
export declare const gtmCellCommentDeleteCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
159
|
+
/**
|
|
160
|
+
* Bulk owner-tag reassignment. This is the one GTM edit that is genuinely
|
|
161
|
+
* N-at-a-time — a book of companies moving to a new owner — and doing it one
|
|
162
|
+
* confirmation at a time is both slow and easy to half-finish. Every other
|
|
163
|
+
* bulk case is a loop over the single-record verbs.
|
|
164
|
+
*/
|
|
165
|
+
export declare const gtmCompanyBulkRetagInput: z.ZodObject<{
|
|
166
|
+
companyIds: z.ZodArray<z.ZodNumber>;
|
|
167
|
+
ownerTags: z.ZodString;
|
|
168
|
+
confirm: z.ZodOptional<z.ZodBoolean>;
|
|
169
|
+
}, z.core.$strip>;
|
|
170
|
+
export declare const gtmCompanyBulkRetagCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
171
|
+
export declare const staffGTMCapabilities: import("../capability.ts").Capability<unknown, unknown>[];
|
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineStaffMcpCapability, preview } from "../capability.js";
|
|
3
|
+
import { seg } from "./staff-shared.js";
|
|
4
|
+
const companyFields = {
|
|
5
|
+
name: z.string().min(1),
|
|
6
|
+
ownerTags: z.string().default(""),
|
|
7
|
+
/** The editable company summary that sits above the comment thread. */
|
|
8
|
+
notes: z.string().optional(),
|
|
9
|
+
};
|
|
10
|
+
/** Narrowing every list accepts, so an agent need not hold the whole table. */
|
|
11
|
+
const listFilterFields = {
|
|
12
|
+
q: z.string().min(1).optional(),
|
|
13
|
+
limit: z.number().int().positive().max(500).optional(),
|
|
14
|
+
};
|
|
15
|
+
function listQuery(parsed) {
|
|
16
|
+
const parts = [];
|
|
17
|
+
if (parsed.q)
|
|
18
|
+
parts.push(`q=${encodeURIComponent(parsed.q)}`);
|
|
19
|
+
if (parsed.limit)
|
|
20
|
+
parts.push(`limit=${parsed.limit}`);
|
|
21
|
+
return parts.length ? `?${parts.join("&")}` : "";
|
|
22
|
+
}
|
|
23
|
+
export const gtmCompanyListInput = z.object({ ...listFilterFields });
|
|
24
|
+
export const gtmCompanyListCapability = defineStaffMcpCapability({
|
|
25
|
+
id: "gtm.company.list",
|
|
26
|
+
domain: "gtm",
|
|
27
|
+
roles: ["reviewer", "admin"],
|
|
28
|
+
description: "GTM staff only: list plaintext CRM companies, their owner tags and summary notes. Narrow with q, cap with limit. A company's running discussion is its comment thread.",
|
|
29
|
+
mutation: "read",
|
|
30
|
+
http: { method: "GET", path: "/internal/v1/gtm/companies" },
|
|
31
|
+
input: gtmCompanyListInput,
|
|
32
|
+
execute: (ctx, input) => ctx.fetch(`/internal/v1/gtm/companies${listQuery(gtmCompanyListInput.parse(input))}`, {
|
|
33
|
+
method: "GET",
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
export const gtmCompanyWriteInput = z.object({
|
|
37
|
+
companyId: z.number().int().positive().optional(),
|
|
38
|
+
...companyFields,
|
|
39
|
+
confirm: z.boolean().optional(),
|
|
40
|
+
});
|
|
41
|
+
export const gtmCompanyWriteCapability = defineStaffMcpCapability({
|
|
42
|
+
id: "gtm.company.write",
|
|
43
|
+
domain: "gtm",
|
|
44
|
+
roles: ["reviewer", "admin"],
|
|
45
|
+
description: "GTM staff only: create a plaintext CRM company, or replace its name, owner tags and summary notes when companyId is supplied. Requires confirm: true.",
|
|
46
|
+
mutation: "preview-confirm",
|
|
47
|
+
http: { method: "POST", path: "/internal/v1/gtm/companies" },
|
|
48
|
+
input: gtmCompanyWriteInput,
|
|
49
|
+
execute: async (ctx, input) => {
|
|
50
|
+
const parsed = gtmCompanyWriteInput.parse(input);
|
|
51
|
+
const body = { name: parsed.name, owner_tags: parsed.ownerTags };
|
|
52
|
+
if (parsed.notes !== undefined)
|
|
53
|
+
body.notes = parsed.notes;
|
|
54
|
+
if (!ctx.confirmed) {
|
|
55
|
+
return preview("gtm.company.write", { companyId: parsed.companyId, ...body });
|
|
56
|
+
}
|
|
57
|
+
return ctx.fetch(parsed.companyId
|
|
58
|
+
? `/internal/v1/gtm/companies/${seg(String(parsed.companyId))}`
|
|
59
|
+
: "/internal/v1/gtm/companies", { method: parsed.companyId ? "PATCH" : "POST", body });
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const dealFields = {
|
|
63
|
+
customer: z.string().min(1),
|
|
64
|
+
owner: z.string().min(1),
|
|
65
|
+
supplier: z.string().min(1),
|
|
66
|
+
gpuType: z.string().min(1),
|
|
67
|
+
};
|
|
68
|
+
export const gtmDealListInput = z.object({ ...listFilterFields });
|
|
69
|
+
export const gtmDealListCapability = defineStaffMcpCapability({
|
|
70
|
+
id: "gtm.deal.list",
|
|
71
|
+
domain: "gtm",
|
|
72
|
+
roles: ["reviewer", "admin"],
|
|
73
|
+
description: "GTM staff only: list manually entered deals. Each deal has only customer, Ornn owner, supplier, and GPU type. Narrow with q, cap with limit.",
|
|
74
|
+
mutation: "read",
|
|
75
|
+
http: { method: "GET", path: "/internal/v1/gtm/deals" },
|
|
76
|
+
input: gtmDealListInput,
|
|
77
|
+
execute: (ctx, input) => ctx.fetch(`/internal/v1/gtm/deals${listQuery(gtmDealListInput.parse(input))}`, {
|
|
78
|
+
method: "GET",
|
|
79
|
+
}),
|
|
80
|
+
});
|
|
81
|
+
export const gtmDealWriteInput = z.object({
|
|
82
|
+
dealId: z.number().int().positive().optional(),
|
|
83
|
+
...dealFields,
|
|
84
|
+
/** The negotiation's one link to Commerce. "" clears it. */
|
|
85
|
+
listingId: z.union([z.string().uuid(), z.literal("")]).optional(),
|
|
86
|
+
/** The negotiation's own bid. "" clears it. Never writes the listing. */
|
|
87
|
+
currentBid: z.string().optional(),
|
|
88
|
+
confirm: z.boolean().optional(),
|
|
89
|
+
});
|
|
90
|
+
export const gtmDealWriteCapability = defineStaffMcpCapability({
|
|
91
|
+
id: "gtm.deal.write",
|
|
92
|
+
domain: "gtm",
|
|
93
|
+
roles: ["reviewer", "admin"],
|
|
94
|
+
description: 'GTM staff only: create a negotiation, or replace it when dealId is supplied. The customer also associates it to that company. listingId links it to one Commerce listing and is the only connection between the two; currentBid records what the buyer offered and never writes the listing. Pass "" to clear either. Requires confirm: true.',
|
|
95
|
+
mutation: "preview-confirm",
|
|
96
|
+
http: { method: "POST", path: "/internal/v1/gtm/deals" },
|
|
97
|
+
input: gtmDealWriteInput,
|
|
98
|
+
execute: async (ctx, input) => {
|
|
99
|
+
const parsed = gtmDealWriteInput.parse(input);
|
|
100
|
+
const body = {
|
|
101
|
+
customer: parsed.customer,
|
|
102
|
+
owner: parsed.owner,
|
|
103
|
+
supplier: parsed.supplier,
|
|
104
|
+
gpu_type: parsed.gpuType,
|
|
105
|
+
};
|
|
106
|
+
// Omitted leaves the stored value alone; "" clears it.
|
|
107
|
+
if (parsed.listingId !== undefined)
|
|
108
|
+
body.listing_id = parsed.listingId;
|
|
109
|
+
if (parsed.currentBid !== undefined)
|
|
110
|
+
body.current_bid = parsed.currentBid;
|
|
111
|
+
if (!ctx.confirmed) {
|
|
112
|
+
return preview("gtm.deal.write", { dealId: parsed.dealId, ...body });
|
|
113
|
+
}
|
|
114
|
+
return ctx.fetch(parsed.dealId
|
|
115
|
+
? `/internal/v1/gtm/deals/${seg(String(parsed.dealId))}`
|
|
116
|
+
: "/internal/v1/gtm/deals", { method: parsed.dealId ? "PATCH" : "POST", body });
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
export const gtmCommentListInput = z.object({
|
|
120
|
+
dealId: z.number().int().positive().optional(),
|
|
121
|
+
companyId: z.number().int().positive().optional(),
|
|
122
|
+
});
|
|
123
|
+
export const gtmCommentListCapability = defineStaffMcpCapability({
|
|
124
|
+
id: "gtm.comment.list",
|
|
125
|
+
domain: "gtm",
|
|
126
|
+
roles: ["reviewer", "admin"],
|
|
127
|
+
description: "GTM staff only: list the plaintext comment thread for one deal or one company, replies included. A reply carries parent_id.",
|
|
128
|
+
mutation: "read",
|
|
129
|
+
http: { method: "GET", path: "/internal/v1/gtm/deals/{dealId}/comments" },
|
|
130
|
+
input: gtmCommentListInput,
|
|
131
|
+
execute: (ctx, input) => {
|
|
132
|
+
const parsed = gtmCommentListInput.parse(input);
|
|
133
|
+
return ctx.fetch(commentThreadPath(parsed), { method: "GET" });
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
export const gtmCommentWriteInput = z.object({
|
|
137
|
+
dealId: z.number().int().positive().optional(),
|
|
138
|
+
companyId: z.number().int().positive().optional(),
|
|
139
|
+
commentId: z.number().int().positive().optional(),
|
|
140
|
+
/** Set to reply to an existing comment. Threads are one level deep. */
|
|
141
|
+
parentId: z.number().int().positive().optional(),
|
|
142
|
+
body: z.string().min(1),
|
|
143
|
+
bid: z.boolean().default(false),
|
|
144
|
+
confirm: z.boolean().optional(),
|
|
145
|
+
});
|
|
146
|
+
export const gtmCommentWriteCapability = defineStaffMcpCapability({
|
|
147
|
+
id: "gtm.comment.write",
|
|
148
|
+
domain: "gtm",
|
|
149
|
+
roles: ["reviewer", "admin"],
|
|
150
|
+
description: "GTM staff only: append a plaintext comment to a deal or a company, reply to one with parentId, or replace one when commentId is supplied. bid=true adds only the BID tag and changes no other behavior. Mention a teammate by writing @name in the body. Requires confirm: true.",
|
|
151
|
+
mutation: "preview-confirm",
|
|
152
|
+
http: { method: "POST", path: "/internal/v1/gtm/deals/{dealId}/comments" },
|
|
153
|
+
input: gtmCommentWriteInput,
|
|
154
|
+
execute: async (ctx, input) => {
|
|
155
|
+
const parsed = gtmCommentWriteInput.parse(input);
|
|
156
|
+
if (!parsed.commentId && !parsed.dealId && !parsed.companyId) {
|
|
157
|
+
throw new Error("dealId or companyId is required when creating a comment");
|
|
158
|
+
}
|
|
159
|
+
const body = {
|
|
160
|
+
body: parsed.body,
|
|
161
|
+
tag: parsed.bid ? "BID" : null,
|
|
162
|
+
};
|
|
163
|
+
if (parsed.parentId) {
|
|
164
|
+
body.parent_id = parsed.parentId;
|
|
165
|
+
}
|
|
166
|
+
if (!ctx.confirmed) {
|
|
167
|
+
return preview("gtm.comment.write", {
|
|
168
|
+
dealId: parsed.dealId,
|
|
169
|
+
companyId: parsed.companyId,
|
|
170
|
+
commentId: parsed.commentId,
|
|
171
|
+
...body,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return ctx.fetch(parsed.commentId
|
|
175
|
+
? `/internal/v1/gtm/comments/${seg(String(parsed.commentId))}`
|
|
176
|
+
: commentThreadPath(parsed), { method: parsed.commentId ? "PATCH" : "POST", body });
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
/** A comment thread hangs off exactly one subject. */
|
|
180
|
+
function commentThreadPath(parsed) {
|
|
181
|
+
if (parsed.companyId) {
|
|
182
|
+
return `/internal/v1/gtm/companies/${seg(String(parsed.companyId))}/comments`;
|
|
183
|
+
}
|
|
184
|
+
if (parsed.dealId) {
|
|
185
|
+
return `/internal/v1/gtm/deals/${seg(String(parsed.dealId))}/comments`;
|
|
186
|
+
}
|
|
187
|
+
throw new Error("dealId or companyId is required");
|
|
188
|
+
}
|
|
189
|
+
export const gtmMentionDismissInput = z.object({
|
|
190
|
+
commentId: z.number().int().positive(),
|
|
191
|
+
confirm: z.boolean().optional(),
|
|
192
|
+
});
|
|
193
|
+
export const gtmMentionDismissCapability = defineStaffMcpCapability({
|
|
194
|
+
id: "gtm.mention.dismiss",
|
|
195
|
+
domain: "gtm",
|
|
196
|
+
roles: ["reviewer", "admin"],
|
|
197
|
+
description: "GTM staff only: dismiss one mention for the signed-in user. The comment is untouched and everyone else still sees it. Requires confirm: true.",
|
|
198
|
+
mutation: "preview-confirm",
|
|
199
|
+
http: { method: "POST", path: "/internal/v1/gtm/comments/{commentId}/dismiss" },
|
|
200
|
+
input: gtmMentionDismissInput,
|
|
201
|
+
execute: async (ctx, input) => {
|
|
202
|
+
const parsed = gtmMentionDismissInput.parse(input);
|
|
203
|
+
if (!ctx.confirmed) {
|
|
204
|
+
return preview("gtm.mention.dismiss", { commentId: parsed.commentId });
|
|
205
|
+
}
|
|
206
|
+
return ctx.fetch(`/internal/v1/gtm/comments/${seg(String(parsed.commentId))}/dismiss`, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
export const gtmWorkspaceShowInput = z.object({});
|
|
212
|
+
export const gtmWorkspaceShowCapability = defineStaffMcpCapability({
|
|
213
|
+
id: "gtm.workspace.show",
|
|
214
|
+
domain: "gtm",
|
|
215
|
+
roles: ["reviewer", "admin"],
|
|
216
|
+
description: "GTM staff only: show companies tagged to the signed-in user, deals they own, undismissed mentions of them, and tickets assigned to them.",
|
|
217
|
+
mutation: "read",
|
|
218
|
+
http: { method: "GET", path: "/internal/v1/gtm/me" },
|
|
219
|
+
input: gtmWorkspaceShowInput,
|
|
220
|
+
execute: (ctx) => ctx.fetch("/internal/v1/gtm/me", { method: "GET" }),
|
|
221
|
+
});
|
|
222
|
+
export const gtmTaskListInput = z.object({ ...listFilterFields });
|
|
223
|
+
export const gtmTaskListCapability = defineStaffMcpCapability({
|
|
224
|
+
id: "gtm.task.list",
|
|
225
|
+
domain: "gtm",
|
|
226
|
+
roles: ["reviewer", "admin"],
|
|
227
|
+
description: "GTM staff only: list isolated CRM tickets tied to companies. Narrow with q, cap with limit.",
|
|
228
|
+
mutation: "read",
|
|
229
|
+
http: { method: "GET", path: "/internal/v1/gtm/tasks" },
|
|
230
|
+
input: gtmTaskListInput,
|
|
231
|
+
execute: (ctx, input) => ctx.fetch(`/internal/v1/gtm/tasks${listQuery(gtmTaskListInput.parse(input))}`, {
|
|
232
|
+
method: "GET",
|
|
233
|
+
}),
|
|
234
|
+
});
|
|
235
|
+
export const gtmTaskWriteInput = z
|
|
236
|
+
.object({
|
|
237
|
+
taskId: z.number().int().positive().optional(),
|
|
238
|
+
title: z.string().min(1),
|
|
239
|
+
body: z.string().default(""),
|
|
240
|
+
companyId: z.number().int().positive(),
|
|
241
|
+
dealId: z.number().int().positive().nullable().optional(),
|
|
242
|
+
assignee: z.string().min(1),
|
|
243
|
+
status: z.enum(["open", "done"]).optional(),
|
|
244
|
+
confirm: z.boolean().optional(),
|
|
245
|
+
})
|
|
246
|
+
// Creating defaults to open; updating replaces the whole ticket, so an
|
|
247
|
+
// omitted status would be sent as empty and rejected by Commerce. Catch it
|
|
248
|
+
// here instead of after the caller has confirmed.
|
|
249
|
+
.refine((value) => value.taskId === undefined || value.status !== undefined, {
|
|
250
|
+
message: "status is required when updating a ticket",
|
|
251
|
+
path: ["status"],
|
|
252
|
+
});
|
|
253
|
+
export const gtmTaskWriteCapability = defineStaffMcpCapability({
|
|
254
|
+
id: "gtm.task.write",
|
|
255
|
+
domain: "gtm",
|
|
256
|
+
roles: ["reviewer", "admin"],
|
|
257
|
+
description: "GTM staff only: create a company ticket, or replace one when taskId is supplied. Requires confirm: true.",
|
|
258
|
+
mutation: "preview-confirm",
|
|
259
|
+
http: { method: "POST", path: "/internal/v1/gtm/tasks" },
|
|
260
|
+
input: gtmTaskWriteInput,
|
|
261
|
+
execute: async (ctx, input) => {
|
|
262
|
+
const parsed = gtmTaskWriteInput.parse(input);
|
|
263
|
+
const body = {
|
|
264
|
+
title: parsed.title,
|
|
265
|
+
body: parsed.body,
|
|
266
|
+
company_id: parsed.companyId,
|
|
267
|
+
deal_id: parsed.dealId ?? null,
|
|
268
|
+
assignee: parsed.assignee,
|
|
269
|
+
status: parsed.status,
|
|
270
|
+
};
|
|
271
|
+
if (!ctx.confirmed) {
|
|
272
|
+
return preview("gtm.task.write", { taskId: parsed.taskId, ...body });
|
|
273
|
+
}
|
|
274
|
+
return ctx.fetch(parsed.taskId
|
|
275
|
+
? `/internal/v1/gtm/tasks/${seg(String(parsed.taskId))}`
|
|
276
|
+
: "/internal/v1/gtm/tasks", { method: parsed.taskId ? "PATCH" : "POST", body });
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
/**
|
|
280
|
+
* Delete verbs. Each takes the record id, previews first, and maps onto the
|
|
281
|
+
* matching DELETE route. Deleting a deal removes its comments with it; a
|
|
282
|
+
* company refuses while deals or tickets still name it.
|
|
283
|
+
*/
|
|
284
|
+
function gtmDeleteCapability(entity, idKey, route, consequence) {
|
|
285
|
+
const input = z.object({
|
|
286
|
+
[idKey]: z.number().int().positive(),
|
|
287
|
+
confirm: z.boolean().optional(),
|
|
288
|
+
});
|
|
289
|
+
const capability = defineStaffMcpCapability({
|
|
290
|
+
id: `gtm.${entity}.delete`,
|
|
291
|
+
domain: "gtm",
|
|
292
|
+
roles: ["reviewer", "admin"],
|
|
293
|
+
description: `GTM staff only: delete one ${entity}. ${consequence} Requires confirm: true.`,
|
|
294
|
+
mutation: "preview-confirm",
|
|
295
|
+
http: { method: "DELETE", path: `${route}/{${idKey}}` },
|
|
296
|
+
input,
|
|
297
|
+
execute: async (ctx, raw) => {
|
|
298
|
+
const parsed = input.parse(raw);
|
|
299
|
+
const id = parsed[idKey];
|
|
300
|
+
if (!ctx.confirmed) {
|
|
301
|
+
return preview(`gtm.${entity}.delete`, { [idKey]: id });
|
|
302
|
+
}
|
|
303
|
+
return ctx.fetch(`${route}/${seg(String(id))}`, { method: "DELETE" });
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
return { input, capability };
|
|
307
|
+
}
|
|
308
|
+
const companyDelete = gtmDeleteCapability("company", "companyId", "/internal/v1/gtm/companies", "Refused while the company still has deals or tickets.");
|
|
309
|
+
export const gtmCompanyDeleteInput = companyDelete.input;
|
|
310
|
+
export const gtmCompanyDeleteCapability = companyDelete.capability;
|
|
311
|
+
const dealDelete = gtmDeleteCapability("deal", "dealId", "/internal/v1/gtm/deals", "Its comments are deleted with it; tickets that referenced it are kept.");
|
|
312
|
+
export const gtmDealDeleteInput = dealDelete.input;
|
|
313
|
+
export const gtmDealDeleteCapability = dealDelete.capability;
|
|
314
|
+
const commentDelete = gtmDeleteCapability("comment", "commentId", "/internal/v1/gtm/comments", "Removes only that comment.");
|
|
315
|
+
export const gtmCommentDeleteInput = commentDelete.input;
|
|
316
|
+
export const gtmCommentDeleteCapability = commentDelete.capability;
|
|
317
|
+
const taskDelete = gtmDeleteCapability("task", "taskId", "/internal/v1/gtm/tasks", "Removes only that ticket.");
|
|
318
|
+
export const gtmTaskDeleteInput = taskDelete.input;
|
|
319
|
+
export const gtmTaskDeleteCapability = taskDelete.capability;
|
|
320
|
+
/**
|
|
321
|
+
* Listing verbs. GTM never touches a Commerce table directly: these call
|
|
322
|
+
* Commerce's own listing routes through the gateway, exactly as the staff app
|
|
323
|
+
* does. Nothing here writes a gtm_ row, and nothing in the CRM constrains a
|
|
324
|
+
* listing.
|
|
325
|
+
*/
|
|
326
|
+
export const gtmListingListInput = z.object({
|
|
327
|
+
gpuType: z.string().optional(),
|
|
328
|
+
limit: z.number().int().positive().max(200).optional(),
|
|
329
|
+
});
|
|
330
|
+
export const gtmListingListCapability = defineStaffMcpCapability({
|
|
331
|
+
id: "gtm.listing.list",
|
|
332
|
+
domain: "gtm",
|
|
333
|
+
roles: ["reviewer", "admin"],
|
|
334
|
+
description: "GTM staff only: list Commerce listings to apply a bid against, optionally filtered by GPU type. Read-only; this is Commerce's catalog, not CRM data.",
|
|
335
|
+
mutation: "read",
|
|
336
|
+
http: { method: "GET", path: "/internal/v1/listings" },
|
|
337
|
+
input: gtmListingListInput,
|
|
338
|
+
execute: async (ctx, input) => {
|
|
339
|
+
const parsed = gtmListingListInput.parse(input);
|
|
340
|
+
const parts = [`limit=${encodeURIComponent(String(parsed.limit ?? 50))}`];
|
|
341
|
+
if (parsed.gpuType)
|
|
342
|
+
parts.push(`q=${encodeURIComponent(parsed.gpuType)}`);
|
|
343
|
+
const page = (await ctx.fetch(`/internal/v1/listings?${parts.join("&")}`, {
|
|
344
|
+
method: "GET",
|
|
345
|
+
}));
|
|
346
|
+
return { listings: (page.listings ?? []).map(publicListing) };
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
export const gtmListingUpdateInput = z.object({
|
|
350
|
+
listingId: z.string().uuid(),
|
|
351
|
+
buyNowRatePerGpuHour: z.string().optional(),
|
|
352
|
+
costPerGpuHour: z.string().optional(),
|
|
353
|
+
/** true = month to month, false = the fixed window the listing already carries. */
|
|
354
|
+
monthToMonth: z.boolean().optional(),
|
|
355
|
+
allowsDepositPlan: z.boolean().optional(),
|
|
356
|
+
downPaymentPct: z.string().optional(),
|
|
357
|
+
capacityNodes: z.number().int().positive().optional(),
|
|
358
|
+
state: z.enum(["draft", "listed", "delisted"]).optional(),
|
|
359
|
+
confirm: z.boolean().optional(),
|
|
360
|
+
});
|
|
361
|
+
export const gtmListingUpdateCapability = defineStaffMcpCapability({
|
|
362
|
+
id: "gtm.listing.update",
|
|
363
|
+
domain: "gtm",
|
|
364
|
+
roles: ["reviewer", "admin"],
|
|
365
|
+
description: "GTM staff only: update a Commerce listing's commercial terms — buy-now rate, cost, term and deposit plan — plus capacity or state. Setting a buy-now rate also enables buy-now, otherwise the rate would be inert. Commerce owns the record; this is its own PATCH, not a CRM write. Requires confirm: true.",
|
|
366
|
+
mutation: "preview-confirm",
|
|
367
|
+
http: { method: "PATCH", path: "/internal/v1/listings/{listingId}" },
|
|
368
|
+
input: gtmListingUpdateInput,
|
|
369
|
+
execute: async (ctx, input) => {
|
|
370
|
+
const parsed = gtmListingUpdateInput.parse(input);
|
|
371
|
+
const body = listingPatchBody(parsed);
|
|
372
|
+
if (Object.keys(body).length === 0) {
|
|
373
|
+
throw new Error("supply at least one field to update");
|
|
374
|
+
}
|
|
375
|
+
if (!ctx.confirmed) {
|
|
376
|
+
return preview("gtm.listing.update", { listingId: parsed.listingId, ...body });
|
|
377
|
+
}
|
|
378
|
+
return patchListing(ctx, parsed.listingId, body);
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
/**
|
|
382
|
+
* Strip true operator identity before it reaches an MCP client. Facility and
|
|
383
|
+
* region are allowed; operator ids and provider names are not. Every listing
|
|
384
|
+
* this module returns -- list, update, and the apply result -- goes through it.
|
|
385
|
+
*/
|
|
386
|
+
function publicListing(raw) {
|
|
387
|
+
const listing = raw;
|
|
388
|
+
const { operatorId: _operatorId, operatorName: _operatorName, provider: _provider, ...safe } = listing;
|
|
389
|
+
return safe;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* The listing PATCH is a compare-and-set: it refuses without the version the
|
|
393
|
+
* row was read at. Read it here so a caller never has to guess, and a listing
|
|
394
|
+
* changed in between fails loudly rather than being clobbered.
|
|
395
|
+
*/
|
|
396
|
+
async function patchListing(ctx, listingId, body) {
|
|
397
|
+
const current = (await ctx.fetch(`/internal/v1/listings/${seg(listingId)}`, {
|
|
398
|
+
method: "GET",
|
|
399
|
+
}));
|
|
400
|
+
if (typeof current.version !== "number") {
|
|
401
|
+
throw new Error("listing did not report a version to update against");
|
|
402
|
+
}
|
|
403
|
+
const updated = await ctx.fetch(`/internal/v1/listings/${seg(listingId)}`, {
|
|
404
|
+
method: "PATCH",
|
|
405
|
+
body: { ...body, version: current.version },
|
|
406
|
+
});
|
|
407
|
+
return publicListing(updated);
|
|
408
|
+
}
|
|
409
|
+
function listingPatchBody(parsed) {
|
|
410
|
+
const body = {};
|
|
411
|
+
if (parsed.buyNowRatePerGpuHour !== undefined) {
|
|
412
|
+
body.buyNowRatePerGpuHour = parsed.buyNowRatePerGpuHour;
|
|
413
|
+
// A rate with buy-now still disabled would change nothing a buyer sees.
|
|
414
|
+
body.buyNowEnabled = true;
|
|
415
|
+
}
|
|
416
|
+
if (parsed.costPerGpuHour !== undefined)
|
|
417
|
+
body.costPerGpuHour = parsed.costPerGpuHour;
|
|
418
|
+
if (parsed.monthToMonth !== undefined)
|
|
419
|
+
body.monthToMonth = parsed.monthToMonth;
|
|
420
|
+
if (parsed.allowsDepositPlan !== undefined)
|
|
421
|
+
body.allowsDepositPlan = parsed.allowsDepositPlan;
|
|
422
|
+
if (parsed.downPaymentPct !== undefined)
|
|
423
|
+
body.downPaymentPct = parsed.downPaymentPct;
|
|
424
|
+
if (parsed.capacityNodes !== undefined)
|
|
425
|
+
body.capacityNodes = parsed.capacityNodes;
|
|
426
|
+
if (parsed.state !== undefined)
|
|
427
|
+
body.state = parsed.state;
|
|
428
|
+
return body;
|
|
429
|
+
}
|
|
430
|
+
export const gtmTaskActivityListInput = z.object({
|
|
431
|
+
taskId: z.number().int().positive(),
|
|
432
|
+
});
|
|
433
|
+
export const gtmTaskActivityListCapability = defineStaffMcpCapability({
|
|
434
|
+
id: "gtm.task.activity.list",
|
|
435
|
+
domain: "gtm",
|
|
436
|
+
roles: ["reviewer", "admin"],
|
|
437
|
+
description: "GTM staff only: read one ticket's timeline — the comments left on it and the field changes recorded against it, oldest first.",
|
|
438
|
+
mutation: "read",
|
|
439
|
+
http: { method: "GET", path: "/internal/v1/gtm/tasks/{taskId}/activity" },
|
|
440
|
+
input: gtmTaskActivityListInput,
|
|
441
|
+
execute: (ctx, input) => {
|
|
442
|
+
const parsed = gtmTaskActivityListInput.parse(input);
|
|
443
|
+
return ctx.fetch(`/internal/v1/gtm/tasks/${seg(String(parsed.taskId))}/activity`, {
|
|
444
|
+
method: "GET",
|
|
445
|
+
});
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
export const gtmTaskActivityWriteInput = z.object({
|
|
449
|
+
taskId: z.number().int().positive(),
|
|
450
|
+
body: z.string().min(1),
|
|
451
|
+
/** Set to reply to a comment on the same ticket. Threads are one level deep. */
|
|
452
|
+
parentId: z.number().int().positive().optional(),
|
|
453
|
+
confirm: z.boolean().optional(),
|
|
454
|
+
});
|
|
455
|
+
export const gtmTaskActivityWriteCapability = defineStaffMcpCapability({
|
|
456
|
+
id: "gtm.task.activity.write",
|
|
457
|
+
domain: "gtm",
|
|
458
|
+
roles: ["reviewer", "admin"],
|
|
459
|
+
description: "GTM staff only: add a plaintext comment to a ticket, or reply to one with parentId. Mention a teammate by writing @name in the body. Requires confirm: true.",
|
|
460
|
+
mutation: "preview-confirm",
|
|
461
|
+
http: { method: "POST", path: "/internal/v1/gtm/tasks/{taskId}/activity" },
|
|
462
|
+
input: gtmTaskActivityWriteInput,
|
|
463
|
+
execute: async (ctx, input) => {
|
|
464
|
+
const parsed = gtmTaskActivityWriteInput.parse(input);
|
|
465
|
+
const body = { body: parsed.body, parent_id: parsed.parentId ?? null };
|
|
466
|
+
if (!ctx.confirmed) {
|
|
467
|
+
return preview("gtm.task.activity.write", { taskId: parsed.taskId, ...body });
|
|
468
|
+
}
|
|
469
|
+
return ctx.fetch(`/internal/v1/gtm/tasks/${seg(String(parsed.taskId))}/activity`, {
|
|
470
|
+
method: "POST",
|
|
471
|
+
body,
|
|
472
|
+
});
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
/**
|
|
476
|
+
* Company cell comments. These hang off one field of one company — the
|
|
477
|
+
* discussion about a name, its owner tags, its summary, or its tickets —
|
|
478
|
+
* rather than off the company's main thread.
|
|
479
|
+
*/
|
|
480
|
+
const cellField = z.enum(["name", "owner_tags", "notes", "tickets"]);
|
|
481
|
+
export const gtmCellCommentListInput = z.object({
|
|
482
|
+
companyId: z.number().int().positive(),
|
|
483
|
+
field: cellField,
|
|
484
|
+
});
|
|
485
|
+
export const gtmCellCommentListCapability = defineStaffMcpCapability({
|
|
486
|
+
id: "gtm.cell.comment.list",
|
|
487
|
+
domain: "gtm",
|
|
488
|
+
roles: ["reviewer", "admin"],
|
|
489
|
+
description: "GTM staff only: read the comments attached to one field of one company (name, owner_tags, notes or tickets), replies included.",
|
|
490
|
+
mutation: "read",
|
|
491
|
+
http: { method: "GET", path: "/internal/v1/gtm/companies/{companyId}/cells/{field}/comments" },
|
|
492
|
+
input: gtmCellCommentListInput,
|
|
493
|
+
execute: (ctx, input) => {
|
|
494
|
+
const parsed = gtmCellCommentListInput.parse(input);
|
|
495
|
+
return ctx.fetch(`/internal/v1/gtm/companies/${seg(String(parsed.companyId))}/cells/${seg(parsed.field)}/comments`, { method: "GET" });
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
export const gtmCellCommentWriteInput = z.object({
|
|
499
|
+
companyId: z.number().int().positive().optional(),
|
|
500
|
+
field: cellField.optional(),
|
|
501
|
+
/** Set to replace an existing cell comment; companyId and field are then unused. */
|
|
502
|
+
commentId: z.number().int().positive().optional(),
|
|
503
|
+
parentId: z.number().int().positive().optional(),
|
|
504
|
+
body: z.string().min(1),
|
|
505
|
+
confirm: z.boolean().optional(),
|
|
506
|
+
});
|
|
507
|
+
export const gtmCellCommentWriteCapability = defineStaffMcpCapability({
|
|
508
|
+
id: "gtm.cell.comment.write",
|
|
509
|
+
domain: "gtm",
|
|
510
|
+
roles: ["reviewer", "admin"],
|
|
511
|
+
description: "GTM staff only: comment on one field of one company, reply to such a comment with parentId, or replace one when commentId is supplied. Requires confirm: true.",
|
|
512
|
+
mutation: "preview-confirm",
|
|
513
|
+
http: { method: "POST", path: "/internal/v1/gtm/companies/{companyId}/cells/{field}/comments" },
|
|
514
|
+
input: gtmCellCommentWriteInput,
|
|
515
|
+
execute: async (ctx, input) => {
|
|
516
|
+
const parsed = gtmCellCommentWriteInput.parse(input);
|
|
517
|
+
if (!parsed.commentId && (!parsed.companyId || !parsed.field)) {
|
|
518
|
+
throw new Error("companyId and field are required unless commentId is supplied");
|
|
519
|
+
}
|
|
520
|
+
if (!ctx.confirmed) {
|
|
521
|
+
return preview("gtm.cell.comment.write", {
|
|
522
|
+
companyId: parsed.companyId,
|
|
523
|
+
field: parsed.field,
|
|
524
|
+
commentId: parsed.commentId,
|
|
525
|
+
parentId: parsed.parentId,
|
|
526
|
+
body: parsed.body,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
if (parsed.commentId) {
|
|
530
|
+
return ctx.fetch(`/internal/v1/gtm/company-comments/${seg(String(parsed.commentId))}`, {
|
|
531
|
+
method: "PATCH",
|
|
532
|
+
body: { body: parsed.body },
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
return ctx.fetch(`/internal/v1/gtm/companies/${seg(String(parsed.companyId))}/cells/${seg(parsed.field)}/comments`, { method: "POST", body: { body: parsed.body, parent_id: parsed.parentId ?? null } });
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
const cellCommentDelete = gtmDeleteCapability("cell.comment", "commentId", "/internal/v1/gtm/company-comments", "Removes that field comment and its replies.");
|
|
539
|
+
export const gtmCellCommentDeleteInput = cellCommentDelete.input;
|
|
540
|
+
export const gtmCellCommentDeleteCapability = cellCommentDelete.capability;
|
|
541
|
+
/**
|
|
542
|
+
* Bulk owner-tag reassignment. This is the one GTM edit that is genuinely
|
|
543
|
+
* N-at-a-time — a book of companies moving to a new owner — and doing it one
|
|
544
|
+
* confirmation at a time is both slow and easy to half-finish. Every other
|
|
545
|
+
* bulk case is a loop over the single-record verbs.
|
|
546
|
+
*/
|
|
547
|
+
export const gtmCompanyBulkRetagInput = z.object({
|
|
548
|
+
companyIds: z.array(z.number().int().positive()).min(1).max(200),
|
|
549
|
+
ownerTags: z.string(),
|
|
550
|
+
confirm: z.boolean().optional(),
|
|
551
|
+
});
|
|
552
|
+
export const gtmCompanyBulkRetagCapability = defineStaffMcpCapability({
|
|
553
|
+
id: "gtm.company.bulk_retag",
|
|
554
|
+
domain: "gtm",
|
|
555
|
+
roles: ["reviewer", "admin"],
|
|
556
|
+
description: "GTM staff only: set the same owner tags on many companies at once, for moving a book to a new owner. Names, notes and comments are untouched. The preview lists every company it would change. Requires confirm: true.",
|
|
557
|
+
mutation: "preview-confirm",
|
|
558
|
+
http: { method: "PATCH", path: "/internal/v1/gtm/companies/{companyId}" },
|
|
559
|
+
input: gtmCompanyBulkRetagInput,
|
|
560
|
+
execute: async (ctx, input) => {
|
|
561
|
+
const parsed = gtmCompanyBulkRetagInput.parse(input);
|
|
562
|
+
const ids = [...new Set(parsed.companyIds)];
|
|
563
|
+
if (!ctx.confirmed) {
|
|
564
|
+
return preview("gtm.company.bulk_retag", { companyIds: ids, ownerTags: parsed.ownerTags });
|
|
565
|
+
}
|
|
566
|
+
// PATCH replaces the company's name as well, so the current names are read
|
|
567
|
+
// once up front rather than per row. Omitting notes preserves them.
|
|
568
|
+
// Commerce owns no bulk route, so partial progress is reported rather than
|
|
569
|
+
// rolled back; re-running is safe.
|
|
570
|
+
const all = (await ctx.fetch("/internal/v1/gtm/companies", { method: "GET" }));
|
|
571
|
+
const names = new Map(all.map((company) => [company.id, company.name]));
|
|
572
|
+
const updated = [];
|
|
573
|
+
const failed = [];
|
|
574
|
+
for (const companyId of ids) {
|
|
575
|
+
const name = names.get(companyId);
|
|
576
|
+
if (name === undefined) {
|
|
577
|
+
failed.push({ companyId, error: "no such company" });
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
try {
|
|
581
|
+
updated.push(await ctx.fetch(`/internal/v1/gtm/companies/${seg(String(companyId))}`, {
|
|
582
|
+
method: "PATCH",
|
|
583
|
+
body: { name, owner_tags: parsed.ownerTags },
|
|
584
|
+
}));
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
failed.push({ companyId, error: error instanceof Error ? error.message : String(error) });
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return { updated, failed };
|
|
591
|
+
},
|
|
592
|
+
});
|
|
593
|
+
export const staffGTMCapabilities = [
|
|
594
|
+
gtmCompanyListCapability,
|
|
595
|
+
gtmCompanyWriteCapability,
|
|
596
|
+
gtmCompanyDeleteCapability,
|
|
597
|
+
gtmCompanyBulkRetagCapability,
|
|
598
|
+
gtmDealListCapability,
|
|
599
|
+
gtmDealWriteCapability,
|
|
600
|
+
gtmDealDeleteCapability,
|
|
601
|
+
gtmCommentListCapability,
|
|
602
|
+
gtmCommentWriteCapability,
|
|
603
|
+
gtmCommentDeleteCapability,
|
|
604
|
+
gtmCellCommentListCapability,
|
|
605
|
+
gtmCellCommentWriteCapability,
|
|
606
|
+
gtmCellCommentDeleteCapability,
|
|
607
|
+
gtmTaskListCapability,
|
|
608
|
+
gtmTaskWriteCapability,
|
|
609
|
+
gtmTaskDeleteCapability,
|
|
610
|
+
gtmTaskActivityListCapability,
|
|
611
|
+
gtmTaskActivityWriteCapability,
|
|
612
|
+
gtmListingListCapability,
|
|
613
|
+
gtmListingUpdateCapability,
|
|
614
|
+
gtmMentionDismissCapability,
|
|
615
|
+
gtmWorkspaceShowCapability,
|
|
616
|
+
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ZodType } from "zod";
|
|
2
2
|
import type { ClientKind, Role } from "./role.ts";
|
|
3
|
-
export declare const DOMAINS: readonly ["identity", "listings", "fleet", "users", "reservations", "bids", "access", "clusters", "slurm", "kubernetes", "networks", "storage", "vpn", "billing", "observability"];
|
|
3
|
+
export declare const DOMAINS: readonly ["identity", "listings", "fleet", "users", "reservations", "bids", "access", "clusters", "slurm", "kubernetes", "networks", "storage", "vpn", "billing", "observability", "gtm"];
|
|
4
4
|
export type Domain = (typeof DOMAINS)[number];
|
|
5
5
|
export type Mutation = "read" | "preview-confirm" | "local";
|
|
6
6
|
export type HttpSpec = {
|
|
@@ -7,6 +7,7 @@ import { observabilityCapabilities } from "./capabilities/observability.js";
|
|
|
7
7
|
import { staffAccessCapabilities } from "./capabilities/staff-access.js";
|
|
8
8
|
import { staffCommerceCapabilities } from "./capabilities/staff-commerce.js";
|
|
9
9
|
import { staffFleetCapabilities } from "./capabilities/staff-fleet.js";
|
|
10
|
+
import { staffGTMCapabilities } from "./capabilities/staff-gtm.js";
|
|
10
11
|
import { userCapabilities } from "./capabilities/users.js";
|
|
11
12
|
/** System of record for CLI, MCP, and Slack verbs. Add capabilities here. */
|
|
12
13
|
export const catalog = [
|
|
@@ -19,6 +20,7 @@ export const catalog = [
|
|
|
19
20
|
...slurmCapabilities,
|
|
20
21
|
...kubernetesCapabilities,
|
|
21
22
|
...staffFleetCapabilities,
|
|
23
|
+
...staffGTMCapabilities,
|
|
22
24
|
...staffCommerceCapabilities,
|
|
23
25
|
...staffAccessCapabilities,
|
|
24
26
|
];
|
|
@@ -3,7 +3,7 @@ export { namesFromId } from "./names.ts";
|
|
|
3
3
|
export { catalog } from "./catalog.ts";
|
|
4
4
|
export { fleetCapabilities, listFacilitiesCapability, listNodesCapability, listOperatorsCapability, nodeExecCapability, nodeOffGridCapability, nodeOnGridCapability, nodeTerminateCapability, } from "./capabilities/fleet.ts";
|
|
5
5
|
export { buildEnrollmentInstallCommand, tokenCapabilities, tokensCreateCapability, tokensListCapability, } from "./capabilities/tokens.ts";
|
|
6
|
-
export { identityCapabilities, statusCapability, whoamiCapability } from "./capabilities/identity.ts";
|
|
6
|
+
export { identityCapabilities, statusCapability, whoamiCapability, } from "./capabilities/identity.ts";
|
|
7
7
|
export { createListingCapability, forecastListingsCapability, listingCreateInput, listCatalogTermsCapability, listingCapabilities, listListingsCapability, proposeListingCapability, } from "./capabilities/listings.ts";
|
|
8
8
|
export { userCapabilities, usersQueryCapability } from "./capabilities/users.ts";
|
|
9
9
|
export { kubernetesCapabilities, slurmCapabilities } from "./capabilities/clusters.ts";
|
|
@@ -11,5 +11,6 @@ export { logsLatestCapability, logsTailCapability, observabilityCapabilities, te
|
|
|
11
11
|
export { enrollmentTokenRevokeCapability, enrollmentTokenRevokeInput, facilityCreateCapability, facilityCreateInput, facilityUpdateCapability, facilityUpdateInput, nodeAdminKeyCapability, nodeAdminKeyInput, nodeBenchmarkCapability, nodeBenchmarkInput, nodeDeployCapability, nodeDeployInput, nodeDetailsUpdateCapability, nodeDetailsUpdateInput, nodeHardResetCapability, nodeHardResetInput, nodeOperatorTransferCapability, nodeOperatorTransferInput, nodeRebootCapability, nodeRebootInput, nodeShowCapability, nodeShowInput, nodeTestResultCapability, nodeTestResultInput, nodeTestTriggerCapability, nodeTestTriggerInput, nodeUpdateCapability, nodeUpdateInput, operatorCreateCapability, operatorCreateInput, operatorDeleteCapability, operatorDeleteInput, operatorUpdateCapability, operatorUpdateInput, staffFleetCapabilities, } from "./capabilities/staff-fleet.ts";
|
|
12
12
|
export { bidShowCapability, bidShowInput, bidAcceptCapability, bidAcceptInput, bidRejectCapability, bidRejectInput, billingInvoicesListCapability, billingInvoicesListInput, inventoryDelistCapability, inventoryDelistInput, inventoryUpdateCapability, inventoryUpdateInput, reservationActivateCapability, reservationActivateInput, reservationCancelCapability, reservationCancelInput, reservationCompleteCapability, reservationCompleteInput, reservationDraftCreateCapability, reservationDraftCreateInput, reservationListCapability, reservationListInput, reservationMachinesListCapability, reservationMachinesListInput, reservationPublishCapability, reservationPublishInput, reservationShowCapability, reservationShowInput, reservationSshKeyAddCapability, reservationSshKeyAddInput, reservationSshKeyRemoveCapability, reservationSshKeyRemoveInput, reservationTransferCapability, reservationTransferInput, reservationUpdateCapability, reservationUpdateInput, staffCommerceCapabilities, tenantCreditProfileShowCapability, tenantCreditProfileShowInput, } from "./capabilities/staff-commerce.ts";
|
|
13
13
|
export { sshKeyAddCapability, sshKeyAddInput, sshKeyPushCapability, sshKeyPushInput, sshKeyRevokeCapability, sshKeyRevokeInput, staffAccessCapabilities, } from "./capabilities/staff-access.ts";
|
|
14
|
+
export { gtmCommentDeleteCapability, gtmCommentDeleteInput, gtmCommentListCapability, gtmCommentListInput, gtmCommentWriteCapability, gtmCommentWriteInput, gtmCompanyDeleteCapability, gtmCompanyDeleteInput, gtmCompanyListCapability, gtmCompanyListInput, gtmCompanyWriteCapability, gtmCompanyWriteInput, gtmCompanyBulkRetagCapability, gtmCompanyBulkRetagInput, gtmCellCommentDeleteCapability, gtmCellCommentDeleteInput, gtmCellCommentListCapability, gtmCellCommentListInput, gtmCellCommentWriteCapability, gtmCellCommentWriteInput, gtmDealDeleteCapability, gtmDealDeleteInput, gtmDealListCapability, gtmDealListInput, gtmMentionDismissCapability, gtmMentionDismissInput, gtmListingListCapability, gtmListingListInput, gtmListingUpdateCapability, gtmListingUpdateInput, gtmDealWriteCapability, gtmDealWriteInput, gtmTaskDeleteCapability, gtmTaskDeleteInput, gtmTaskListCapability, gtmTaskListInput, gtmTaskWriteCapability, gtmTaskWriteInput, gtmTaskActivityListCapability, gtmTaskActivityListInput, gtmTaskActivityWriteCapability, gtmTaskActivityWriteInput, gtmWorkspaceShowCapability, gtmWorkspaceShowInput, staffGTMCapabilities, } from "./capabilities/staff-gtm.ts";
|
|
14
15
|
export { CLIENTS, parseRole, roleAtLeast, ROLES, type ClientKind, type Role } from "./role.ts";
|
|
15
16
|
export { bindHardwareFields, HARDWARE_CATALOG_PATH, HARDWARE_KINDS, HARDWARE_LISTING_FIELDS, HARDWARE_LISTING_FILTERS, loadHardwareCatalog, looksLikeSpecDump, normalizeCatalogQuery, parseHardwareCatalog, projectCatalogTerms, proposeListingPayload, resolveCatalogTerm, UNRESOLVED_CATALOG_HINT, type CatalogBindResult, type CatalogTerm, type HardwareCatalog, type HardwareKind, type ResolvedCatalogTerm, } from "./spec-catalog.ts";
|
|
@@ -3,7 +3,7 @@ export { namesFromId } from "./names.js";
|
|
|
3
3
|
export { catalog } from "./catalog.js";
|
|
4
4
|
export { fleetCapabilities, listFacilitiesCapability, listNodesCapability, listOperatorsCapability, nodeExecCapability, nodeOffGridCapability, nodeOnGridCapability, nodeTerminateCapability, } from "./capabilities/fleet.js";
|
|
5
5
|
export { buildEnrollmentInstallCommand, tokenCapabilities, tokensCreateCapability, tokensListCapability, } from "./capabilities/tokens.js";
|
|
6
|
-
export { identityCapabilities, statusCapability, whoamiCapability } from "./capabilities/identity.js";
|
|
6
|
+
export { identityCapabilities, statusCapability, whoamiCapability, } from "./capabilities/identity.js";
|
|
7
7
|
export { createListingCapability, forecastListingsCapability, listingCreateInput, listCatalogTermsCapability, listingCapabilities, listListingsCapability, proposeListingCapability, } from "./capabilities/listings.js";
|
|
8
8
|
export { userCapabilities, usersQueryCapability } from "./capabilities/users.js";
|
|
9
9
|
export { kubernetesCapabilities, slurmCapabilities } from "./capabilities/clusters.js";
|
|
@@ -11,5 +11,6 @@ export { logsLatestCapability, logsTailCapability, observabilityCapabilities, te
|
|
|
11
11
|
export { enrollmentTokenRevokeCapability, enrollmentTokenRevokeInput, facilityCreateCapability, facilityCreateInput, facilityUpdateCapability, facilityUpdateInput, nodeAdminKeyCapability, nodeAdminKeyInput, nodeBenchmarkCapability, nodeBenchmarkInput, nodeDeployCapability, nodeDeployInput, nodeDetailsUpdateCapability, nodeDetailsUpdateInput, nodeHardResetCapability, nodeHardResetInput, nodeOperatorTransferCapability, nodeOperatorTransferInput, nodeRebootCapability, nodeRebootInput, nodeShowCapability, nodeShowInput, nodeTestResultCapability, nodeTestResultInput, nodeTestTriggerCapability, nodeTestTriggerInput, nodeUpdateCapability, nodeUpdateInput, operatorCreateCapability, operatorCreateInput, operatorDeleteCapability, operatorDeleteInput, operatorUpdateCapability, operatorUpdateInput, staffFleetCapabilities, } from "./capabilities/staff-fleet.js";
|
|
12
12
|
export { bidShowCapability, bidShowInput, bidAcceptCapability, bidAcceptInput, bidRejectCapability, bidRejectInput, billingInvoicesListCapability, billingInvoicesListInput, inventoryDelistCapability, inventoryDelistInput, inventoryUpdateCapability, inventoryUpdateInput, reservationActivateCapability, reservationActivateInput, reservationCancelCapability, reservationCancelInput, reservationCompleteCapability, reservationCompleteInput, reservationDraftCreateCapability, reservationDraftCreateInput, reservationListCapability, reservationListInput, reservationMachinesListCapability, reservationMachinesListInput, reservationPublishCapability, reservationPublishInput, reservationShowCapability, reservationShowInput, reservationSshKeyAddCapability, reservationSshKeyAddInput, reservationSshKeyRemoveCapability, reservationSshKeyRemoveInput, reservationTransferCapability, reservationTransferInput, reservationUpdateCapability, reservationUpdateInput, staffCommerceCapabilities, tenantCreditProfileShowCapability, tenantCreditProfileShowInput, } from "./capabilities/staff-commerce.js";
|
|
13
13
|
export { sshKeyAddCapability, sshKeyAddInput, sshKeyPushCapability, sshKeyPushInput, sshKeyRevokeCapability, sshKeyRevokeInput, staffAccessCapabilities, } from "./capabilities/staff-access.js";
|
|
14
|
+
export { gtmCommentDeleteCapability, gtmCommentDeleteInput, gtmCommentListCapability, gtmCommentListInput, gtmCommentWriteCapability, gtmCommentWriteInput, gtmCompanyDeleteCapability, gtmCompanyDeleteInput, gtmCompanyListCapability, gtmCompanyListInput, gtmCompanyWriteCapability, gtmCompanyWriteInput, gtmCompanyBulkRetagCapability, gtmCompanyBulkRetagInput, gtmCellCommentDeleteCapability, gtmCellCommentDeleteInput, gtmCellCommentListCapability, gtmCellCommentListInput, gtmCellCommentWriteCapability, gtmCellCommentWriteInput, gtmDealDeleteCapability, gtmDealDeleteInput, gtmDealListCapability, gtmDealListInput, gtmMentionDismissCapability, gtmMentionDismissInput, gtmListingListCapability, gtmListingListInput, gtmListingUpdateCapability, gtmListingUpdateInput, gtmDealWriteCapability, gtmDealWriteInput, gtmTaskDeleteCapability, gtmTaskDeleteInput, gtmTaskListCapability, gtmTaskListInput, gtmTaskWriteCapability, gtmTaskWriteInput, gtmTaskActivityListCapability, gtmTaskActivityListInput, gtmTaskActivityWriteCapability, gtmTaskActivityWriteInput, gtmWorkspaceShowCapability, gtmWorkspaceShowInput, staffGTMCapabilities, } from "./capabilities/staff-gtm.js";
|
|
14
15
|
export { CLIENTS, parseRole, roleAtLeast, ROLES } from "./role.js";
|
|
15
16
|
export { bindHardwareFields, HARDWARE_CATALOG_PATH, HARDWARE_KINDS, HARDWARE_LISTING_FIELDS, HARDWARE_LISTING_FILTERS, loadHardwareCatalog, looksLikeSpecDump, normalizeCatalogQuery, parseHardwareCatalog, projectCatalogTerms, proposeListingPayload, resolveCatalogTerm, UNRESOLVED_CATALOG_HINT, } from "./spec-catalog.js";
|