@gravity-rail/cli 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/chunk-424PT5DM.js +23 -0
- package/dist/chunk-424PT5DM.js.map +1 -0
- package/dist/{chunk-C6D4FO6A.js → chunk-H62EC2CF.js} +66 -24
- package/dist/{chunk-C6D4FO6A.js.map → chunk-H62EC2CF.js.map} +1 -1
- package/dist/chunk-OPWCTOHP.js +49 -0
- package/dist/chunk-OPWCTOHP.js.map +1 -0
- package/dist/chunk-XRBVAFCB.js +47 -0
- package/dist/chunk-XRBVAFCB.js.map +1 -0
- package/dist/{auth-DBspA1Dh.d.ts → cli-client-context-9KRUt6O-.d.ts} +13 -1
- package/dist/cli-utils.d.ts +26 -5
- package/dist/cli-utils.js +2 -1
- package/dist/errors-4MAJGD3C.js +12 -0
- package/dist/errors-4MAJGD3C.js.map +1 -0
- package/dist/errors-DJILV5RW.js +4 -0
- package/dist/errors-DJILV5RW.js.map +1 -0
- package/dist/gr.js +2218 -351
- package/dist/gr.js.map +1 -1
- package/dist/lib.d.ts +25 -3
- package/dist/lib.js +1472 -258
- package/dist/lib.js.map +1 -1
- package/package.json +3 -3
package/dist/lib.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGUIClient,
|
|
3
3
|
CredentialsManager,
|
|
4
|
-
__export,
|
|
5
|
-
__reExport,
|
|
6
4
|
clearScreen,
|
|
7
5
|
formatTimestamp,
|
|
8
6
|
getEnvironmentConfig,
|
|
@@ -13,48 +11,16 @@ import {
|
|
|
13
11
|
printTable,
|
|
14
12
|
refreshAccessToken,
|
|
15
13
|
selectFromList
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
26
|
-
var AuthRequiredError = class extends CLIError {
|
|
27
|
-
constructor(message = "Not authenticated. Run `gr login` or set GRAVITY_RAIL_API_KEY.") {
|
|
28
|
-
super(message, 2);
|
|
29
|
-
this.name = "AuthRequiredError";
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
function handleError(err) {
|
|
33
|
-
if (err instanceof CLIError) {
|
|
34
|
-
process.stderr.write(`Error: ${err.message}
|
|
35
|
-
`);
|
|
36
|
-
process.exit(err.exitCode);
|
|
37
|
-
}
|
|
38
|
-
if (err instanceof Error) {
|
|
39
|
-
process.stderr.write(`Error: ${err.message}
|
|
40
|
-
`);
|
|
41
|
-
if ("errors" in err && Array.isArray(err.errors)) {
|
|
42
|
-
for (const e of err.errors) {
|
|
43
|
-
process.stderr.write(` - ${e}
|
|
44
|
-
`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if ("statusCode" in err && err.statusCode === 401) {
|
|
48
|
-
process.stderr.write(`
|
|
49
|
-
Hint: Run "yarn gr login" or use "--realm <name>" (dev only)
|
|
50
|
-
`);
|
|
51
|
-
}
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
process.stderr.write(`Error: ${String(err)}
|
|
55
|
-
`);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
14
|
+
} from "./chunk-H62EC2CF.js";
|
|
15
|
+
import {
|
|
16
|
+
AuthRequiredError,
|
|
17
|
+
CLIError,
|
|
18
|
+
handleError
|
|
19
|
+
} from "./chunk-OPWCTOHP.js";
|
|
20
|
+
import {
|
|
21
|
+
__export,
|
|
22
|
+
__reExport
|
|
23
|
+
} from "./chunk-424PT5DM.js";
|
|
58
24
|
|
|
59
25
|
// src/lib/pagination.ts
|
|
60
26
|
function isListResult(data) {
|
|
@@ -204,7 +170,7 @@ var COMMON_ARGS = [
|
|
|
204
170
|
{ name: "uuid", description: "Entity UUID" }
|
|
205
171
|
];
|
|
206
172
|
function parseArgs(defs, argv) {
|
|
207
|
-
const allDefs = [...
|
|
173
|
+
const allDefs = [...defs, ...COMMON_ARGS];
|
|
208
174
|
const result = {};
|
|
209
175
|
for (const def of allDefs) {
|
|
210
176
|
if (def.default !== void 0) {
|
|
@@ -231,8 +197,7 @@ function parseArgs(defs, argv) {
|
|
|
231
197
|
const def = findDef(allDefs, flagPart);
|
|
232
198
|
if (!def) {
|
|
233
199
|
if ((flagPart.startsWith("--") || flagPart.startsWith("-")) && !KNOWN_GLOBAL_FLAGS.has(flagPart)) {
|
|
234
|
-
|
|
235
|
-
`);
|
|
200
|
+
throw new CLIError(`Unknown flag "${flagPart}"`);
|
|
236
201
|
}
|
|
237
202
|
continue;
|
|
238
203
|
}
|
|
@@ -247,9 +212,11 @@ function parseArgs(defs, argv) {
|
|
|
247
212
|
}
|
|
248
213
|
}
|
|
249
214
|
for (const def of allDefs) {
|
|
250
|
-
if (def.required
|
|
251
|
-
|
|
252
|
-
|
|
215
|
+
if (!def.required) continue;
|
|
216
|
+
if (result[def.name] !== void 0) continue;
|
|
217
|
+
const aliasNames = [def.alias, ...def.aliases ?? []].filter(Boolean);
|
|
218
|
+
if (aliasNames.some((a) => result[a] !== void 0)) continue;
|
|
219
|
+
throw new CLIError(`Missing required argument: --${def.name}`);
|
|
253
220
|
}
|
|
254
221
|
return result;
|
|
255
222
|
}
|
|
@@ -277,7 +244,67 @@ __reExport(dist_exports, internal_star);
|
|
|
277
244
|
import * as internal_star from "@gravity-rail/sdk/internal";
|
|
278
245
|
import { BaseApiClient } from "@gravity-rail/sdk/internal";
|
|
279
246
|
import { GravityRailClient } from "@gravity-rail/sdk";
|
|
247
|
+
function supportRequestParams(params) {
|
|
248
|
+
const searchParams = new URLSearchParams();
|
|
249
|
+
if (params?.page) searchParams.append("page", params.page.toString());
|
|
250
|
+
if (params?.pageSize) searchParams.append("pageSize", params.pageSize.toString());
|
|
251
|
+
if (params?.status) searchParams.append("status", params.status);
|
|
252
|
+
if (params?.supportRequestType) {
|
|
253
|
+
searchParams.append("supportRequestType", params.supportRequestType);
|
|
254
|
+
}
|
|
255
|
+
if (params?.severity) searchParams.append("severity", params.severity);
|
|
256
|
+
if (params?.source) searchParams.append("source", params.source);
|
|
257
|
+
if (params?.organizationId !== void 0) {
|
|
258
|
+
searchParams.append("organizationId", params.organizationId.toString());
|
|
259
|
+
}
|
|
260
|
+
if (params?.workspaceId !== void 0) {
|
|
261
|
+
searchParams.append("workspaceId", params.workspaceId.toString());
|
|
262
|
+
}
|
|
263
|
+
if (params?.linearLinked !== void 0) {
|
|
264
|
+
searchParams.append("linearLinked", params.linearLinked.toString());
|
|
265
|
+
}
|
|
266
|
+
const query = searchParams.toString();
|
|
267
|
+
return query ? `?${query}` : "";
|
|
268
|
+
}
|
|
280
269
|
var AdminApi = class extends BaseApiClient {
|
|
270
|
+
// Support Requests API
|
|
271
|
+
async listSupportRequestsAsAdmin(params) {
|
|
272
|
+
return this.fetch(
|
|
273
|
+
`admin/support-requests${supportRequestParams(params)}`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
async getSupportRequestAsAdmin(uuid) {
|
|
277
|
+
return this.fetch(`admin/support-requests/${uuid}`);
|
|
278
|
+
}
|
|
279
|
+
async commentOnSupportRequestAsAdmin(uuid, data) {
|
|
280
|
+
return this.fetch(`admin/support-requests/${uuid}/comments`, {
|
|
281
|
+
method: "POST",
|
|
282
|
+
body: JSON.stringify(data)
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
async noteOnSupportRequestAsAdmin(uuid, data) {
|
|
286
|
+
return this.fetch(`admin/support-requests/${uuid}/notes`, {
|
|
287
|
+
method: "POST",
|
|
288
|
+
body: JSON.stringify(data)
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
async updateSupportRequestStatusAsAdmin(uuid, data) {
|
|
292
|
+
return this.fetch(`admin/support-requests/${uuid}/status`, {
|
|
293
|
+
method: "POST",
|
|
294
|
+
body: JSON.stringify(data)
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async updateSupportRequestAttributesAsAdmin(uuid, data) {
|
|
298
|
+
return this.fetch(`admin/support-requests/${uuid}/attributes`, {
|
|
299
|
+
method: "POST",
|
|
300
|
+
body: JSON.stringify(data)
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
async createSupportRequestLinearIssueAsAdmin(uuid) {
|
|
304
|
+
return this.fetch(`admin/support-requests/${uuid}/linear-issue`, {
|
|
305
|
+
method: "POST"
|
|
306
|
+
});
|
|
307
|
+
}
|
|
281
308
|
// Admin Accounts API
|
|
282
309
|
async getAccounts(options) {
|
|
283
310
|
const params = new URLSearchParams();
|
|
@@ -416,11 +443,11 @@ var AdminApi = class extends BaseApiClient {
|
|
|
416
443
|
return this.fetch(`o/${idOrUuid}`);
|
|
417
444
|
}
|
|
418
445
|
/**
|
|
419
|
-
* Get organization by numeric ID via admin endpoint.
|
|
446
|
+
* Get organization by numeric ID or UUID via admin endpoint.
|
|
420
447
|
* Does NOT require organization membership — superuser-only.
|
|
421
448
|
*/
|
|
422
|
-
async getOrganizationAsAdmin(
|
|
423
|
-
return this.fetch(`admin/organizations/${
|
|
449
|
+
async getOrganizationAsAdmin(idOrUuid) {
|
|
450
|
+
return this.fetch(`admin/organizations/${idOrUuid}`);
|
|
424
451
|
}
|
|
425
452
|
async getOrganizationMembersAsAdmin(organizationId, options) {
|
|
426
453
|
const params = new URLSearchParams();
|
|
@@ -597,6 +624,26 @@ var AdminApi = class extends BaseApiClient {
|
|
|
597
624
|
body: JSON.stringify(request || {})
|
|
598
625
|
});
|
|
599
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Switch a workspace's plan product on its active SubscriptionItem.
|
|
629
|
+
*
|
|
630
|
+
* Superuser only. Re-syncs ``workspace.features`` from the new product
|
|
631
|
+
* and emits a ``WORKSPACE_PRODUCT_CHANGED`` audit event identifying the
|
|
632
|
+
* actor, target workspace, prior and new product, and feature diff.
|
|
633
|
+
*
|
|
634
|
+
* Stripe is not touched here; price-id changes belong to the dedicated
|
|
635
|
+
* Stripe sync surface (``syncProductToStripe``).
|
|
636
|
+
*/
|
|
637
|
+
async changeWorkspaceProduct(workspaceUuid, productId) {
|
|
638
|
+
const body = { productId };
|
|
639
|
+
return this.fetch(
|
|
640
|
+
`admin/workspaces/${workspaceUuid}/product`,
|
|
641
|
+
{
|
|
642
|
+
method: "POST",
|
|
643
|
+
body: JSON.stringify(body)
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
}
|
|
600
647
|
// ===== Workflow Access Validation =====
|
|
601
648
|
async validateWorkflowAccessAsAdmin(workspaceUuid) {
|
|
602
649
|
const params = new URLSearchParams();
|
|
@@ -629,6 +676,11 @@ var AdminApi = class extends BaseApiClient {
|
|
|
629
676
|
body: JSON.stringify(data)
|
|
630
677
|
});
|
|
631
678
|
}
|
|
679
|
+
async unarchiveAdminWorkspaceMember(wid) {
|
|
680
|
+
return this.fetch(`admin/workspaces/${wid}/me/unarchive`, {
|
|
681
|
+
method: "POST"
|
|
682
|
+
});
|
|
683
|
+
}
|
|
632
684
|
async getAdminWorkspaceMemberRoles(wid) {
|
|
633
685
|
return this.fetch(`admin/workspaces/${wid}/member-roles`);
|
|
634
686
|
}
|
|
@@ -640,8 +692,8 @@ var AdminApi = class extends BaseApiClient {
|
|
|
640
692
|
`admin/phone-numbers/workspace/${workspaceId}/unmapped`
|
|
641
693
|
);
|
|
642
694
|
}
|
|
643
|
-
async getWorkspacePhoneNumbersAsAdmin(
|
|
644
|
-
return this.fetch(`admin/
|
|
695
|
+
async getWorkspacePhoneNumbersAsAdmin(workspaceRef) {
|
|
696
|
+
return this.fetch(`admin/workspaces/${workspaceRef}/phone-numbers`);
|
|
645
697
|
}
|
|
646
698
|
// Phone Numbers API (Provider phone numbers from Twilio, AWS, etc.)
|
|
647
699
|
async getProviderPhoneNumbers(options = {}) {
|
|
@@ -750,6 +802,11 @@ var AdminApi = class extends BaseApiClient {
|
|
|
750
802
|
method: "DELETE"
|
|
751
803
|
});
|
|
752
804
|
}
|
|
805
|
+
async getWorkspaceInboxesAsAdmin(workspaceUuid) {
|
|
806
|
+
return this.fetch(
|
|
807
|
+
`admin/workspaces/${workspaceUuid}/inboxes`
|
|
808
|
+
);
|
|
809
|
+
}
|
|
753
810
|
// Admin Inbox Mappings API
|
|
754
811
|
async getInboxMappings(options = {}) {
|
|
755
812
|
const params = new URLSearchParams();
|
|
@@ -794,21 +851,30 @@ var AdminApi = class extends BaseApiClient {
|
|
|
794
851
|
})
|
|
795
852
|
});
|
|
796
853
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
854
|
+
/**
|
|
855
|
+
* Resolve a Langfuse trace UI URL from a trace ID, chat ID, or message ID.
|
|
856
|
+
*
|
|
857
|
+
* URL-only: never fetches Langfuse payloads, Message content, or
|
|
858
|
+
* tool arguments. Exactly one of ``traceId``, ``chatId``, or
|
|
859
|
+
* ``messageId`` must be set; chat/message forms require a
|
|
860
|
+
* workspaceUuid. See
|
|
861
|
+
* docs/development/observability/llm-call-investigation.md.
|
|
862
|
+
*/
|
|
863
|
+
async getTraceUrl(options) {
|
|
864
|
+
return this.fetch("admin/trace/url", {
|
|
805
865
|
method: "POST",
|
|
806
866
|
body: JSON.stringify({
|
|
807
|
-
|
|
808
|
-
|
|
867
|
+
trace_id: options.traceId,
|
|
868
|
+
chat_id: options.chatId,
|
|
869
|
+
message_id: options.messageId,
|
|
870
|
+
workspace_uuid: options.workspaceUuid
|
|
809
871
|
})
|
|
810
872
|
});
|
|
811
873
|
}
|
|
874
|
+
// Admin Themes API
|
|
875
|
+
async listAllThemes() {
|
|
876
|
+
return this.fetch("admin/themes/list");
|
|
877
|
+
}
|
|
812
878
|
// Missing Memberships API
|
|
813
879
|
async getMissingMemberships(workspaceId) {
|
|
814
880
|
return this.fetch(
|
|
@@ -1381,6 +1447,13 @@ function appendTemporalWorkspaceParams(params, options) {
|
|
|
1381
1447
|
push(options.workspaceUuid);
|
|
1382
1448
|
}
|
|
1383
1449
|
function bindAdminMethods(target, admin) {
|
|
1450
|
+
target.listSupportRequestsAsAdmin = admin.listSupportRequestsAsAdmin.bind(admin);
|
|
1451
|
+
target.getSupportRequestAsAdmin = admin.getSupportRequestAsAdmin.bind(admin);
|
|
1452
|
+
target.commentOnSupportRequestAsAdmin = admin.commentOnSupportRequestAsAdmin.bind(admin);
|
|
1453
|
+
target.noteOnSupportRequestAsAdmin = admin.noteOnSupportRequestAsAdmin.bind(admin);
|
|
1454
|
+
target.updateSupportRequestStatusAsAdmin = admin.updateSupportRequestStatusAsAdmin.bind(admin);
|
|
1455
|
+
target.updateSupportRequestAttributesAsAdmin = admin.updateSupportRequestAttributesAsAdmin.bind(admin);
|
|
1456
|
+
target.createSupportRequestLinearIssueAsAdmin = admin.createSupportRequestLinearIssueAsAdmin.bind(admin);
|
|
1384
1457
|
target.getAccounts = admin.getAccounts.bind(admin);
|
|
1385
1458
|
target.getAccountDetails = admin.getAccountDetails.bind(admin);
|
|
1386
1459
|
target.getAccountByUuid = admin.getAccountByUuid.bind(admin);
|
|
@@ -1429,11 +1502,13 @@ function bindAdminMethods(target, admin) {
|
|
|
1429
1502
|
target.disableMaintenanceMode = admin.disableMaintenanceMode.bind(admin);
|
|
1430
1503
|
target.getWorkspaceHealth = admin.getWorkspaceHealth.bind(admin);
|
|
1431
1504
|
target.provisionPlanFeatures = admin.provisionPlanFeatures.bind(admin);
|
|
1505
|
+
target.changeWorkspaceProduct = admin.changeWorkspaceProduct.bind(admin);
|
|
1432
1506
|
target.validateWorkflowAccessAsAdmin = admin.validateWorkflowAccessAsAdmin.bind(admin);
|
|
1433
1507
|
target.mergeMembers = admin.mergeMembers.bind(admin);
|
|
1434
1508
|
target.getAdminWorkspaceMember = admin.getAdminWorkspaceMember.bind(admin);
|
|
1435
1509
|
target.createAdminWorkspaceMember = admin.createAdminWorkspaceMember.bind(admin);
|
|
1436
1510
|
target.updateAdminWorkspaceMember = admin.updateAdminWorkspaceMember.bind(admin);
|
|
1511
|
+
target.unarchiveAdminWorkspaceMember = admin.unarchiveAdminWorkspaceMember.bind(admin);
|
|
1437
1512
|
target.getAdminWorkspaceMemberRoles = admin.getAdminWorkspaceMemberRoles.bind(admin);
|
|
1438
1513
|
target.getMissingMemberships = admin.getMissingMemberships.bind(admin);
|
|
1439
1514
|
target.linkMissingMembership = admin.linkMissingMembership.bind(admin);
|
|
@@ -1464,8 +1539,8 @@ function bindAdminMethods(target, admin) {
|
|
|
1464
1539
|
target.deleteAdminInboxMapping = admin.deleteAdminInboxMapping.bind(admin);
|
|
1465
1540
|
target.generateChatSummaries = admin.generateChatSummaries.bind(admin);
|
|
1466
1541
|
target.executeQuery = admin.executeQuery.bind(admin);
|
|
1542
|
+
target.getTraceUrl = admin.getTraceUrl.bind(admin);
|
|
1467
1543
|
target.listAllThemes = admin.listAllThemes.bind(admin);
|
|
1468
|
-
target.migrateRoutineCadenceKeys = admin.migrateRoutineCadenceKeys.bind(admin);
|
|
1469
1544
|
target.startBulkTOSAcceptance = admin.startBulkTOSAcceptance.bind(admin);
|
|
1470
1545
|
target.startTokenUsageBackfill = admin.startTokenUsageBackfill.bind(admin);
|
|
1471
1546
|
target.updateChatMessage = admin.updateChatMessage.bind(admin);
|
|
@@ -1525,6 +1600,7 @@ function bindAdminMethods(target, admin) {
|
|
|
1525
1600
|
target.getVoiceMetrics = admin.getVoiceMetrics.bind(admin);
|
|
1526
1601
|
target.getHealthMetrics = admin.getHealthMetrics.bind(admin);
|
|
1527
1602
|
target.getHealthDetail = admin.getHealthDetail.bind(admin);
|
|
1603
|
+
target.getWorkspaceInboxesAsAdmin = admin.getWorkspaceInboxesAsAdmin.bind(admin);
|
|
1528
1604
|
target.listWorkspaceChats = admin.listWorkspaceChats.bind(admin);
|
|
1529
1605
|
target.getWorkspaceChat = admin.getWorkspaceChat.bind(admin);
|
|
1530
1606
|
target.rateWorkspaceChatMessage = admin.rateWorkspaceChatMessage.bind(admin);
|
|
@@ -1543,6 +1619,13 @@ function bindAdminMethods(target, admin) {
|
|
|
1543
1619
|
}
|
|
1544
1620
|
var ApiClient = class extends GravityRailClient {
|
|
1545
1621
|
// Admin API
|
|
1622
|
+
listSupportRequestsAsAdmin;
|
|
1623
|
+
getSupportRequestAsAdmin;
|
|
1624
|
+
commentOnSupportRequestAsAdmin;
|
|
1625
|
+
noteOnSupportRequestAsAdmin;
|
|
1626
|
+
updateSupportRequestStatusAsAdmin;
|
|
1627
|
+
updateSupportRequestAttributesAsAdmin;
|
|
1628
|
+
createSupportRequestLinearIssueAsAdmin;
|
|
1546
1629
|
getAccounts;
|
|
1547
1630
|
getAccountDetails;
|
|
1548
1631
|
getAccountByUuid;
|
|
@@ -1591,11 +1674,13 @@ var ApiClient = class extends GravityRailClient {
|
|
|
1591
1674
|
disableMaintenanceMode;
|
|
1592
1675
|
getWorkspaceHealth;
|
|
1593
1676
|
provisionPlanFeatures;
|
|
1677
|
+
changeWorkspaceProduct;
|
|
1594
1678
|
validateWorkflowAccessAsAdmin;
|
|
1595
1679
|
mergeMembers;
|
|
1596
1680
|
getAdminWorkspaceMember;
|
|
1597
1681
|
createAdminWorkspaceMember;
|
|
1598
1682
|
updateAdminWorkspaceMember;
|
|
1683
|
+
unarchiveAdminWorkspaceMember;
|
|
1599
1684
|
getAdminWorkspaceMemberRoles;
|
|
1600
1685
|
getMissingMemberships;
|
|
1601
1686
|
linkMissingMembership;
|
|
@@ -1626,8 +1711,8 @@ var ApiClient = class extends GravityRailClient {
|
|
|
1626
1711
|
deleteAdminInboxMapping;
|
|
1627
1712
|
generateChatSummaries;
|
|
1628
1713
|
executeQuery;
|
|
1714
|
+
getTraceUrl;
|
|
1629
1715
|
listAllThemes;
|
|
1630
|
-
migrateRoutineCadenceKeys;
|
|
1631
1716
|
startBulkTOSAcceptance;
|
|
1632
1717
|
startTokenUsageBackfill;
|
|
1633
1718
|
updateChatMessage;
|
|
@@ -1681,6 +1766,7 @@ var ApiClient = class extends GravityRailClient {
|
|
|
1681
1766
|
getVoiceMetrics;
|
|
1682
1767
|
getHealthMetrics;
|
|
1683
1768
|
getHealthDetail;
|
|
1769
|
+
getWorkspaceInboxesAsAdmin;
|
|
1684
1770
|
listWorkspaceChats;
|
|
1685
1771
|
getWorkspaceChat;
|
|
1686
1772
|
rateWorkspaceChatMessage;
|
|
@@ -1717,6 +1803,10 @@ var ApiClient = class extends GravityRailClient {
|
|
|
1717
1803
|
this.adminApi.setActingMember(memberUuid);
|
|
1718
1804
|
}
|
|
1719
1805
|
}
|
|
1806
|
+
// setOrgToken is intentionally NOT overridden here. Admin routes
|
|
1807
|
+
// (/api/v2/admin/*) are protected by the bearer token's superuser claim and
|
|
1808
|
+
// ignore X-Org-Token entirely, so propagating an org token to adminApi is
|
|
1809
|
+
// dead code that would only obscure auth flow if admin route logic changes.
|
|
1720
1810
|
};
|
|
1721
1811
|
|
|
1722
1812
|
// src/lib/context.ts
|
|
@@ -1892,7 +1982,131 @@ function loadRealmCredentials(realm, devRealmsPath) {
|
|
|
1892
1982
|
}
|
|
1893
1983
|
}
|
|
1894
1984
|
|
|
1985
|
+
// src/lib/org-auth.ts
|
|
1986
|
+
import { execFile } from "child_process";
|
|
1987
|
+
var ORG_AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1988
|
+
function extractOrgTokenFromSetCookies(setCookieValues, preferredCookieName) {
|
|
1989
|
+
let fallback;
|
|
1990
|
+
for (const value of setCookieValues) {
|
|
1991
|
+
const nameValue = value.split(";")[0]?.trim() ?? "";
|
|
1992
|
+
const eqIdx = nameValue.indexOf("=");
|
|
1993
|
+
if (eqIdx === -1) continue;
|
|
1994
|
+
const name = nameValue.slice(0, eqIdx).trim();
|
|
1995
|
+
const tokenValue = nameValue.slice(eqIdx + 1).trim() || void 0;
|
|
1996
|
+
if (!tokenValue) continue;
|
|
1997
|
+
if (name === preferredCookieName) return tokenValue;
|
|
1998
|
+
if (!fallback && (name.startsWith("orgToken") || name.startsWith("orgSession"))) {
|
|
1999
|
+
fallback = tokenValue;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
return fallback;
|
|
2003
|
+
}
|
|
2004
|
+
async function tryDirectJoin(apiUrl, accessToken, orgAuthInfo) {
|
|
2005
|
+
const response = await fetch(`${apiUrl}/api/v2/o/${orgAuthInfo.orgUuid}/join`, {
|
|
2006
|
+
method: "POST",
|
|
2007
|
+
headers: {
|
|
2008
|
+
"Content-Type": "application/json",
|
|
2009
|
+
Authorization: `Bearer ${accessToken}`
|
|
2010
|
+
},
|
|
2011
|
+
body: "{}"
|
|
2012
|
+
});
|
|
2013
|
+
if (response.status === 400) {
|
|
2014
|
+
return null;
|
|
2015
|
+
}
|
|
2016
|
+
if (!response.ok) {
|
|
2017
|
+
throw new Error(`Org join failed (HTTP ${response.status})`);
|
|
2018
|
+
}
|
|
2019
|
+
const rawHeaders = response.headers;
|
|
2020
|
+
const setCookies = rawHeaders.getSetCookie?.() ?? (rawHeaders.get("set-cookie") ? [rawHeaders.get("set-cookie")] : []);
|
|
2021
|
+
if (setCookies.length === 0) {
|
|
2022
|
+
throw new Error("Org join succeeded but no Set-Cookie header returned");
|
|
2023
|
+
}
|
|
2024
|
+
const orgToken = extractOrgTokenFromSetCookies(setCookies, orgAuthInfo.cookieName);
|
|
2025
|
+
if (!orgToken) {
|
|
2026
|
+
throw new Error("Org join succeeded but org token cookie not found in Set-Cookie");
|
|
2027
|
+
}
|
|
2028
|
+
return orgToken;
|
|
2029
|
+
}
|
|
2030
|
+
function isAllowedAuthUrl(url, apiUrl) {
|
|
2031
|
+
try {
|
|
2032
|
+
const parsed = new URL(url);
|
|
2033
|
+
const api = new URL(apiUrl);
|
|
2034
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return false;
|
|
2035
|
+
if (parsed.hostname === api.hostname) return true;
|
|
2036
|
+
const lastTwo = (host) => {
|
|
2037
|
+
const parts = host.split(".").filter(Boolean);
|
|
2038
|
+
return parts.length >= 2 ? parts.slice(-2).join(".") : host;
|
|
2039
|
+
};
|
|
2040
|
+
const isIp = /^[0-9.]+$|^\[.*\]$/.test(parsed.hostname);
|
|
2041
|
+
if (isIp) return false;
|
|
2042
|
+
return lastTwo(parsed.hostname) === lastTwo(api.hostname);
|
|
2043
|
+
} catch {
|
|
2044
|
+
return false;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
function openBrowser(url) {
|
|
2048
|
+
let command;
|
|
2049
|
+
let args;
|
|
2050
|
+
if (process.platform === "darwin") {
|
|
2051
|
+
command = "open";
|
|
2052
|
+
args = [url];
|
|
2053
|
+
} else if (process.platform === "win32") {
|
|
2054
|
+
command = "cmd.exe";
|
|
2055
|
+
args = ["/c", "start", "", url];
|
|
2056
|
+
} else {
|
|
2057
|
+
command = "xdg-open";
|
|
2058
|
+
args = [url];
|
|
2059
|
+
}
|
|
2060
|
+
execFile(command, args, (_err) => {
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
function registerOrgAuthHandler(apiClient, apiUrl, accessToken) {
|
|
2064
|
+
apiClient.setOrgAuthHandler(async (orgAuthInfo) => {
|
|
2065
|
+
try {
|
|
2066
|
+
const orgToken = await tryDirectJoin(apiUrl, accessToken, orgAuthInfo);
|
|
2067
|
+
if (orgToken !== null) {
|
|
2068
|
+
apiClient.setOrgToken(orgToken, orgAuthInfo.orgUuid);
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
} catch (err) {
|
|
2072
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2073
|
+
stderr(`Warning: org direct-join failed (${msg}); trying browser auth`);
|
|
2074
|
+
}
|
|
2075
|
+
if (!isAllowedAuthUrl(orgAuthInfo.authUrl, apiUrl)) {
|
|
2076
|
+
throw new Error(
|
|
2077
|
+
`Refusing to open organization auth URL: scheme/host does not match the configured API host. Re-run with the expected --api-url, or contact support.`
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
2080
|
+
stderr(`
|
|
2081
|
+
This workspace requires organization SSO authentication.`);
|
|
2082
|
+
stderr(`Opening browser to complete authentication...`);
|
|
2083
|
+
stderr(` ${orgAuthInfo.authUrl}
|
|
2084
|
+
`);
|
|
2085
|
+
stderr(`If the browser does not open, visit the URL above manually.`);
|
|
2086
|
+
stderr(
|
|
2087
|
+
`After signing in, re-run the command \u2014 the CLI cannot receive the SSO token directly.`
|
|
2088
|
+
);
|
|
2089
|
+
openBrowser(orgAuthInfo.authUrl);
|
|
2090
|
+
await new Promise((_, reject) => {
|
|
2091
|
+
const t = setTimeout(
|
|
2092
|
+
() => reject(
|
|
2093
|
+
new Error(
|
|
2094
|
+
"Organization authentication timed out.\nPlease complete authentication in the browser, then re-run the command."
|
|
2095
|
+
)
|
|
2096
|
+
),
|
|
2097
|
+
ORG_AUTH_TIMEOUT_MS
|
|
2098
|
+
);
|
|
2099
|
+
t.unref?.();
|
|
2100
|
+
});
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
|
|
1895
2104
|
// src/lib/context.ts
|
|
2105
|
+
function makeApiClient(accessToken, apiUrl) {
|
|
2106
|
+
const apiClient = new ApiClient(accessToken, apiUrl);
|
|
2107
|
+
registerOrgAuthHandler(apiClient, apiUrl, accessToken);
|
|
2108
|
+
return apiClient;
|
|
2109
|
+
}
|
|
1896
2110
|
async function tryTokenRefresh(ctx, creds) {
|
|
1897
2111
|
if (!creds.refreshToken) return null;
|
|
1898
2112
|
const refreshed = await refreshAccessToken(
|
|
@@ -1902,7 +2116,7 @@ async function tryTokenRefresh(ctx, creds) {
|
|
|
1902
2116
|
ctx.env
|
|
1903
2117
|
);
|
|
1904
2118
|
if (!refreshed?.accessToken) return null;
|
|
1905
|
-
const apiClient =
|
|
2119
|
+
const apiClient = makeApiClient(refreshed.accessToken, ctx.envConfig.apiUrl);
|
|
1906
2120
|
return { ...ctx, accessToken: refreshed.accessToken, apiClient };
|
|
1907
2121
|
}
|
|
1908
2122
|
function createContext(opts) {
|
|
@@ -1926,12 +2140,12 @@ async function createAuthenticatedContext(opts) {
|
|
|
1926
2140
|
devRealmsPath = loadDevCloudRealmFile(namespace);
|
|
1927
2141
|
}
|
|
1928
2142
|
const { token, workspaceUuid } = loadRealmCredentials(opts.realm, devRealmsPath);
|
|
1929
|
-
const apiClient =
|
|
2143
|
+
const apiClient = makeApiClient(token, ctx.envConfig.apiUrl);
|
|
1930
2144
|
return { ...ctx, accessToken: token, apiClient, realmWorkspace: workspaceUuid };
|
|
1931
2145
|
}
|
|
1932
2146
|
const apiKey = process.env.GRAVITY_RAIL_API_KEY;
|
|
1933
2147
|
if (apiKey) {
|
|
1934
|
-
const apiClient =
|
|
2148
|
+
const apiClient = makeApiClient(apiKey, ctx.envConfig.apiUrl);
|
|
1935
2149
|
return { ...ctx, accessToken: apiKey, apiClient };
|
|
1936
2150
|
}
|
|
1937
2151
|
const creds = ctx.credentialsManager.load(ctx.env);
|
|
@@ -1939,7 +2153,7 @@ async function createAuthenticatedContext(opts) {
|
|
|
1939
2153
|
const buffer = 5 * 60 * 1e3;
|
|
1940
2154
|
const notExpired = !creds.expiresAt || new Date(creds.expiresAt) > new Date(Date.now() + buffer);
|
|
1941
2155
|
if (notExpired) {
|
|
1942
|
-
const apiClient =
|
|
2156
|
+
const apiClient = makeApiClient(creds.accessToken, ctx.envConfig.apiUrl);
|
|
1943
2157
|
return { ...ctx, accessToken: creds.accessToken, apiClient };
|
|
1944
2158
|
}
|
|
1945
2159
|
const refreshed = await tryTokenRefresh(ctx, creds);
|
|
@@ -1954,12 +2168,14 @@ function requireAuth(ctx) {
|
|
|
1954
2168
|
return ctx;
|
|
1955
2169
|
}
|
|
1956
2170
|
function createPublicContext(opts) {
|
|
1957
|
-
const
|
|
1958
|
-
const
|
|
1959
|
-
|
|
1960
|
-
|
|
2171
|
+
const env = opts.env ?? "prod";
|
|
2172
|
+
const envConfig = opts.apiUrl ? {
|
|
2173
|
+
apiUrl: opts.apiUrl,
|
|
2174
|
+
frontendUrl: opts.frontendUrl || process.env.GRAVITY_RAIL_FRONTEND_URL || "https://app.gravityrail.com"
|
|
2175
|
+
} : getEnvironmentConfig(env);
|
|
2176
|
+
const credentialsManager = new CredentialsManager(homedir(), envConfig.apiUrl);
|
|
1961
2177
|
return {
|
|
1962
|
-
env
|
|
2178
|
+
env,
|
|
1963
2179
|
envConfig,
|
|
1964
2180
|
outputFormat: detectOutputFormat(opts.outputFormat),
|
|
1965
2181
|
verbose: opts.verbose ?? false,
|
|
@@ -1971,7 +2187,7 @@ async function createPublicAuthenticatedContext(opts) {
|
|
|
1971
2187
|
const ctx = createPublicContext(opts);
|
|
1972
2188
|
const apiKey = process.env.GRAVITY_RAIL_API_KEY;
|
|
1973
2189
|
if (apiKey) {
|
|
1974
|
-
const apiClient =
|
|
2190
|
+
const apiClient = makeApiClient(apiKey, ctx.envConfig.apiUrl);
|
|
1975
2191
|
return { ...ctx, accessToken: apiKey, apiClient };
|
|
1976
2192
|
}
|
|
1977
2193
|
const creds = ctx.credentialsManager.load(ctx.env);
|
|
@@ -1979,7 +2195,7 @@ async function createPublicAuthenticatedContext(opts) {
|
|
|
1979
2195
|
const buffer = 5 * 60 * 1e3;
|
|
1980
2196
|
const notExpired = !creds.expiresAt || new Date(creds.expiresAt) > new Date(Date.now() + buffer);
|
|
1981
2197
|
if (notExpired) {
|
|
1982
|
-
const apiClient =
|
|
2198
|
+
const apiClient = makeApiClient(creds.accessToken, ctx.envConfig.apiUrl);
|
|
1983
2199
|
return { ...ctx, accessToken: creds.accessToken, apiClient };
|
|
1984
2200
|
}
|
|
1985
2201
|
const refreshed = await tryTokenRefresh(ctx, creds);
|
|
@@ -2011,32 +2227,48 @@ async function readJsonInput(args) {
|
|
|
2011
2227
|
throw new CLIError(`Cannot read file: ${filePath}`);
|
|
2012
2228
|
}
|
|
2013
2229
|
}
|
|
2014
|
-
if (
|
|
2015
|
-
return
|
|
2230
|
+
if (process.stdin.isTTY === true) {
|
|
2231
|
+
return void 0;
|
|
2016
2232
|
}
|
|
2017
|
-
return
|
|
2233
|
+
return readStdinIfPiped();
|
|
2018
2234
|
}
|
|
2019
|
-
function
|
|
2235
|
+
function readStdinIfPiped() {
|
|
2020
2236
|
return new Promise((resolve, reject) => {
|
|
2021
2237
|
let data = "";
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2238
|
+
let settled = false;
|
|
2239
|
+
const finish = (value) => {
|
|
2240
|
+
if (settled) return;
|
|
2241
|
+
settled = true;
|
|
2242
|
+
resolve(value);
|
|
2243
|
+
};
|
|
2244
|
+
const flush = () => {
|
|
2027
2245
|
if (!data.trim()) {
|
|
2028
|
-
|
|
2246
|
+
finish(void 0);
|
|
2029
2247
|
return;
|
|
2030
2248
|
}
|
|
2031
2249
|
try {
|
|
2032
|
-
|
|
2250
|
+
finish(JSON.parse(data));
|
|
2033
2251
|
} catch {
|
|
2034
2252
|
reject(new CLIError("Invalid JSON from stdin"));
|
|
2035
2253
|
}
|
|
2254
|
+
};
|
|
2255
|
+
process.stdin.setEncoding("utf-8");
|
|
2256
|
+
process.stdin.on("data", (chunk) => {
|
|
2257
|
+
data += chunk;
|
|
2036
2258
|
});
|
|
2259
|
+
process.stdin.on("end", flush);
|
|
2037
2260
|
process.stdin.on("error", (err) => {
|
|
2038
2261
|
reject(new CLIError(`Error reading stdin: ${err.message}`));
|
|
2039
2262
|
});
|
|
2263
|
+
process.stdin.resume();
|
|
2264
|
+
setImmediate(() => {
|
|
2265
|
+
if (data) return;
|
|
2266
|
+
if (process.stdin.readableEnded) {
|
|
2267
|
+
flush();
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
finish(void 0);
|
|
2271
|
+
});
|
|
2040
2272
|
});
|
|
2041
2273
|
}
|
|
2042
2274
|
|
|
@@ -2632,6 +2864,17 @@ function requireNumber(args, key) {
|
|
|
2632
2864
|
if (isNaN(num)) throw new Error(`Argument --${key} must be a number`);
|
|
2633
2865
|
return num;
|
|
2634
2866
|
}
|
|
2867
|
+
function requireNumberFrom(args, ...keys) {
|
|
2868
|
+
for (const key of keys) {
|
|
2869
|
+
const val = args[key];
|
|
2870
|
+
if (typeof val === "string") {
|
|
2871
|
+
const num = Number(val);
|
|
2872
|
+
if (!isNaN(num)) return num;
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
const hint = keys.map((k) => `--${k}`).join(" or ");
|
|
2876
|
+
throw new Error(`Missing required argument: ${hint}`);
|
|
2877
|
+
}
|
|
2635
2878
|
|
|
2636
2879
|
// src/domains/agent-tasks.ts
|
|
2637
2880
|
var LIST_COLUMNS = [
|
|
@@ -2792,6 +3035,7 @@ var agentsDomain = {
|
|
|
2792
3035
|
var COLUMNS2 = [
|
|
2793
3036
|
COL.id(),
|
|
2794
3037
|
COL.uuid(),
|
|
3038
|
+
{ header: "TYPE", getValue: (item) => item.chatType ?? item.chat_type ?? "" },
|
|
2795
3039
|
{ header: "MEMBER", getValue: (item) => item.member_name ?? String(item.member_id ?? "") },
|
|
2796
3040
|
COL.status(),
|
|
2797
3041
|
{ header: "CHANNEL", getValue: (item) => item.channel ?? "" },
|
|
@@ -2843,7 +3087,11 @@ var chatsDomain = {
|
|
|
2843
3087
|
isFlag: true
|
|
2844
3088
|
},
|
|
2845
3089
|
{ name: "is-test", description: "Show only test chats", isFlag: true },
|
|
2846
|
-
{ name: "member-id", description: "Filter by member ID" }
|
|
3090
|
+
{ name: "member-id", description: "Filter by member ID" },
|
|
3091
|
+
{
|
|
3092
|
+
name: "chat-type",
|
|
3093
|
+
description: "Filter by chat type (assignment, manager, developer, member, contributor). Default: assignment"
|
|
3094
|
+
}
|
|
2847
3095
|
]
|
|
2848
3096
|
}),
|
|
2849
3097
|
// Messages
|
|
@@ -2852,7 +3100,7 @@ var chatsDomain = {
|
|
|
2852
3100
|
description: "List messages for a chat. Use --limit/--offset to page; the API caps a single request at 100. Tool-call shell rows that the API filter excludes (parent_message_id IS NOT NULL or parent_call_id IS NOT NULL) are still hidden \u2014 use `gr chats inspect --id <chat>` for the full debug view.",
|
|
2853
3101
|
requiresWorkspace: true,
|
|
2854
3102
|
args: [
|
|
2855
|
-
{ name: "chat-id", description: "Chat ID", required: true },
|
|
3103
|
+
{ name: "chat-id", aliases: ["id"], description: "Chat ID", required: true },
|
|
2856
3104
|
{
|
|
2857
3105
|
name: "limit",
|
|
2858
3106
|
description: "Max messages to return (1-100, default 100)"
|
|
@@ -2875,7 +3123,7 @@ var chatsDomain = {
|
|
|
2875
3123
|
}
|
|
2876
3124
|
const result = await requireAuth(ctx).apiClient.getChatMessages(
|
|
2877
3125
|
workspace,
|
|
2878
|
-
|
|
3126
|
+
requireNumberFrom(args, "chat-id", "id"),
|
|
2879
3127
|
limit,
|
|
2880
3128
|
offset
|
|
2881
3129
|
);
|
|
@@ -2888,7 +3136,7 @@ var chatsDomain = {
|
|
|
2888
3136
|
mutates: true,
|
|
2889
3137
|
requiresWorkspace: true,
|
|
2890
3138
|
args: [
|
|
2891
|
-
{ name: "chat-id", description: "Chat ID", required: true },
|
|
3139
|
+
{ name: "chat-id", aliases: ["id"], description: "Chat ID", required: true },
|
|
2892
3140
|
{ name: "stream", description: "Stream the AI response via AG-UI", isFlag: true }
|
|
2893
3141
|
],
|
|
2894
3142
|
handler: async ({ ctx, workspace, args, data }) => {
|
|
@@ -2897,7 +3145,7 @@ var chatsDomain = {
|
|
|
2897
3145
|
throw new Error(
|
|
2898
3146
|
`Message content is required (pass --data '"your message"' or --data '{"content":"your message"}')`
|
|
2899
3147
|
);
|
|
2900
|
-
const chatId =
|
|
3148
|
+
const chatId = requireNumberFrom(args, "chat-id", "id");
|
|
2901
3149
|
if (args.stream) {
|
|
2902
3150
|
const aguiClient = new AGUIClient({
|
|
2903
3151
|
baseUrl: ctx.envConfig.apiUrl,
|
|
@@ -2930,11 +3178,11 @@ var chatsDomain = {
|
|
|
2930
3178
|
description: "Send an assistant message to a chat (does not trigger AI inference)",
|
|
2931
3179
|
mutates: true,
|
|
2932
3180
|
requiresWorkspace: true,
|
|
2933
|
-
args: [{ name: "chat-id", description: "Chat ID", required: true }],
|
|
3181
|
+
args: [{ name: "chat-id", aliases: ["id"], description: "Chat ID", required: true }],
|
|
2934
3182
|
handler: async ({ ctx, workspace, args, data }) => {
|
|
2935
3183
|
const result = await requireAuth(ctx).apiClient.sendAssistantMessage(
|
|
2936
3184
|
workspace,
|
|
2937
|
-
|
|
3185
|
+
requireNumberFrom(args, "chat-id", "id"),
|
|
2938
3186
|
data
|
|
2939
3187
|
);
|
|
2940
3188
|
outputSingle(result, ctx, MESSAGE_COLUMNS2);
|
|
@@ -4975,8 +5223,8 @@ var dataTypesDomain = {
|
|
|
4975
5223
|
var RULE_COLUMNS = [
|
|
4976
5224
|
COL.id(),
|
|
4977
5225
|
COL.name(),
|
|
4978
|
-
{ header: "EVENT", getValue: (item) => item.
|
|
4979
|
-
|
|
5226
|
+
{ header: "EVENT", getValue: (item) => item.eventType ?? "" },
|
|
5227
|
+
{ header: "ACTIVE", getValue: (item) => String(item.active ?? "") }
|
|
4980
5228
|
];
|
|
4981
5229
|
var EVENT_COLUMNS2 = [
|
|
4982
5230
|
COL.id(),
|
|
@@ -4984,29 +5232,33 @@ var EVENT_COLUMNS2 = [
|
|
|
4984
5232
|
{ header: "SLUG", getValue: (item) => item.slug ?? "" },
|
|
4985
5233
|
COL.status()
|
|
4986
5234
|
];
|
|
4987
|
-
var
|
|
5235
|
+
var eventRulesCrudNoList = crudCommands({
|
|
4988
5236
|
prefix: ["events", "rules"],
|
|
4989
5237
|
entityName: "event rule",
|
|
4990
|
-
listMethod: "getEventRules",
|
|
4991
5238
|
getMethod: "getEventRule",
|
|
4992
5239
|
createMethod: "createEventRule",
|
|
4993
5240
|
updateMethod: "updateEventRule",
|
|
4994
5241
|
deleteMethod: "deleteEventRule",
|
|
4995
|
-
// The endpoint is paginated and excludes nothing by object_type. Without
|
|
4996
|
-
// 'full', the CLI silently returned only page 1 (default 25 rules), which
|
|
4997
|
-
// hid routine/journey/calendar-scoped rules in workspaces with many
|
|
4998
|
-
// task-scoped rules. 'full' wires up --page/--page-size and --all.
|
|
4999
|
-
paginationSupport: "full",
|
|
5000
5242
|
columns: RULE_COLUMNS,
|
|
5001
5243
|
createExamples: [
|
|
5002
5244
|
`gr events rules create -w $WID --data '{"name":"Label on step","objectType":"journey",`,
|
|
5003
5245
|
' "eventType":"journey_step:execute","actionType":"member:label",',
|
|
5004
5246
|
` "actionParams":{"addLabelIds":["<label-uuid>"]},"journeyStepId":<step-id>}'`,
|
|
5005
5247
|
"",
|
|
5248
|
+
"Scheduled outbound (routine \u2192 chat \u2192 voice/SMS):",
|
|
5249
|
+
`gr events rules create -w $WID --allow-write --data '{"name":"AM voice check-in",`,
|
|
5250
|
+
' "objectType":"routine","eventType":"routine:triggered","actionType":"chat:create",',
|
|
5251
|
+
' "routineId":<routine-id>,',
|
|
5252
|
+
' "actionParams":{"workflowId":<workflow-id>,"channel":"phone-voice",',
|
|
5253
|
+
` "phoneNumberId":<workspace-phone-id>}}'`,
|
|
5254
|
+
"",
|
|
5255
|
+
' PM SMS variant: use "channel":"phone-sms" and a second rule on the same routineId.',
|
|
5256
|
+
" Prereq: org phone assigned + gr phone-numbers link; workflow published with start task.",
|
|
5257
|
+
"",
|
|
5006
5258
|
"Required: objectType, eventType, actionType",
|
|
5007
|
-
"Optional: name, actionParams, conditions (CEL), journeyStepId, delay (sec), order",
|
|
5259
|
+
"Optional: name, actionParams, conditions (CEL), journeyStepId, routineId, delay (sec), order",
|
|
5008
5260
|
"",
|
|
5009
|
-
"Common eventTypes: journey_step:execute, journey:enrolled,
|
|
5261
|
+
"Common eventTypes: journey_step:execute, journey:enrolled, routine:triggered,",
|
|
5010
5262
|
" task:created, chat:created, data_record:created, member:created",
|
|
5011
5263
|
"",
|
|
5012
5264
|
"Common actionTypes: member:label, member:notify, webhook:send, email:send,",
|
|
@@ -5015,6 +5267,57 @@ var eventRulesCrud = crudCommands({
|
|
|
5015
5267
|
'member:label actionParams: {"addLabelIds":["uuid"],"removeLabelIds":["uuid"]}'
|
|
5016
5268
|
]
|
|
5017
5269
|
});
|
|
5270
|
+
var eventRulesListCmd = {
|
|
5271
|
+
path: ["events", "rules", "list"],
|
|
5272
|
+
description: "List event rules (use --journey-step-id or --event-type to filter)",
|
|
5273
|
+
requiresWorkspace: true,
|
|
5274
|
+
columns: RULE_COLUMNS,
|
|
5275
|
+
args: [
|
|
5276
|
+
{ name: "journey-step-id", description: "Filter by journey step ID" },
|
|
5277
|
+
{ name: "event-type", description: "Filter by event type (e.g. journey_step:execute)" },
|
|
5278
|
+
{ name: "object-type", description: "Filter by object type (e.g. journey, task)" },
|
|
5279
|
+
{ name: "include-disabled", description: "Include inactive rules", isFlag: true },
|
|
5280
|
+
{ name: "page", description: "Page number (without filters)" },
|
|
5281
|
+
{ name: "page-size", description: "Page size (without filters)" },
|
|
5282
|
+
{
|
|
5283
|
+
name: "all",
|
|
5284
|
+
description: "Fetch all pages (without filters)",
|
|
5285
|
+
isFlag: true
|
|
5286
|
+
}
|
|
5287
|
+
],
|
|
5288
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
5289
|
+
const client = requireAuth(ctx).apiClient;
|
|
5290
|
+
const journeyStepId = args["journey-step-id"] ? requireNumber(args, "journey-step-id") : void 0;
|
|
5291
|
+
const eventType = args["event-type"];
|
|
5292
|
+
const objectType = args["object-type"];
|
|
5293
|
+
const includeDisabled = args["include-disabled"] === true;
|
|
5294
|
+
if (journeyStepId !== void 0 || eventType !== void 0 || objectType !== void 0 || includeDisabled) {
|
|
5295
|
+
const result2 = await client.searchEventRules(workspace, {
|
|
5296
|
+
...journeyStepId !== void 0 ? { journeyStepId } : {},
|
|
5297
|
+
...eventType ? { eventType } : {},
|
|
5298
|
+
...objectType ? { objectType } : {},
|
|
5299
|
+
...includeDisabled ? { includeDisabled: true } : {}
|
|
5300
|
+
});
|
|
5301
|
+
outputData(listResult(result2, false), ctx, RULE_COLUMNS);
|
|
5302
|
+
return;
|
|
5303
|
+
}
|
|
5304
|
+
if (args["all"]) {
|
|
5305
|
+
const allItems = await fetchAllPages(
|
|
5306
|
+
async ({ page: page2, pageSize: pageSize2 }) => client.getEventRules(workspace, { page: page2, pageSize: pageSize2 })
|
|
5307
|
+
);
|
|
5308
|
+
outputData(allItems, ctx, RULE_COLUMNS);
|
|
5309
|
+
return;
|
|
5310
|
+
}
|
|
5311
|
+
const page = args["page"] ? requireNumber(args, "page") : void 0;
|
|
5312
|
+
const pageSize = args["page-size"] ? requireNumber(args, "page-size") : void 0;
|
|
5313
|
+
const result = await client.getEventRules(workspace, {
|
|
5314
|
+
...page !== void 0 ? { page } : {},
|
|
5315
|
+
...pageSize !== void 0 ? { pageSize } : {}
|
|
5316
|
+
});
|
|
5317
|
+
outputData(listResult(result, true), ctx, RULE_COLUMNS);
|
|
5318
|
+
}
|
|
5319
|
+
};
|
|
5320
|
+
var eventRulesCrud = [eventRulesListCmd, ...eventRulesCrudNoList];
|
|
5018
5321
|
var eventRulesAliases = eventRulesCrud.filter((cmd) => {
|
|
5019
5322
|
const verb = cmd.path[cmd.path.length - 1];
|
|
5020
5323
|
return verb === "list" || verb === "get";
|
|
@@ -5224,6 +5527,7 @@ var filesDomain = {
|
|
|
5224
5527
|
};
|
|
5225
5528
|
|
|
5226
5529
|
// src/domains/journeys.ts
|
|
5530
|
+
import { EventRuleEventType } from "@gravity-rail/sdk";
|
|
5227
5531
|
var JOURNEY_COLUMNS = [
|
|
5228
5532
|
COL.id(),
|
|
5229
5533
|
{ header: "UUID", getValue: (item) => (item.uuid ?? "").slice(0, 8) },
|
|
@@ -5287,6 +5591,15 @@ var NOTE_COLUMNS2 = [
|
|
|
5287
5591
|
{ header: "CONTENT", getValue: (item) => (item.contents ?? "").slice(0, 60) },
|
|
5288
5592
|
{ header: "CREATED", getValue: (item) => item.createdAt ?? "" }
|
|
5289
5593
|
];
|
|
5594
|
+
var ACTION_COLUMNS = [
|
|
5595
|
+
COL.id(),
|
|
5596
|
+
{ header: "UUID", getValue: (item) => (item.uuid ?? "").slice(0, 8) },
|
|
5597
|
+
COL.name(),
|
|
5598
|
+
{ header: "EVENT", getValue: (item) => item.eventType ?? "" },
|
|
5599
|
+
{ header: "ACTION", getValue: (item) => item.actionType ?? "" },
|
|
5600
|
+
{ header: "ACTIVE", getValue: (item) => String(item.active ?? "") },
|
|
5601
|
+
{ header: "CONDITIONS", getValue: (item) => (item.conditions ?? "").slice(0, 40) }
|
|
5602
|
+
];
|
|
5290
5603
|
function baseUrl(ctx, wid) {
|
|
5291
5604
|
return `${ctx.envConfig.apiUrl}/api/v2/w/${wid}/journeys`;
|
|
5292
5605
|
}
|
|
@@ -6001,6 +6314,70 @@ var journeysDomain = {
|
|
|
6001
6314
|
process.stdout.write(`Deleted note ${noteId}
|
|
6002
6315
|
`);
|
|
6003
6316
|
}
|
|
6317
|
+
},
|
|
6318
|
+
// ── Step execute actions (event rules scoped to a step) ────────
|
|
6319
|
+
{
|
|
6320
|
+
path: ["journeys", "steps", "actions", "list"],
|
|
6321
|
+
description: "List event rules (actions) attached to a journey step",
|
|
6322
|
+
requiresWorkspace: true,
|
|
6323
|
+
args: [
|
|
6324
|
+
{ name: "step-id", description: "Journey step ID (numeric)", required: true },
|
|
6325
|
+
{
|
|
6326
|
+
name: "include-disabled",
|
|
6327
|
+
description: "Include inactive/disabled rules",
|
|
6328
|
+
isFlag: true
|
|
6329
|
+
}
|
|
6330
|
+
],
|
|
6331
|
+
examples: [
|
|
6332
|
+
"gr journeys steps actions list -w $WID --step-id <step-id>",
|
|
6333
|
+
"gr journeys steps actions list -w $WID --step-id <step-id> --include-disabled"
|
|
6334
|
+
],
|
|
6335
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
6336
|
+
const stepId = requireNumber(args, "step-id");
|
|
6337
|
+
const includeDisabled = args["include-disabled"] === true;
|
|
6338
|
+
const result = await requireAuth(ctx).apiClient.searchEventRules(workspace, {
|
|
6339
|
+
eventType: EventRuleEventType.JOURNEY_STEP_EXECUTE,
|
|
6340
|
+
journeyStepId: stepId,
|
|
6341
|
+
...includeDisabled ? { includeDisabled: true } : {}
|
|
6342
|
+
});
|
|
6343
|
+
outputData(listResult(result, false), ctx, ACTION_COLUMNS);
|
|
6344
|
+
}
|
|
6345
|
+
},
|
|
6346
|
+
{
|
|
6347
|
+
path: ["journeys", "steps", "actions", "get"],
|
|
6348
|
+
description: "Get a journey step action (event rule) by ID",
|
|
6349
|
+
requiresWorkspace: true,
|
|
6350
|
+
args: [{ name: "id", description: "Event rule ID", required: true }],
|
|
6351
|
+
examples: ["gr journeys steps actions get -w $WID --id <rule-id>"],
|
|
6352
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
6353
|
+
const id = requireNumber(args, "id");
|
|
6354
|
+
const result = await requireAuth(ctx).apiClient.getEventRule(workspace, id);
|
|
6355
|
+
outputSingle(result, ctx, ACTION_COLUMNS);
|
|
6356
|
+
}
|
|
6357
|
+
},
|
|
6358
|
+
{
|
|
6359
|
+
path: ["journeys", "steps", "actions", "update"],
|
|
6360
|
+
description: "Update a journey step action \u2014 modify template, conditions, or active state",
|
|
6361
|
+
mutates: true,
|
|
6362
|
+
requiresWorkspace: true,
|
|
6363
|
+
args: [{ name: "id", description: "Event rule ID", required: true }],
|
|
6364
|
+
examples: [
|
|
6365
|
+
"gr journeys steps actions update -w $WID --id <rule-id> \\",
|
|
6366
|
+
` --data '{"actionParams":{"messageTemplate":"Hello {{member.name}}"}}' --allow-write`,
|
|
6367
|
+
"",
|
|
6368
|
+
"Updatable fields: name, conditions (CEL), actionParams, active, delay, order",
|
|
6369
|
+
"Non-updatable: objectType, eventType, actionType, journeyStepId"
|
|
6370
|
+
],
|
|
6371
|
+
handler: async ({ ctx, workspace, args, data }) => {
|
|
6372
|
+
if (!data) throw new CLIError("JSON data required (--data, --file, or stdin)");
|
|
6373
|
+
const id = requireNumber(args, "id");
|
|
6374
|
+
const result = await requireAuth(ctx).apiClient.updateEventRule(
|
|
6375
|
+
workspace,
|
|
6376
|
+
id,
|
|
6377
|
+
data
|
|
6378
|
+
);
|
|
6379
|
+
outputSingle(result, ctx, ACTION_COLUMNS);
|
|
6380
|
+
}
|
|
6004
6381
|
}
|
|
6005
6382
|
]
|
|
6006
6383
|
};
|
|
@@ -6626,6 +7003,13 @@ var COLUMNS16 = [
|
|
|
6626
7003
|
{ header: "PREFIX", getValue: (item) => item.prefix ?? "" },
|
|
6627
7004
|
COL.created()
|
|
6628
7005
|
];
|
|
7006
|
+
var CREATE_COLUMNS = [
|
|
7007
|
+
COL.id(),
|
|
7008
|
+
COL.name(),
|
|
7009
|
+
{ header: "KEY", getValue: (item) => item.key ?? "" },
|
|
7010
|
+
{ header: "PREFIX", getValue: (item) => item.prefix ?? "" },
|
|
7011
|
+
COL.created()
|
|
7012
|
+
];
|
|
6629
7013
|
var apiKeysDomain = {
|
|
6630
7014
|
name: "api-keys",
|
|
6631
7015
|
description: "Manage API keys",
|
|
@@ -6646,7 +7030,10 @@ var apiKeysDomain = {
|
|
|
6646
7030
|
requiresWorkspace: true,
|
|
6647
7031
|
handler: async ({ ctx, workspace, data }) => {
|
|
6648
7032
|
const result = await requireAuth(ctx).apiClient.createApiKey(workspace, data);
|
|
6649
|
-
outputSingle(result, ctx,
|
|
7033
|
+
outputSingle(result, ctx, CREATE_COLUMNS);
|
|
7034
|
+
if (ctx.outputFormat === "table") {
|
|
7035
|
+
process.stdout.write("\n\u26A0 This key will not be shown again. Copy it now.\n");
|
|
7036
|
+
}
|
|
6650
7037
|
}
|
|
6651
7038
|
},
|
|
6652
7039
|
{
|
|
@@ -6678,7 +7065,10 @@ var apiKeysDomain = {
|
|
|
6678
7065
|
requiresWorkspace: false,
|
|
6679
7066
|
handler: async ({ ctx, data }) => {
|
|
6680
7067
|
const result = await requireAuth(ctx).apiClient.createGlobalApiKey(data);
|
|
6681
|
-
outputSingle(result, ctx,
|
|
7068
|
+
outputSingle(result, ctx, CREATE_COLUMNS);
|
|
7069
|
+
if (ctx.outputFormat === "table") {
|
|
7070
|
+
process.stdout.write("\n\u26A0 This key will not be shown again. Copy it now.\n");
|
|
7071
|
+
}
|
|
6682
7072
|
}
|
|
6683
7073
|
},
|
|
6684
7074
|
{
|
|
@@ -7152,7 +7542,7 @@ var conciergeDomain = {
|
|
|
7152
7542
|
path: ["concierge", "messages"],
|
|
7153
7543
|
description: "Show messages for a concierge chat",
|
|
7154
7544
|
requiresWorkspace: false,
|
|
7155
|
-
args: [{ name: "id", description: "Chat ID", required: true }],
|
|
7545
|
+
args: [{ name: "id", aliases: ["chat-id"], description: "Chat ID", required: true }],
|
|
7156
7546
|
handler: async ({ ctx, args }) => {
|
|
7157
7547
|
const auth = requireAuth(ctx);
|
|
7158
7548
|
const chatId = Number(args.id);
|
|
@@ -7584,7 +7974,7 @@ var milestonesDomain = {
|
|
|
7584
7974
|
var COLUMNS23 = [
|
|
7585
7975
|
COL.id(),
|
|
7586
7976
|
COL.name(),
|
|
7587
|
-
{ header: "TYPE", getValue: (item) => item.
|
|
7977
|
+
{ header: "TYPE", getValue: (item) => item.noticeType ?? "" },
|
|
7588
7978
|
COL.enabled()
|
|
7589
7979
|
];
|
|
7590
7980
|
var notificationRulesDomain = {
|
|
@@ -7785,15 +8175,15 @@ var DOMAIN_UUID_ARG = {
|
|
|
7785
8175
|
function pluralize(count, singular, plural = `${singular}s`) {
|
|
7786
8176
|
return count === 1 ? singular : plural;
|
|
7787
8177
|
}
|
|
7788
|
-
function emailVerifyCommand(
|
|
8178
|
+
function emailVerifyCommand(path4, description) {
|
|
7789
8179
|
return {
|
|
7790
|
-
path,
|
|
8180
|
+
path: path4,
|
|
7791
8181
|
description,
|
|
7792
8182
|
mutates: true,
|
|
7793
8183
|
requiresWorkspace: false,
|
|
7794
8184
|
args: [ORG_UUID_ARG, DOMAIN_UUID_ARG],
|
|
7795
8185
|
examples: [
|
|
7796
|
-
`yarn gr ${
|
|
8186
|
+
`yarn gr ${path4.join(" ")} --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write`
|
|
7797
8187
|
],
|
|
7798
8188
|
handler: async (hctx) => {
|
|
7799
8189
|
const result = await requireAuth(hctx.ctx).apiClient.verifyOrgDomainEmail(
|
|
@@ -7804,15 +8194,15 @@ function emailVerifyCommand(path, description) {
|
|
|
7804
8194
|
}
|
|
7805
8195
|
};
|
|
7806
8196
|
}
|
|
7807
|
-
function siteVerifyCommand(
|
|
8197
|
+
function siteVerifyCommand(path4, description) {
|
|
7808
8198
|
return {
|
|
7809
|
-
path,
|
|
8199
|
+
path: path4,
|
|
7810
8200
|
description,
|
|
7811
8201
|
mutates: true,
|
|
7812
8202
|
requiresWorkspace: false,
|
|
7813
8203
|
args: [ORG_UUID_ARG, DOMAIN_UUID_ARG],
|
|
7814
8204
|
examples: [
|
|
7815
|
-
`yarn gr ${
|
|
8205
|
+
`yarn gr ${path4.join(" ")} --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write`
|
|
7816
8206
|
],
|
|
7817
8207
|
handler: async (hctx) => {
|
|
7818
8208
|
const result = await requireAuth(hctx.ctx).apiClient.verifyOrgDomainSite(
|
|
@@ -7823,9 +8213,9 @@ function siteVerifyCommand(path, description) {
|
|
|
7823
8213
|
}
|
|
7824
8214
|
};
|
|
7825
8215
|
}
|
|
7826
|
-
function registerDomainCommand(
|
|
8216
|
+
function registerDomainCommand(path4) {
|
|
7827
8217
|
return {
|
|
7828
|
-
path,
|
|
8218
|
+
path: path4,
|
|
7829
8219
|
description: "Register an organization domain and return ownership TXT instructions",
|
|
7830
8220
|
mutates: true,
|
|
7831
8221
|
requiresWorkspace: false,
|
|
@@ -7834,7 +8224,7 @@ function registerDomainCommand(path) {
|
|
|
7834
8224
|
{ name: "domain", description: "Domain to register, e.g. example.com", required: true }
|
|
7835
8225
|
],
|
|
7836
8226
|
examples: [
|
|
7837
|
-
`yarn gr ${
|
|
8227
|
+
`yarn gr ${path4.join(" ")} --org-uuid $ORG_UUID --domain example.com --allow-write`
|
|
7838
8228
|
],
|
|
7839
8229
|
handler: async (hctx) => {
|
|
7840
8230
|
const result = await requireAuth(hctx.ctx).apiClient.registerOrgDomain(
|
|
@@ -8189,6 +8579,12 @@ var commands2 = [
|
|
|
8189
8579
|
workspaceUuid
|
|
8190
8580
|
);
|
|
8191
8581
|
outputSingle(result, hctx.ctx, PROVIDER_PHONE_COLUMNS);
|
|
8582
|
+
process.stdout.write(
|
|
8583
|
+
`
|
|
8584
|
+
If workspace phone list is still empty, create a workspace phone record then:
|
|
8585
|
+
gr phone-numbers link -w ${workspaceUuid} --id <workspace-phone-id> --provider-phone-uuid ${providerPhoneUuid} --allow-write
|
|
8586
|
+
`
|
|
8587
|
+
);
|
|
8192
8588
|
}
|
|
8193
8589
|
},
|
|
8194
8590
|
{
|
|
@@ -8292,6 +8688,31 @@ var phoneNumbersDomain = {
|
|
|
8292
8688
|
outputData(result, ctx, COLUMNS25);
|
|
8293
8689
|
}
|
|
8294
8690
|
},
|
|
8691
|
+
{
|
|
8692
|
+
path: ["phone-numbers", "link"],
|
|
8693
|
+
description: "Link a workspace phone number to a provider (org) phone",
|
|
8694
|
+
mutates: true,
|
|
8695
|
+
requiresWorkspace: true,
|
|
8696
|
+
args: [
|
|
8697
|
+
{
|
|
8698
|
+
name: "phone-number-id",
|
|
8699
|
+
aliases: ["id"],
|
|
8700
|
+
description: "Workspace phone number ID",
|
|
8701
|
+
required: true
|
|
8702
|
+
},
|
|
8703
|
+
{ name: "provider-phone-uuid", description: "Provider phone UUID", required: true }
|
|
8704
|
+
],
|
|
8705
|
+
examples: [
|
|
8706
|
+
"gr phone-numbers link -w $WID --id 1 --provider-phone-uuid <uuid> --allow-write"
|
|
8707
|
+
],
|
|
8708
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
8709
|
+
const auth = requireAuth(ctx);
|
|
8710
|
+
const id = requireNumberFrom(args, "phone-number-id", "id");
|
|
8711
|
+
const providerPhoneUuid = requireString(args, "provider-phone-uuid");
|
|
8712
|
+
const result = await auth.apiClient.linkPhoneNumber(workspace, id, providerPhoneUuid);
|
|
8713
|
+
outputData([result], auth, COLUMNS25);
|
|
8714
|
+
}
|
|
8715
|
+
},
|
|
8295
8716
|
{
|
|
8296
8717
|
path: ["phone-numbers", "request"],
|
|
8297
8718
|
description: "Request a phone number",
|
|
@@ -8310,7 +8731,12 @@ var phoneNumbersDomain = {
|
|
|
8310
8731
|
description: "List calls for a phone number",
|
|
8311
8732
|
requiresWorkspace: true,
|
|
8312
8733
|
args: [
|
|
8313
|
-
{
|
|
8734
|
+
{
|
|
8735
|
+
name: "phone-number-id",
|
|
8736
|
+
aliases: ["id"],
|
|
8737
|
+
description: "Phone number ID",
|
|
8738
|
+
required: true
|
|
8739
|
+
},
|
|
8314
8740
|
{ name: "page", description: "Page number" },
|
|
8315
8741
|
{ name: "page-size", description: "Page size" },
|
|
8316
8742
|
{ name: "status", description: "Filter by status" }
|
|
@@ -8319,7 +8745,7 @@ var phoneNumbersDomain = {
|
|
|
8319
8745
|
const status = args.status;
|
|
8320
8746
|
const result = await requireAuth(ctx).apiClient.getPhoneNumberCalls(
|
|
8321
8747
|
workspace,
|
|
8322
|
-
|
|
8748
|
+
requireNumberFrom(args, "phone-number-id", "id"),
|
|
8323
8749
|
args.page ? Number(args.page) : void 0,
|
|
8324
8750
|
args["page-size"] ? Number(args["page-size"]) : void 0,
|
|
8325
8751
|
status ? { status } : void 0
|
|
@@ -8332,14 +8758,19 @@ var phoneNumbersDomain = {
|
|
|
8332
8758
|
description: "List messages for a phone number",
|
|
8333
8759
|
requiresWorkspace: true,
|
|
8334
8760
|
args: [
|
|
8335
|
-
{
|
|
8761
|
+
{
|
|
8762
|
+
name: "phone-number-id",
|
|
8763
|
+
aliases: ["id"],
|
|
8764
|
+
description: "Phone number ID",
|
|
8765
|
+
required: true
|
|
8766
|
+
},
|
|
8336
8767
|
{ name: "page", description: "Page number" },
|
|
8337
8768
|
{ name: "page-size", description: "Page size" }
|
|
8338
8769
|
],
|
|
8339
8770
|
handler: async ({ ctx, workspace, args }) => {
|
|
8340
8771
|
const result = await requireAuth(ctx).apiClient.getPhoneNumberMessages(
|
|
8341
8772
|
workspace,
|
|
8342
|
-
|
|
8773
|
+
requireNumberFrom(args, "phone-number-id", "id"),
|
|
8343
8774
|
args.page ? Number(args.page) : void 0,
|
|
8344
8775
|
args["page-size"] ? Number(args["page-size"]) : void 0
|
|
8345
8776
|
);
|
|
@@ -8349,36 +8780,85 @@ var phoneNumbersDomain = {
|
|
|
8349
8780
|
]
|
|
8350
8781
|
};
|
|
8351
8782
|
|
|
8783
|
+
// src/lib/resolve-org.ts
|
|
8784
|
+
async function resolveOrgUuidFromWorkspace(apiClient, workspaceUuid) {
|
|
8785
|
+
const workspace = await apiClient.getWorkspace(workspaceUuid);
|
|
8786
|
+
const orgUuid = workspace.organizationUuid;
|
|
8787
|
+
if (!orgUuid) {
|
|
8788
|
+
throw new CLIError(
|
|
8789
|
+
"Workspace has no organization; pass --org-uuid for organization-scoped commands"
|
|
8790
|
+
);
|
|
8791
|
+
}
|
|
8792
|
+
return orgUuid;
|
|
8793
|
+
}
|
|
8794
|
+
function normalizePronunciationCreatePayload(data) {
|
|
8795
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
8796
|
+
throw new CLIError("JSON body required with term and respelling (--data or --file)");
|
|
8797
|
+
}
|
|
8798
|
+
const raw = data;
|
|
8799
|
+
const term = raw.term ?? raw.word;
|
|
8800
|
+
const respelling = raw.respelling ?? raw.pronunciation;
|
|
8801
|
+
if (typeof term !== "string" || !term) {
|
|
8802
|
+
throw new CLIError("Payload requires term (alias: word)");
|
|
8803
|
+
}
|
|
8804
|
+
if (typeof respelling !== "string" || !respelling) {
|
|
8805
|
+
throw new CLIError("Payload requires respelling (alias: pronunciation)");
|
|
8806
|
+
}
|
|
8807
|
+
const payload = { term, respelling };
|
|
8808
|
+
if (raw.accountId !== void 0 && raw.accountId !== null) {
|
|
8809
|
+
payload.accountId = Number(raw.accountId);
|
|
8810
|
+
}
|
|
8811
|
+
return payload;
|
|
8812
|
+
}
|
|
8813
|
+
function normalizePronunciationUpdatePayload(data) {
|
|
8814
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
8815
|
+
throw new CLIError("JSON body required (--data or --file)");
|
|
8816
|
+
}
|
|
8817
|
+
const raw = data;
|
|
8818
|
+
const respelling = raw.respelling ?? raw.pronunciation;
|
|
8819
|
+
if (typeof respelling !== "string" || !respelling) {
|
|
8820
|
+
throw new CLIError("Payload requires respelling (alias: pronunciation)");
|
|
8821
|
+
}
|
|
8822
|
+
return { respelling };
|
|
8823
|
+
}
|
|
8824
|
+
|
|
8352
8825
|
// src/domains/pronunciations.ts
|
|
8353
8826
|
var COLUMNS26 = [
|
|
8354
8827
|
COL.id(),
|
|
8355
|
-
{ header: "
|
|
8356
|
-
{ header: "
|
|
8357
|
-
{ header: "
|
|
8828
|
+
{ header: "TERM", getValue: (item) => item.term ?? "" },
|
|
8829
|
+
{ header: "RESPELLING", getValue: (item) => item.respelling ?? "" },
|
|
8830
|
+
{ header: "SCOPE", getValue: (item) => item.scope ?? "" }
|
|
8831
|
+
];
|
|
8832
|
+
var CREATE_EXAMPLES = [
|
|
8833
|
+
`gr pronunciations create -w $WID --data '{"term":"Jeeves","respelling":"JEEVZ"}' --allow-write`,
|
|
8834
|
+
" (aliases: word \u2192 term, pronunciation \u2192 respelling)"
|
|
8358
8835
|
];
|
|
8359
8836
|
var pronunciationsDomain = {
|
|
8360
8837
|
name: "pronunciations",
|
|
8361
|
-
description: "Manage pronunciation entries",
|
|
8838
|
+
description: "Manage pronunciation entries (org-scoped; -w resolves organization)",
|
|
8362
8839
|
commands: [
|
|
8363
8840
|
{
|
|
8364
8841
|
path: ["pronunciations", "list"],
|
|
8365
|
-
description: "List pronunciation entries",
|
|
8842
|
+
description: "List pronunciation entries for the workspace organization",
|
|
8366
8843
|
requiresWorkspace: true,
|
|
8367
8844
|
handler: async ({ ctx, workspace }) => {
|
|
8368
|
-
const
|
|
8845
|
+
const auth = requireAuth(ctx);
|
|
8846
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
8847
|
+
const result = await auth.apiClient.getPronunciationEntries(orgUuid);
|
|
8369
8848
|
outputData(result, ctx, COLUMNS26);
|
|
8370
8849
|
}
|
|
8371
8850
|
},
|
|
8372
8851
|
{
|
|
8373
8852
|
path: ["pronunciations", "create"],
|
|
8374
|
-
description: "Create a pronunciation entry",
|
|
8853
|
+
description: "Create a pronunciation entry (term + respelling)",
|
|
8375
8854
|
mutates: true,
|
|
8376
8855
|
requiresWorkspace: true,
|
|
8856
|
+
examples: CREATE_EXAMPLES,
|
|
8377
8857
|
handler: async ({ ctx, workspace, data }) => {
|
|
8378
|
-
const
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
);
|
|
8858
|
+
const auth = requireAuth(ctx);
|
|
8859
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
8860
|
+
const payload = normalizePronunciationCreatePayload(data);
|
|
8861
|
+
const result = await auth.apiClient.createPronunciationEntry(orgUuid, payload);
|
|
8382
8862
|
outputSingle(result, ctx, COLUMNS26);
|
|
8383
8863
|
}
|
|
8384
8864
|
},
|
|
@@ -8389,10 +8869,13 @@ var pronunciationsDomain = {
|
|
|
8389
8869
|
requiresWorkspace: true,
|
|
8390
8870
|
args: [{ name: "id", description: "Pronunciation entry ID", required: true }],
|
|
8391
8871
|
handler: async ({ ctx, workspace, args, data }) => {
|
|
8392
|
-
const
|
|
8393
|
-
|
|
8872
|
+
const auth = requireAuth(ctx);
|
|
8873
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
8874
|
+
const payload = normalizePronunciationUpdatePayload(data);
|
|
8875
|
+
const result = await auth.apiClient.updatePronunciationEntry(
|
|
8876
|
+
orgUuid,
|
|
8394
8877
|
requireNumber(args, "id"),
|
|
8395
|
-
|
|
8878
|
+
payload
|
|
8396
8879
|
);
|
|
8397
8880
|
outputSingle(result, ctx, COLUMNS26);
|
|
8398
8881
|
}
|
|
@@ -8404,8 +8887,10 @@ var pronunciationsDomain = {
|
|
|
8404
8887
|
requiresWorkspace: true,
|
|
8405
8888
|
args: [{ name: "id", description: "Pronunciation entry ID", required: true }],
|
|
8406
8889
|
handler: async ({ ctx, workspace, args }) => {
|
|
8890
|
+
const auth = requireAuth(ctx);
|
|
8891
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
8407
8892
|
const id = requireNumber(args, "id");
|
|
8408
|
-
await
|
|
8893
|
+
await auth.apiClient.deletePronunciationEntry(orgUuid, id);
|
|
8409
8894
|
process.stdout.write(`Deleted pronunciation entry ${id}
|
|
8410
8895
|
`);
|
|
8411
8896
|
}
|
|
@@ -8425,7 +8910,8 @@ var pronunciationsDomain = {
|
|
|
8425
8910
|
mutates: true,
|
|
8426
8911
|
requiresWorkspace: true,
|
|
8427
8912
|
handler: async ({ ctx, data }) => {
|
|
8428
|
-
const
|
|
8913
|
+
const payload = normalizePronunciationCreatePayload(data);
|
|
8914
|
+
const result = await requireAuth(ctx).apiClient.createMyPronunciation(payload);
|
|
8429
8915
|
outputSingle(result, ctx, COLUMNS26);
|
|
8430
8916
|
}
|
|
8431
8917
|
},
|
|
@@ -8433,11 +8919,17 @@ var pronunciationsDomain = {
|
|
|
8433
8919
|
path: ["pronunciations", "suggest"],
|
|
8434
8920
|
description: "Get pronunciation suggestions for a word",
|
|
8435
8921
|
requiresWorkspace: true,
|
|
8436
|
-
args: [
|
|
8922
|
+
args: [
|
|
8923
|
+
{
|
|
8924
|
+
name: "word",
|
|
8925
|
+
aliases: ["term"],
|
|
8926
|
+
description: "Word to get suggestions for",
|
|
8927
|
+
required: true
|
|
8928
|
+
}
|
|
8929
|
+
],
|
|
8437
8930
|
handler: async ({ ctx, args }) => {
|
|
8438
|
-
const
|
|
8439
|
-
|
|
8440
|
-
);
|
|
8931
|
+
const word = typeof args.word === "string" ? args.word : typeof args.term === "string" ? args.term : requireString(args, "word");
|
|
8932
|
+
const result = await requireAuth(ctx).apiClient.suggestPronunciations(word);
|
|
8441
8933
|
outputData(result, ctx);
|
|
8442
8934
|
}
|
|
8443
8935
|
},
|
|
@@ -8603,20 +9095,20 @@ var promptsDomain = {
|
|
|
8603
9095
|
"gr prompts inspect --chat-id 789 --turn 3"
|
|
8604
9096
|
],
|
|
8605
9097
|
handler: async ({ ctx, workspace, args }) => {
|
|
8606
|
-
const taskId = args["task-id"] ? requireNumber(args, "task-id") : void 0;
|
|
8607
|
-
const chatId = args["chat-id"] ? requireNumber(args, "chat-id") : void 0;
|
|
8608
|
-
if (
|
|
9098
|
+
const taskId = args["task-id"] !== void 0 ? requireNumber(args, "task-id") : void 0;
|
|
9099
|
+
const chatId = args["chat-id"] !== void 0 ? requireNumber(args, "chat-id") : void 0;
|
|
9100
|
+
if (taskId === void 0 && chatId === void 0) {
|
|
8609
9101
|
throw new Error("Provide --task-id or --chat-id");
|
|
8610
9102
|
}
|
|
8611
|
-
if (taskId && chatId) {
|
|
9103
|
+
if (taskId !== void 0 && chatId !== void 0) {
|
|
8612
9104
|
throw new Error("Provide --task-id or --chat-id, not both");
|
|
8613
9105
|
}
|
|
8614
9106
|
const channel = args.channel || void 0;
|
|
8615
|
-
const turn = args.turn ? requireNumber(args, "turn") : void 0;
|
|
8616
|
-
const result = await requireAuth(ctx).apiClient.inspectPrompt(workspace,
|
|
8617
|
-
|
|
8618
|
-
...
|
|
8619
|
-
...turn ? { turn } : {}
|
|
9107
|
+
const turn = args.turn !== void 0 ? requireNumber(args, "turn") : void 0;
|
|
9108
|
+
const result = await requireAuth(ctx).apiClient.inspectPrompt(workspace, {
|
|
9109
|
+
...taskId !== void 0 ? { taskId } : { chatId },
|
|
9110
|
+
...channel ? { channel } : {},
|
|
9111
|
+
...turn !== void 0 ? { turn } : {}
|
|
8620
9112
|
});
|
|
8621
9113
|
outputSingle(result, ctx);
|
|
8622
9114
|
}
|
|
@@ -8624,6 +9116,128 @@ var promptsDomain = {
|
|
|
8624
9116
|
]
|
|
8625
9117
|
};
|
|
8626
9118
|
|
|
9119
|
+
// src/domains/support.ts
|
|
9120
|
+
var SUPPORT_COLUMNS = [
|
|
9121
|
+
COL.uuid(),
|
|
9122
|
+
{ header: "STATUS", getValue: (item) => item.status ?? "" },
|
|
9123
|
+
{ header: "TYPE", getValue: (item) => item.supportRequestType ?? "" },
|
|
9124
|
+
{ header: "SEVERITY", getValue: (item) => item.severity ?? "" },
|
|
9125
|
+
{ header: "SOURCE", getValue: (item) => item.source ?? "" },
|
|
9126
|
+
{ header: "CREATED", getValue: (item) => (item.createdAt ?? "").slice(0, 19) },
|
|
9127
|
+
{ header: "TITLE", getValue: (item) => item.title ?? "" }
|
|
9128
|
+
];
|
|
9129
|
+
var CONTEXT_ARGS = [
|
|
9130
|
+
{ name: "workspace", aliases: ["w"], description: "Workspace UUID" },
|
|
9131
|
+
{ name: "org-uuid", description: "Organization UUID" }
|
|
9132
|
+
];
|
|
9133
|
+
function listParams(args) {
|
|
9134
|
+
return {
|
|
9135
|
+
page: args.page ? Number(args.page) : void 0,
|
|
9136
|
+
pageSize: args["page-size"] ? Number(args["page-size"]) : void 0,
|
|
9137
|
+
status: args.status,
|
|
9138
|
+
supportRequestType: args.type,
|
|
9139
|
+
severity: args.severity,
|
|
9140
|
+
source: args.source
|
|
9141
|
+
};
|
|
9142
|
+
}
|
|
9143
|
+
function requestPayload(data, args) {
|
|
9144
|
+
const input = data ?? {};
|
|
9145
|
+
return {
|
|
9146
|
+
supportRequestType: input.supportRequestType ?? input.support_request_type ?? args.type ?? "question",
|
|
9147
|
+
title: input.title ?? requireString(args, "title"),
|
|
9148
|
+
description: input.description ?? requireString(args, "description"),
|
|
9149
|
+
severity: input.severity ?? args.severity
|
|
9150
|
+
};
|
|
9151
|
+
}
|
|
9152
|
+
function commentPayload(data, args) {
|
|
9153
|
+
const input = data ?? {};
|
|
9154
|
+
return {
|
|
9155
|
+
body: input.body ?? requireString(args, "body")
|
|
9156
|
+
};
|
|
9157
|
+
}
|
|
9158
|
+
var supportDomain = {
|
|
9159
|
+
name: "support",
|
|
9160
|
+
description: "Manage support requests",
|
|
9161
|
+
commands: [
|
|
9162
|
+
{
|
|
9163
|
+
path: ["support", "list"],
|
|
9164
|
+
description: "List support requests",
|
|
9165
|
+
args: [
|
|
9166
|
+
...CONTEXT_ARGS,
|
|
9167
|
+
{ name: "status", description: "Filter by status" },
|
|
9168
|
+
{ name: "type", description: "Filter by type: bug, feature_request, question" },
|
|
9169
|
+
{ name: "severity", description: "Filter by severity" },
|
|
9170
|
+
{ name: "source", description: "Filter by source" },
|
|
9171
|
+
{ name: "page", description: "Page number" },
|
|
9172
|
+
{ name: "page-size", description: "Page size" }
|
|
9173
|
+
],
|
|
9174
|
+
columns: SUPPORT_COLUMNS,
|
|
9175
|
+
handler: async ({ ctx, args, workspace }) => {
|
|
9176
|
+
const client = requireAuth(ctx).apiClient;
|
|
9177
|
+
const wid = args.workspace ?? workspace;
|
|
9178
|
+
const orgUuid = args["org-uuid"];
|
|
9179
|
+
const result = wid ? await client.listWorkspaceSupportRequests(wid, listParams(args)) : orgUuid ? await client.listOrgSupportRequests(orgUuid, listParams(args)) : await client.listSupportRequests(listParams(args));
|
|
9180
|
+
outputData(result, ctx, SUPPORT_COLUMNS);
|
|
9181
|
+
}
|
|
9182
|
+
},
|
|
9183
|
+
{
|
|
9184
|
+
path: ["support", "get"],
|
|
9185
|
+
description: "Get a support request",
|
|
9186
|
+
args: [
|
|
9187
|
+
...CONTEXT_ARGS,
|
|
9188
|
+
{ name: "id", aliases: ["uuid"], description: "Support request UUID", required: true }
|
|
9189
|
+
],
|
|
9190
|
+
handler: async ({ ctx, args, workspace }) => {
|
|
9191
|
+
const client = requireAuth(ctx).apiClient;
|
|
9192
|
+
const id = requireString(args, "id");
|
|
9193
|
+
const wid = args.workspace ?? workspace;
|
|
9194
|
+
const orgUuid = args["org-uuid"];
|
|
9195
|
+
const result = wid ? await client.getWorkspaceSupportRequest(wid, id) : orgUuid ? await client.getOrgSupportRequest(orgUuid, id) : await client.getSupportRequest(id);
|
|
9196
|
+
outputSingle(result, ctx, SUPPORT_COLUMNS);
|
|
9197
|
+
}
|
|
9198
|
+
},
|
|
9199
|
+
{
|
|
9200
|
+
path: ["support", "create"],
|
|
9201
|
+
description: "Create a support request",
|
|
9202
|
+
mutates: true,
|
|
9203
|
+
args: [
|
|
9204
|
+
...CONTEXT_ARGS,
|
|
9205
|
+
{ name: "type", description: "Request type: bug, feature_request, question" },
|
|
9206
|
+
{ name: "title", description: "Support request title" },
|
|
9207
|
+
{ name: "description", description: "Support request description" },
|
|
9208
|
+
{ name: "severity", description: "Optional severity" }
|
|
9209
|
+
],
|
|
9210
|
+
handler: async ({ ctx, args, workspace, data }) => {
|
|
9211
|
+
const client = requireAuth(ctx).apiClient;
|
|
9212
|
+
const wid = args.workspace ?? workspace;
|
|
9213
|
+
const orgUuid = args["org-uuid"];
|
|
9214
|
+
const payload = requestPayload(data, args);
|
|
9215
|
+
const result = wid ? await client.createWorkspaceSupportRequest(wid, payload) : orgUuid ? await client.createOrgSupportRequest(orgUuid, payload) : await client.createSupportRequest(payload);
|
|
9216
|
+
outputSingle(result, ctx, SUPPORT_COLUMNS);
|
|
9217
|
+
}
|
|
9218
|
+
},
|
|
9219
|
+
{
|
|
9220
|
+
path: ["support", "comment"],
|
|
9221
|
+
description: "Add a customer comment to a support request",
|
|
9222
|
+
mutates: true,
|
|
9223
|
+
args: [
|
|
9224
|
+
...CONTEXT_ARGS,
|
|
9225
|
+
{ name: "id", aliases: ["uuid"], description: "Support request UUID", required: true },
|
|
9226
|
+
{ name: "body", description: "Comment body" }
|
|
9227
|
+
],
|
|
9228
|
+
handler: async ({ ctx, args, workspace, data }) => {
|
|
9229
|
+
const client = requireAuth(ctx).apiClient;
|
|
9230
|
+
const id = requireString(args, "id");
|
|
9231
|
+
const wid = args.workspace ?? workspace;
|
|
9232
|
+
const orgUuid = args["org-uuid"];
|
|
9233
|
+
const payload = commentPayload(data, args);
|
|
9234
|
+
const result = wid ? await client.commentOnWorkspaceSupportRequest(wid, id, payload) : orgUuid ? await client.commentOnOrgSupportRequest(orgUuid, id, payload) : await client.commentOnSupportRequest(id, payload);
|
|
9235
|
+
outputSingle(result, ctx, SUPPORT_COLUMNS);
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
]
|
|
9239
|
+
};
|
|
9240
|
+
|
|
8627
9241
|
// src/domains/supervisors.ts
|
|
8628
9242
|
var COLUMNS28 = [
|
|
8629
9243
|
COL.id(),
|
|
@@ -8740,6 +9354,10 @@ var syncDomain = {
|
|
|
8740
9354
|
{
|
|
8741
9355
|
name: "board-id",
|
|
8742
9356
|
description: "Scope to a single board mapping (requires --entity-type)"
|
|
9357
|
+
},
|
|
9358
|
+
{
|
|
9359
|
+
name: "record-limit",
|
|
9360
|
+
description: "Cap the run to a preview sample (1..200). Requires --dry-run and --entity-type."
|
|
8743
9361
|
}
|
|
8744
9362
|
],
|
|
8745
9363
|
examples: [
|
|
@@ -8748,7 +9366,8 @@ var syncDomain = {
|
|
|
8748
9366
|
"sync run --connection-id 6 --full-resync",
|
|
8749
9367
|
"sync run --connection-id 6 --quiet",
|
|
8750
9368
|
"sync run --connection-id 6 --entity-type member",
|
|
8751
|
-
"sync run --connection-id 6 --entity-type member --board-id 18406694561"
|
|
9369
|
+
"sync run --connection-id 6 --entity-type member --board-id 18406694561",
|
|
9370
|
+
"sync run --connection-id 6 --dry-run --entity-type member --record-limit 20"
|
|
8752
9371
|
],
|
|
8753
9372
|
mutates: true,
|
|
8754
9373
|
handler: async ({ ctx, args, workspace }) => {
|
|
@@ -8768,12 +9387,39 @@ var syncDomain = {
|
|
|
8768
9387
|
entityType = rawEntityType;
|
|
8769
9388
|
}
|
|
8770
9389
|
const boardId = args["board-id"];
|
|
9390
|
+
const rawRecordLimit = args["record-limit"];
|
|
9391
|
+
let recordLimit;
|
|
9392
|
+
if (rawRecordLimit !== void 0 && rawRecordLimit !== "") {
|
|
9393
|
+
const parsed = Number(rawRecordLimit);
|
|
9394
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 200) {
|
|
9395
|
+
throw new CLIError(
|
|
9396
|
+
`Invalid --record-limit "${rawRecordLimit}". Must be an integer between 1 and 200.`
|
|
9397
|
+
);
|
|
9398
|
+
}
|
|
9399
|
+
if (!dryRun) {
|
|
9400
|
+
throw new CLIError(
|
|
9401
|
+
"--record-limit requires --dry-run: capped runs are preview-only."
|
|
9402
|
+
);
|
|
9403
|
+
}
|
|
9404
|
+
if (entityType === void 0) {
|
|
9405
|
+
throw new CLIError(
|
|
9406
|
+
"--record-limit requires --entity-type: capped previews must target a single entity type."
|
|
9407
|
+
);
|
|
9408
|
+
}
|
|
9409
|
+
recordLimit = parsed;
|
|
9410
|
+
}
|
|
9411
|
+
if (fullResync && (dryRun || recordLimit !== void 0)) {
|
|
9412
|
+
throw new CLIError(
|
|
9413
|
+
"--full-resync is incompatible with --dry-run / --record-limit: checkpoint reset must happen on a non-preview run."
|
|
9414
|
+
);
|
|
9415
|
+
}
|
|
8771
9416
|
if (!quiet) {
|
|
8772
9417
|
const flags = [
|
|
8773
9418
|
dryRun && "(dry run)",
|
|
8774
9419
|
fullResync && "(full re-sync)",
|
|
8775
9420
|
entityType && `(${entityType} only)`,
|
|
8776
|
-
boardId && `(board ${boardId})
|
|
9421
|
+
boardId && `(board ${boardId})`,
|
|
9422
|
+
recordLimit !== void 0 && `(limit ${recordLimit})`
|
|
8777
9423
|
].filter(Boolean).join(" ");
|
|
8778
9424
|
console.error(
|
|
8779
9425
|
`Starting sync for connection ${connectionId}${flags ? ` ${flags}` : ""}...`
|
|
@@ -8786,7 +9432,8 @@ var syncDomain = {
|
|
|
8786
9432
|
dryRun,
|
|
8787
9433
|
resetCheckpoints: fullResync,
|
|
8788
9434
|
entityType,
|
|
8789
|
-
boardId
|
|
9435
|
+
boardId,
|
|
9436
|
+
recordLimit
|
|
8790
9437
|
}
|
|
8791
9438
|
);
|
|
8792
9439
|
if (!response.ok) {
|
|
@@ -8980,6 +9627,31 @@ var appConnectionsDomain = {
|
|
|
8980
9627
|
);
|
|
8981
9628
|
outputSingle(result, ctx);
|
|
8982
9629
|
}
|
|
9630
|
+
},
|
|
9631
|
+
{
|
|
9632
|
+
path: ["app-connections", "relink-member"],
|
|
9633
|
+
description: "Re-enable sync for a previously unlinked member by clearing the manual-unlink exclusion on this connection. Idempotent: returns cleared=false when no exclusion exists.",
|
|
9634
|
+
mutates: true,
|
|
9635
|
+
requiresWorkspace: true,
|
|
9636
|
+
args: [
|
|
9637
|
+
{ name: "id", description: "Connection ID (numeric)", required: true },
|
|
9638
|
+
{ name: "member-id", description: "Member ID (numeric)", required: true },
|
|
9639
|
+
{
|
|
9640
|
+
name: "board-id",
|
|
9641
|
+
description: "Optional Monday board ID. Required for Monday connections where exclusions are board-scoped; omit for single-scope connectors."
|
|
9642
|
+
}
|
|
9643
|
+
],
|
|
9644
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
9645
|
+
const boardIdRaw = args["board-id"];
|
|
9646
|
+
const options = boardIdRaw !== void 0 && boardIdRaw !== "" ? { boardId: String(boardIdRaw) } : void 0;
|
|
9647
|
+
const result = await requireAuth(ctx).apiClient.clearConnectionMemberExclusion(
|
|
9648
|
+
workspace,
|
|
9649
|
+
requireNumber(args, "id"),
|
|
9650
|
+
requireNumber(args, "member-id"),
|
|
9651
|
+
options
|
|
9652
|
+
);
|
|
9653
|
+
outputSingle(result, ctx);
|
|
9654
|
+
}
|
|
8983
9655
|
}
|
|
8984
9656
|
]
|
|
8985
9657
|
};
|
|
@@ -9672,49 +10344,6 @@ var mondayDomain = {
|
|
|
9672
10344
|
outputData(result, ctx);
|
|
9673
10345
|
}
|
|
9674
10346
|
},
|
|
9675
|
-
{
|
|
9676
|
-
path: ["monday", "webhook-get"],
|
|
9677
|
-
description: "Get a Monday.com webhook",
|
|
9678
|
-
requiresWorkspace: true,
|
|
9679
|
-
args: [
|
|
9680
|
-
{ name: "board-id", description: "Board ID", required: true },
|
|
9681
|
-
{ name: "id", description: "Webhook ID", required: true }
|
|
9682
|
-
],
|
|
9683
|
-
handler: async ({ ctx, args, workspace }) => {
|
|
9684
|
-
const result = await requireAuth(ctx).apiClient.getMondayWebhook(
|
|
9685
|
-
workspace,
|
|
9686
|
-
requireString(args, "board-id"),
|
|
9687
|
-
requireString(args, "id")
|
|
9688
|
-
);
|
|
9689
|
-
outputSingle(result, ctx);
|
|
9690
|
-
}
|
|
9691
|
-
},
|
|
9692
|
-
{
|
|
9693
|
-
path: ["monday", "webhook-create"],
|
|
9694
|
-
description: "Create a Monday.com webhook",
|
|
9695
|
-
mutates: true,
|
|
9696
|
-
requiresWorkspace: true,
|
|
9697
|
-
handler: async ({ ctx, data, workspace }) => {
|
|
9698
|
-
const result = await requireAuth(ctx).apiClient.createMondayWebhook(
|
|
9699
|
-
workspace,
|
|
9700
|
-
data
|
|
9701
|
-
);
|
|
9702
|
-
outputSingle(result, ctx);
|
|
9703
|
-
}
|
|
9704
|
-
},
|
|
9705
|
-
{
|
|
9706
|
-
path: ["monday", "webhook-delete"],
|
|
9707
|
-
description: "Delete a Monday.com webhook",
|
|
9708
|
-
mutates: true,
|
|
9709
|
-
requiresWorkspace: true,
|
|
9710
|
-
args: [{ name: "id", description: "Webhook ID", required: true }],
|
|
9711
|
-
handler: async ({ ctx, args, workspace }) => {
|
|
9712
|
-
const id = requireString(args, "id");
|
|
9713
|
-
await requireAuth(ctx).apiClient.deleteMondayWebhook(workspace, id);
|
|
9714
|
-
process.stdout.write(`Deleted Monday webhook ${id}
|
|
9715
|
-
`);
|
|
9716
|
-
}
|
|
9717
|
-
},
|
|
9718
10347
|
{
|
|
9719
10348
|
path: ["monday", "user-info"],
|
|
9720
10349
|
description: "Get current Monday.com user info",
|
|
@@ -10054,6 +10683,113 @@ var slackAppsDomain = {
|
|
|
10054
10683
|
]
|
|
10055
10684
|
};
|
|
10056
10685
|
|
|
10686
|
+
// src/domains/integrations/teams-apps.ts
|
|
10687
|
+
var COLUMNS33 = [COL.id(), COL.name(), COL.status()];
|
|
10688
|
+
function addTeamsUrls(data, apiUrl, workspaceUuid) {
|
|
10689
|
+
const base = `${apiUrl}/api/v2/w/${workspaceUuid}/app-connections/teams`;
|
|
10690
|
+
return {
|
|
10691
|
+
...data,
|
|
10692
|
+
_urls: {
|
|
10693
|
+
eventsEndpoint: `${base}/events`,
|
|
10694
|
+
oauthUserAuthorize: `${base}/oauth/user/authorize?app_id=${data.id ?? ""}`,
|
|
10695
|
+
oauthUserCallback: `${base}/oauth/user/callback`
|
|
10696
|
+
}
|
|
10697
|
+
};
|
|
10698
|
+
}
|
|
10699
|
+
var teamsAppsDomain = {
|
|
10700
|
+
name: "teams-apps",
|
|
10701
|
+
description: "Manage Microsoft Teams app integrations",
|
|
10702
|
+
commands: [
|
|
10703
|
+
...crudCommands({
|
|
10704
|
+
prefix: ["teams-apps"],
|
|
10705
|
+
entityName: "Teams app",
|
|
10706
|
+
listMethod: "getTeamsApps",
|
|
10707
|
+
deleteMethod: "deleteTeamsApp",
|
|
10708
|
+
columns: COLUMNS33,
|
|
10709
|
+
paginationSupport: "full"
|
|
10710
|
+
}),
|
|
10711
|
+
{
|
|
10712
|
+
path: ["teams-apps", "get"],
|
|
10713
|
+
description: "Get a Teams app by ID",
|
|
10714
|
+
args: [{ name: "id", description: "Teams app ID", required: true }],
|
|
10715
|
+
requiresWorkspace: true,
|
|
10716
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
10717
|
+
const result = await requireAuth(ctx).apiClient.getTeamsApp(
|
|
10718
|
+
workspace,
|
|
10719
|
+
requireNumber(args, "id")
|
|
10720
|
+
);
|
|
10721
|
+
outputSingle(addTeamsUrls(result, ctx.envConfig.apiUrl, workspace), ctx);
|
|
10722
|
+
}
|
|
10723
|
+
},
|
|
10724
|
+
{
|
|
10725
|
+
path: ["teams-apps", "create"],
|
|
10726
|
+
description: "Create a Teams app",
|
|
10727
|
+
mutates: true,
|
|
10728
|
+
requiresWorkspace: true,
|
|
10729
|
+
handler: async ({ ctx, workspace, data }) => {
|
|
10730
|
+
if (!data) throw new CLIError("JSON data required (--data, --file, or stdin)");
|
|
10731
|
+
const result = await requireAuth(ctx).apiClient.createTeamsApp(workspace, data);
|
|
10732
|
+
outputSingle(addTeamsUrls(result, ctx.envConfig.apiUrl, workspace), ctx);
|
|
10733
|
+
}
|
|
10734
|
+
},
|
|
10735
|
+
{
|
|
10736
|
+
path: ["teams-apps", "update"],
|
|
10737
|
+
description: "Update a Teams app",
|
|
10738
|
+
mutates: true,
|
|
10739
|
+
args: [{ name: "id", description: "Teams app ID", required: true }],
|
|
10740
|
+
requiresWorkspace: true,
|
|
10741
|
+
handler: async ({ ctx, workspace, args, data }) => {
|
|
10742
|
+
if (!data) throw new CLIError("JSON data required (--data, --file, or stdin)");
|
|
10743
|
+
const result = await requireAuth(ctx).apiClient.updateTeamsApp(
|
|
10744
|
+
workspace,
|
|
10745
|
+
requireNumber(args, "id"),
|
|
10746
|
+
data
|
|
10747
|
+
);
|
|
10748
|
+
outputSingle(addTeamsUrls(result, ctx.envConfig.apiUrl, workspace), ctx);
|
|
10749
|
+
}
|
|
10750
|
+
},
|
|
10751
|
+
{
|
|
10752
|
+
path: ["teams-apps", "conversations"],
|
|
10753
|
+
description: "List Teams conversations linked to chats",
|
|
10754
|
+
requiresWorkspace: true,
|
|
10755
|
+
args: [{ name: "id", description: "Teams app ID", required: true }],
|
|
10756
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
10757
|
+
const result = await requireAuth(ctx).apiClient.getTeamsConversations(
|
|
10758
|
+
workspace,
|
|
10759
|
+
requireNumber(args, "id")
|
|
10760
|
+
);
|
|
10761
|
+
outputData(result, ctx);
|
|
10762
|
+
}
|
|
10763
|
+
},
|
|
10764
|
+
{
|
|
10765
|
+
path: ["teams-apps", "installations"],
|
|
10766
|
+
description: "List per-tenant Teams installations",
|
|
10767
|
+
requiresWorkspace: true,
|
|
10768
|
+
args: [{ name: "id", description: "Teams app ID", required: true }],
|
|
10769
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
10770
|
+
const result = await requireAuth(ctx).apiClient.getTeamsInstallations(
|
|
10771
|
+
workspace,
|
|
10772
|
+
requireNumber(args, "id")
|
|
10773
|
+
);
|
|
10774
|
+
outputData(result, ctx);
|
|
10775
|
+
}
|
|
10776
|
+
},
|
|
10777
|
+
{
|
|
10778
|
+
path: ["teams-apps", "accounts"],
|
|
10779
|
+
description: "List linked Teams (Entra) accounts for this app",
|
|
10780
|
+
requiresWorkspace: true,
|
|
10781
|
+
args: [{ name: "id", description: "Teams app ID", required: true }],
|
|
10782
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
10783
|
+
const result = await requireAuth(ctx).apiClient.getTeamsAppAccounts(
|
|
10784
|
+
workspace,
|
|
10785
|
+
requireNumber(args, "id")
|
|
10786
|
+
);
|
|
10787
|
+
outputData(result, ctx);
|
|
10788
|
+
}
|
|
10789
|
+
}
|
|
10790
|
+
]
|
|
10791
|
+
};
|
|
10792
|
+
|
|
10057
10793
|
// src/domains/index.ts
|
|
10058
10794
|
function registerPublicDomains(registry) {
|
|
10059
10795
|
registry.register(tasksDomain);
|
|
@@ -10085,6 +10821,7 @@ function registerPublicDomains(registry) {
|
|
|
10085
10821
|
registry.register(mcpServersDomain);
|
|
10086
10822
|
registry.register(apiKeysDomain);
|
|
10087
10823
|
registry.register(subscriptionsDomain);
|
|
10824
|
+
registry.register(supportDomain);
|
|
10088
10825
|
registry.register(reportsDomain);
|
|
10089
10826
|
registry.register(aiModelsDomain);
|
|
10090
10827
|
registry.register(featuresDomain);
|
|
@@ -10100,6 +10837,7 @@ function registerPublicDomains(registry) {
|
|
|
10100
10837
|
registry.register(confluenceDomain);
|
|
10101
10838
|
registry.register(discordBotsDomain);
|
|
10102
10839
|
registry.register(slackAppsDomain);
|
|
10840
|
+
registry.register(teamsAppsDomain);
|
|
10103
10841
|
registry.register(fhirConnectionsDomain);
|
|
10104
10842
|
registry.register(appConnectionsDomain);
|
|
10105
10843
|
registry.register(hubspotDomain);
|
|
@@ -10109,7 +10847,7 @@ function registerPublicDomains(registry) {
|
|
|
10109
10847
|
}
|
|
10110
10848
|
|
|
10111
10849
|
// src/commands/auth-browser.ts
|
|
10112
|
-
import { execFile } from "child_process";
|
|
10850
|
+
import { execFile as execFile2 } from "child_process";
|
|
10113
10851
|
async function authBrowserCommand(ctx, args) {
|
|
10114
10852
|
const noOpen = Boolean(args["no-open"]);
|
|
10115
10853
|
const account = await ctx.apiClient.getUserAccount();
|
|
@@ -10145,15 +10883,363 @@ function openUrl(url) {
|
|
|
10145
10883
|
command = "xdg-open";
|
|
10146
10884
|
args = [url];
|
|
10147
10885
|
}
|
|
10148
|
-
|
|
10886
|
+
execFile2(command, args, (err) => {
|
|
10149
10887
|
if (err) {
|
|
10150
10888
|
process.stderr.write("Warning: Could not open browser automatically\n");
|
|
10151
10889
|
}
|
|
10152
10890
|
});
|
|
10153
10891
|
}
|
|
10154
10892
|
|
|
10893
|
+
// src/lib/cli-client-context.ts
|
|
10894
|
+
import path from "path";
|
|
10895
|
+
function buildCliConciergeAppContext() {
|
|
10896
|
+
const shell = process.env.SHELL ? path.basename(process.env.SHELL) : void 0;
|
|
10897
|
+
return {
|
|
10898
|
+
clientSurface: "cli",
|
|
10899
|
+
channel: "text",
|
|
10900
|
+
cliContext: {
|
|
10901
|
+
shell,
|
|
10902
|
+
os: process.platform,
|
|
10903
|
+
cwdBasename: path.basename(process.cwd())
|
|
10904
|
+
}
|
|
10905
|
+
};
|
|
10906
|
+
}
|
|
10907
|
+
|
|
10908
|
+
// src/lib/cli-hitl.ts
|
|
10909
|
+
import readline2 from "readline";
|
|
10910
|
+
|
|
10911
|
+
// src/lib/local-files.ts
|
|
10912
|
+
import fs from "fs";
|
|
10913
|
+
import path2 from "path";
|
|
10914
|
+
var DEFAULT_MAX_READ_BYTES = 5e4;
|
|
10915
|
+
var ABSOLUTE_MAX_READ_BYTES = 1e6;
|
|
10916
|
+
function resolveSafePath(root, relativePath) {
|
|
10917
|
+
const rootResolved = path2.resolve(root);
|
|
10918
|
+
const target = path2.resolve(rootResolved, relativePath || ".");
|
|
10919
|
+
if (target !== rootResolved && !target.startsWith(rootResolved + path2.sep)) {
|
|
10920
|
+
return null;
|
|
10921
|
+
}
|
|
10922
|
+
let cursor = rootResolved;
|
|
10923
|
+
const rest = path2.relative(rootResolved, target);
|
|
10924
|
+
if (rest && rest !== ".") {
|
|
10925
|
+
for (const segment of rest.split(path2.sep)) {
|
|
10926
|
+
cursor = path2.join(cursor, segment);
|
|
10927
|
+
try {
|
|
10928
|
+
const lstat = fs.lstatSync(cursor);
|
|
10929
|
+
if (lstat.isSymbolicLink()) {
|
|
10930
|
+
return null;
|
|
10931
|
+
}
|
|
10932
|
+
} catch {
|
|
10933
|
+
break;
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10936
|
+
}
|
|
10937
|
+
return target;
|
|
10938
|
+
}
|
|
10939
|
+
function executeCliLocalFileAction(root, interrupt, opts) {
|
|
10940
|
+
const safe = resolveSafePath(root, interrupt.path);
|
|
10941
|
+
if (!safe) {
|
|
10942
|
+
return { ok: false, error: "Path escapes trusted directory" };
|
|
10943
|
+
}
|
|
10944
|
+
try {
|
|
10945
|
+
switch (interrupt.action) {
|
|
10946
|
+
case "read_file": {
|
|
10947
|
+
if (!fs.existsSync(safe)) {
|
|
10948
|
+
return { ok: false, error: "File not found" };
|
|
10949
|
+
}
|
|
10950
|
+
const stat = fs.lstatSync(safe);
|
|
10951
|
+
if (stat.isSymbolicLink()) {
|
|
10952
|
+
return { ok: false, error: "Path escapes trusted directory" };
|
|
10953
|
+
}
|
|
10954
|
+
if (!stat.isFile()) {
|
|
10955
|
+
return { ok: false, error: "Not a file" };
|
|
10956
|
+
}
|
|
10957
|
+
const requested = Number.isFinite(interrupt.max_bytes) ? Math.max(1, Math.min(Number(interrupt.max_bytes), ABSOLUTE_MAX_READ_BYTES)) : DEFAULT_MAX_READ_BYTES;
|
|
10958
|
+
if (stat.size > requested) {
|
|
10959
|
+
const buf = fs.readFileSync(safe, { encoding: "utf8" });
|
|
10960
|
+
return {
|
|
10961
|
+
ok: true,
|
|
10962
|
+
content: buf.slice(0, requested) + `
|
|
10963
|
+
|
|
10964
|
+
[truncated at ${requested} bytes]`
|
|
10965
|
+
};
|
|
10966
|
+
}
|
|
10967
|
+
return { ok: true, content: fs.readFileSync(safe, "utf8") };
|
|
10968
|
+
}
|
|
10969
|
+
case "list_dir": {
|
|
10970
|
+
if (!fs.existsSync(safe)) {
|
|
10971
|
+
return { ok: false, error: "Directory not found" };
|
|
10972
|
+
}
|
|
10973
|
+
const entries = fs.readdirSync(safe, { withFileTypes: true });
|
|
10974
|
+
const lines = entries.slice(0, 200).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
|
|
10975
|
+
if (entries.length > 200) {
|
|
10976
|
+
lines.push(`... and ${entries.length - 200} more`);
|
|
10977
|
+
}
|
|
10978
|
+
return { ok: true, content: lines.join("\n") };
|
|
10979
|
+
}
|
|
10980
|
+
case "write_file": {
|
|
10981
|
+
if (!opts.allowWrite) {
|
|
10982
|
+
return { ok: false, error: "Write requires --allow-write" };
|
|
10983
|
+
}
|
|
10984
|
+
if (interrupt.content === void 0) {
|
|
10985
|
+
return { ok: false, error: "Missing content for write_file" };
|
|
10986
|
+
}
|
|
10987
|
+
fs.mkdirSync(path2.dirname(safe), { recursive: true });
|
|
10988
|
+
fs.writeFileSync(safe, interrupt.content, "utf8");
|
|
10989
|
+
return { ok: true, content: "written" };
|
|
10990
|
+
}
|
|
10991
|
+
default:
|
|
10992
|
+
return { ok: false, error: `Unknown action: ${interrupt.action}` };
|
|
10993
|
+
}
|
|
10994
|
+
} catch (e) {
|
|
10995
|
+
return { ok: false, error: sanitizeLocalFileError(e) };
|
|
10996
|
+
}
|
|
10997
|
+
}
|
|
10998
|
+
function sanitizeLocalFileError(e) {
|
|
10999
|
+
if (e && typeof e === "object" && "code" in e) {
|
|
11000
|
+
const code = e.code;
|
|
11001
|
+
if (code === "ENOENT") return "File or directory not found";
|
|
11002
|
+
if (code === "EACCES" || code === "EPERM") return "Permission denied";
|
|
11003
|
+
if (code === "EISDIR") return "Not a file";
|
|
11004
|
+
if (code === "ENOTDIR") return "Not a directory";
|
|
11005
|
+
}
|
|
11006
|
+
return "Local file operation failed";
|
|
11007
|
+
}
|
|
11008
|
+
function isCliLocalFileInterrupt(value) {
|
|
11009
|
+
if (typeof value !== "object" || value === null) return false;
|
|
11010
|
+
const v = value;
|
|
11011
|
+
return v.type === "cli_local_file" && typeof v.action === "string" && typeof v.path === "string";
|
|
11012
|
+
}
|
|
11013
|
+
|
|
11014
|
+
// src/lib/trusted-dirs.ts
|
|
11015
|
+
import fs2 from "fs";
|
|
11016
|
+
import os from "os";
|
|
11017
|
+
import path3 from "path";
|
|
11018
|
+
import readline from "readline";
|
|
11019
|
+
var STORE_VERSION = 1;
|
|
11020
|
+
function storePath() {
|
|
11021
|
+
const base = process.env.XDG_CONFIG_HOME ?? (process.platform === "win32" ? path3.join(process.env.APPDATA ?? os.homedir(), "gravityrail") : path3.join(os.homedir(), ".config", "gravityrail"));
|
|
11022
|
+
return path3.join(base, "trusted-dirs.json");
|
|
11023
|
+
}
|
|
11024
|
+
function loadTrustedDirsStore() {
|
|
11025
|
+
const file = storePath();
|
|
11026
|
+
try {
|
|
11027
|
+
const raw = JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
11028
|
+
if (raw?.version === STORE_VERSION && Array.isArray(raw.always)) {
|
|
11029
|
+
return { version: STORE_VERSION, always: raw.always.map((p) => path3.resolve(p)) };
|
|
11030
|
+
}
|
|
11031
|
+
} catch {
|
|
11032
|
+
}
|
|
11033
|
+
return { version: STORE_VERSION, always: [] };
|
|
11034
|
+
}
|
|
11035
|
+
function saveTrustedDirsStore(store) {
|
|
11036
|
+
const file = storePath();
|
|
11037
|
+
const dir = path3.dirname(file);
|
|
11038
|
+
fs2.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
11039
|
+
fs2.writeFileSync(file, JSON.stringify(store, null, 2) + "\n", { mode: 384 });
|
|
11040
|
+
}
|
|
11041
|
+
function isDirectoryTrusted(dirPath, opts) {
|
|
11042
|
+
if (opts?.trustCwdFlag) return true;
|
|
11043
|
+
const resolved = path3.resolve(dirPath);
|
|
11044
|
+
const store = loadTrustedDirsStore();
|
|
11045
|
+
return store.always.includes(resolved);
|
|
11046
|
+
}
|
|
11047
|
+
async function promptTrustInteractive(dirPath) {
|
|
11048
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
11049
|
+
const question = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
11050
|
+
process.stderr.write(
|
|
11051
|
+
`
|
|
11052
|
+
Gravity Rail may read files in:
|
|
11053
|
+
${dirPath}
|
|
11054
|
+
|
|
11055
|
+
File contents can influence the assistant. Do you trust the files in this directory?
|
|
11056
|
+
[1] Trust once (this session)
|
|
11057
|
+
[2] Trust always (remember for this path)
|
|
11058
|
+
[3] No \u2014 do not read local files
|
|
11059
|
+
|
|
11060
|
+
Choice [1/2/3] (default 3): `
|
|
11061
|
+
);
|
|
11062
|
+
const answer = (await question("")).trim() || "3";
|
|
11063
|
+
rl.close();
|
|
11064
|
+
if (answer === "1") return "once";
|
|
11065
|
+
if (answer === "2") return "always";
|
|
11066
|
+
return "denied";
|
|
11067
|
+
}
|
|
11068
|
+
var sessionTrusted = /* @__PURE__ */ new Set();
|
|
11069
|
+
async function resolveDirectoryTrust(opts) {
|
|
11070
|
+
const dir = path3.resolve(opts.trustDir ?? opts.cwd ?? process.cwd());
|
|
11071
|
+
if (isDirectoryTrusted(dir, { trustCwdFlag: opts.trustCwd })) return "trusted";
|
|
11072
|
+
if (sessionTrusted.has(dir)) return "trusted";
|
|
11073
|
+
if (opts.trustDir && path3.resolve(opts.trustDir) === dir) {
|
|
11074
|
+
sessionTrusted.add(dir);
|
|
11075
|
+
return "trusted";
|
|
11076
|
+
}
|
|
11077
|
+
if (opts.trustCwd) {
|
|
11078
|
+
sessionTrusted.add(dir);
|
|
11079
|
+
return "trusted";
|
|
11080
|
+
}
|
|
11081
|
+
if (!process.stdin.isTTY || opts.nonInteractive) {
|
|
11082
|
+
return "denied";
|
|
11083
|
+
}
|
|
11084
|
+
const decision = await promptTrustInteractive(dir);
|
|
11085
|
+
if (decision === "denied") {
|
|
11086
|
+
return "denied";
|
|
11087
|
+
}
|
|
11088
|
+
if (decision === "always") {
|
|
11089
|
+
const store = loadTrustedDirsStore();
|
|
11090
|
+
if (!store.always.includes(dir)) {
|
|
11091
|
+
store.always.push(dir);
|
|
11092
|
+
saveTrustedDirsStore(store);
|
|
11093
|
+
}
|
|
11094
|
+
}
|
|
11095
|
+
sessionTrusted.add(dir);
|
|
11096
|
+
return "trusted";
|
|
11097
|
+
}
|
|
11098
|
+
async function requireDirectoryTrust(opts) {
|
|
11099
|
+
const { CLIError: CLIError2 } = await import("./errors-4MAJGD3C.js");
|
|
11100
|
+
const dir = path3.resolve(opts.trustDir ?? opts.cwd ?? process.cwd());
|
|
11101
|
+
const status = await resolveDirectoryTrust(opts);
|
|
11102
|
+
if (status === "trusted") return;
|
|
11103
|
+
throw new CLIError2(
|
|
11104
|
+
`Directory not trusted: ${dir}
|
|
11105
|
+
|
|
11106
|
+
Re-run with --trust-cwd (or --trust-dir <path>) after reviewing files in that directory, or run interactively to approve trust when a local file tool is first used.`
|
|
11107
|
+
);
|
|
11108
|
+
}
|
|
11109
|
+
|
|
11110
|
+
// src/lib/cli-hitl.ts
|
|
11111
|
+
function extractHumanInputRequest(event) {
|
|
11112
|
+
const type = String(event.type ?? "").toUpperCase();
|
|
11113
|
+
if (type === "CUSTOM" && event.name === "human_input_request") {
|
|
11114
|
+
return event.value;
|
|
11115
|
+
}
|
|
11116
|
+
if (event.name === "human_input_request") {
|
|
11117
|
+
return event.value;
|
|
11118
|
+
}
|
|
11119
|
+
return null;
|
|
11120
|
+
}
|
|
11121
|
+
async function handleCliHitlInterrupt(value, opts) {
|
|
11122
|
+
if (!value || typeof value !== "object") return null;
|
|
11123
|
+
const payload = value;
|
|
11124
|
+
if (isCliLocalFileInterrupt(payload)) {
|
|
11125
|
+
const interrupt = payload;
|
|
11126
|
+
if (typeof payload.interrupt_id === "string") {
|
|
11127
|
+
interrupt.interrupt_id = payload.interrupt_id;
|
|
11128
|
+
}
|
|
11129
|
+
return handleCliLocalFile(interrupt, opts);
|
|
11130
|
+
}
|
|
11131
|
+
if (payload.type === "present_choice") {
|
|
11132
|
+
return handlePresentChoiceTerminal(payload);
|
|
11133
|
+
}
|
|
11134
|
+
return null;
|
|
11135
|
+
}
|
|
11136
|
+
async function handleCliLocalFile(interrupt, opts) {
|
|
11137
|
+
const trust = await resolveDirectoryTrust({
|
|
11138
|
+
cwd: process.cwd(),
|
|
11139
|
+
trustCwd: opts.trustCwd,
|
|
11140
|
+
trustDir: opts.trustDir,
|
|
11141
|
+
// Interactive sessions: prompt on first local-file use (review feedback).
|
|
11142
|
+
nonInteractive: opts.nonInteractive ?? process.stdin.isTTY !== true
|
|
11143
|
+
});
|
|
11144
|
+
if (trust !== "trusted") {
|
|
11145
|
+
return {
|
|
11146
|
+
resumeValue: JSON.stringify({
|
|
11147
|
+
ok: false,
|
|
11148
|
+
error: "Directory not trusted for local file access. Re-run with --trust-cwd or --trust-dir <path>, or approve trust interactively when prompted."
|
|
11149
|
+
}),
|
|
11150
|
+
resumeInterruptId: interrupt.interrupt_id
|
|
11151
|
+
};
|
|
11152
|
+
}
|
|
11153
|
+
process.stderr.write(`
|
|
11154
|
+
[local ${interrupt.action}] ${interrupt.path}
|
|
11155
|
+
`);
|
|
11156
|
+
const result = executeCliLocalFileAction(process.cwd(), interrupt, {
|
|
11157
|
+
allowWrite: opts.allowWrite
|
|
11158
|
+
});
|
|
11159
|
+
return {
|
|
11160
|
+
resumeValue: JSON.stringify(result),
|
|
11161
|
+
resumeInterruptId: interrupt.interrupt_id
|
|
11162
|
+
};
|
|
11163
|
+
}
|
|
11164
|
+
async function handlePresentChoiceTerminal(payload) {
|
|
11165
|
+
const prompt = typeof payload.user_prompt === "string" && payload.user_prompt || typeof payload.description === "string" && payload.description || "Choose an option";
|
|
11166
|
+
const markup = typeof payload.openui_markup === "string" ? payload.openui_markup : "";
|
|
11167
|
+
process.stderr.write(`
|
|
11168
|
+
${prompt}
|
|
11169
|
+
`);
|
|
11170
|
+
if (markup) {
|
|
11171
|
+
process.stderr.write(`${markup.slice(0, 2e3)}
|
|
11172
|
+
`);
|
|
11173
|
+
}
|
|
11174
|
+
if (!process.stdin.isTTY) {
|
|
11175
|
+
return {
|
|
11176
|
+
resumeValue: "",
|
|
11177
|
+
resumeInterruptId: typeof payload.interrupt_id === "string" ? payload.interrupt_id : void 0
|
|
11178
|
+
};
|
|
11179
|
+
}
|
|
11180
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
11181
|
+
const answer = await new Promise((resolve) => {
|
|
11182
|
+
rl.question("Your choice (value): ", resolve);
|
|
11183
|
+
});
|
|
11184
|
+
rl.close();
|
|
11185
|
+
return {
|
|
11186
|
+
resumeValue: answer.trim(),
|
|
11187
|
+
resumeInterruptId: typeof payload.interrupt_id === "string" ? payload.interrupt_id : void 0
|
|
11188
|
+
};
|
|
11189
|
+
}
|
|
11190
|
+
|
|
11191
|
+
// src/lib/concierge-agui-loop.ts
|
|
11192
|
+
var MAX_HITL_ROUNDS = 25;
|
|
11193
|
+
async function runConciergeAguiLoop(client, initialMessages, onEvent, opts) {
|
|
11194
|
+
let streamText = "";
|
|
11195
|
+
let hasToolCalls = false;
|
|
11196
|
+
let resume;
|
|
11197
|
+
for (let round = 0; round < MAX_HITL_ROUNDS; round++) {
|
|
11198
|
+
let hitlPayload = null;
|
|
11199
|
+
await client.streamRequest(
|
|
11200
|
+
resume ? {
|
|
11201
|
+
messages: [],
|
|
11202
|
+
resumeValue: resume.resumeValue,
|
|
11203
|
+
resumeInterruptId: resume.resumeInterruptId
|
|
11204
|
+
} : { messages: initialMessages },
|
|
11205
|
+
(event) => {
|
|
11206
|
+
onEvent(event);
|
|
11207
|
+
const delta = getTextDelta(event);
|
|
11208
|
+
if (delta) streamText += delta;
|
|
11209
|
+
const type = event.type?.toLowerCase() ?? "";
|
|
11210
|
+
if (type === "tool_call_start") hasToolCalls = true;
|
|
11211
|
+
const errorMsg = getErrorMessage(event);
|
|
11212
|
+
if (errorMsg) process.stderr.write(`\x1B[31mError: ${errorMsg}\x1B[0m
|
|
11213
|
+
`);
|
|
11214
|
+
const hitl = extractHumanInputRequest(event);
|
|
11215
|
+
if (hitl) hitlPayload = hitl;
|
|
11216
|
+
},
|
|
11217
|
+
{ autoApprove: opts.autoApprove }
|
|
11218
|
+
);
|
|
11219
|
+
if (!hitlPayload) {
|
|
11220
|
+
break;
|
|
11221
|
+
}
|
|
11222
|
+
const next = await handleCliHitlInterrupt(hitlPayload, {
|
|
11223
|
+
allowWrite: opts.allowWrite,
|
|
11224
|
+
autoApprove: opts.autoApprove,
|
|
11225
|
+
trustCwd: opts.trustCwd,
|
|
11226
|
+
trustDir: opts.trustDir,
|
|
11227
|
+
nonInteractive: opts.nonInteractive
|
|
11228
|
+
});
|
|
11229
|
+
if (!next) {
|
|
11230
|
+
process.stderr.write(
|
|
11231
|
+
" [HITL] Unsupported interrupt in CLI \u2014 open app.gravityrail.com/setup to continue.\n"
|
|
11232
|
+
);
|
|
11233
|
+
break;
|
|
11234
|
+
}
|
|
11235
|
+
resume = next;
|
|
11236
|
+
streamText = "";
|
|
11237
|
+
}
|
|
11238
|
+
return { streamText, hasToolCalls };
|
|
11239
|
+
}
|
|
11240
|
+
|
|
10155
11241
|
// src/lib/chat-session.ts
|
|
10156
|
-
import * as
|
|
11242
|
+
import * as readline4 from "readline";
|
|
10157
11243
|
|
|
10158
11244
|
// src/lib/realtime-client.ts
|
|
10159
11245
|
import { EventEmitter } from "events";
|
|
@@ -11186,7 +12272,7 @@ function buildDebugSummary(_direction, msg) {
|
|
|
11186
12272
|
// src/lib/audio.ts
|
|
11187
12273
|
import { spawn, execSync as execSync2, spawnSync } from "child_process";
|
|
11188
12274
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
11189
|
-
import * as
|
|
12275
|
+
import * as readline3 from "readline";
|
|
11190
12276
|
var SAMPLE_RATE = 24e3;
|
|
11191
12277
|
var MIC_CHUNK_BYTES = 4800;
|
|
11192
12278
|
function checkSoxAvailable() {
|
|
@@ -11238,7 +12324,7 @@ async function selectDevice(devices, label) {
|
|
|
11238
12324
|
if (devices.length <= 1) {
|
|
11239
12325
|
return devices[0];
|
|
11240
12326
|
}
|
|
11241
|
-
const rl =
|
|
12327
|
+
const rl = readline3.createInterface({
|
|
11242
12328
|
input: process.stdin,
|
|
11243
12329
|
output: process.stderr,
|
|
11244
12330
|
terminal: true
|
|
@@ -11517,56 +12603,52 @@ async function runChatSession(session, args) {
|
|
|
11517
12603
|
"Interactive mode requires a TTY. Use --message for non-interactive mode."
|
|
11518
12604
|
);
|
|
11519
12605
|
}
|
|
12606
|
+
const allowWrite = !!args["allow-write"] || !!session.allowWrite;
|
|
11520
12607
|
if (message) {
|
|
11521
|
-
await singleTurnChatSession(session, message, debug, autoApprove);
|
|
12608
|
+
await singleTurnChatSession(session, message, debug, autoApprove, allowWrite);
|
|
11522
12609
|
} else if (isVoice) {
|
|
11523
12610
|
await voiceChatSession(session, vadMode(args), args, debug);
|
|
11524
12611
|
} else if (isRealtime) {
|
|
11525
12612
|
await realtimeChatSession(session, vadMode(args), debug);
|
|
11526
12613
|
} else {
|
|
11527
|
-
await interactiveChatSession(session, debug);
|
|
12614
|
+
await interactiveChatSession(session, debug, autoApprove, allowWrite);
|
|
11528
12615
|
}
|
|
11529
12616
|
}
|
|
11530
12617
|
function vadMode(args) {
|
|
11531
12618
|
return typeof args.vad === "string" ? args.vad : "server";
|
|
11532
12619
|
}
|
|
11533
|
-
async function singleTurnChatSession(session, message, debug, autoApprove = false) {
|
|
12620
|
+
async function singleTurnChatSession(session, message, debug, autoApprove = false, allowWrite = false) {
|
|
11534
12621
|
const aguiClient = new AGUIClient({
|
|
11535
12622
|
baseUrl: session.ctx.envConfig.apiUrl,
|
|
12623
|
+
workspaceUuid: session.workspaceUuid,
|
|
12624
|
+
chatId: session.chatId,
|
|
11536
12625
|
endpointUrl: session.aguiEndpointUrl,
|
|
11537
12626
|
accessToken: session.ctx.accessToken,
|
|
11538
12627
|
debug,
|
|
11539
|
-
model: session.model
|
|
12628
|
+
model: session.model,
|
|
12629
|
+
appContext: session.appContext
|
|
11540
12630
|
});
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
const logStream = logFile && fs ? fs.createWriteStream(logFile, { flags: "a" }) : null;
|
|
11547
|
-
await aguiClient.sendMessage(
|
|
11548
|
-
[{ role: "user", content: message }],
|
|
11549
|
-
(event) => {
|
|
11550
|
-
eventCount++;
|
|
11551
|
-
if (logStream) logStream.write(JSON.stringify(event) + "\n");
|
|
11552
|
-
const delta = getTextDelta(event);
|
|
11553
|
-
if (delta) streamText += delta;
|
|
11554
|
-
const type = event.type?.toLowerCase() ?? "";
|
|
11555
|
-
if (type === "tool_call_start") {
|
|
11556
|
-
hasToolCalls = true;
|
|
11557
|
-
const name = event.name ?? "";
|
|
11558
|
-
process.stderr.write(` [${name}]
|
|
12631
|
+
const onEvent = (event) => {
|
|
12632
|
+
const type = event.type?.toLowerCase() ?? "";
|
|
12633
|
+
if (type === "tool_call_start") {
|
|
12634
|
+
const name = event.toolCallName ?? event.name ?? "";
|
|
12635
|
+
process.stderr.write(` [${name}]
|
|
11559
12636
|
`);
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
},
|
|
11565
|
-
|
|
12637
|
+
}
|
|
12638
|
+
};
|
|
12639
|
+
const { streamText, hasToolCalls } = await runConciergeAguiLoop(
|
|
12640
|
+
aguiClient,
|
|
12641
|
+
[{ role: "user", content: message }],
|
|
12642
|
+
onEvent,
|
|
12643
|
+
{
|
|
12644
|
+
autoApprove,
|
|
12645
|
+
allowWrite: allowWrite || !!session.allowWrite,
|
|
12646
|
+
debug,
|
|
12647
|
+
trustCwd: session.trustCwd,
|
|
12648
|
+
trustDir: session.trustDir,
|
|
12649
|
+
nonInteractive: true
|
|
12650
|
+
}
|
|
11566
12651
|
);
|
|
11567
|
-
if (logStream) logStream.end();
|
|
11568
|
-
if (debug) process.stderr.write(` (${eventCount} SSE events)
|
|
11569
|
-
`);
|
|
11570
12652
|
if (streamText) {
|
|
11571
12653
|
process.stdout.write(`\x1B[32mAssistant:\x1B[0m ${streamText}
|
|
11572
12654
|
`);
|
|
@@ -11600,13 +12682,16 @@ async function singleTurnChatSession(session, message, debug, autoApprove = fals
|
|
|
11600
12682
|
`);
|
|
11601
12683
|
}
|
|
11602
12684
|
}
|
|
11603
|
-
async function interactiveChatSession(session, debug) {
|
|
12685
|
+
async function interactiveChatSession(session, debug, autoApprove, allowWrite) {
|
|
11604
12686
|
const aguiClient = new AGUIClient({
|
|
11605
12687
|
baseUrl: session.ctx.envConfig.apiUrl,
|
|
12688
|
+
workspaceUuid: session.workspaceUuid,
|
|
12689
|
+
chatId: session.chatId,
|
|
11606
12690
|
endpointUrl: session.aguiEndpointUrl,
|
|
11607
12691
|
accessToken: session.ctx.accessToken,
|
|
11608
12692
|
debug,
|
|
11609
|
-
model: session.model
|
|
12693
|
+
model: session.model,
|
|
12694
|
+
appContext: session.appContext
|
|
11610
12695
|
});
|
|
11611
12696
|
clearScreen();
|
|
11612
12697
|
console.log("=".repeat(60));
|
|
@@ -11634,7 +12719,7 @@ async function interactiveChatSession(session, debug) {
|
|
|
11634
12719
|
conversationMessages.push({ role: msg.role, content: text });
|
|
11635
12720
|
}
|
|
11636
12721
|
}
|
|
11637
|
-
const rl =
|
|
12722
|
+
const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
|
|
11638
12723
|
return new Promise((resolve) => {
|
|
11639
12724
|
const prompt = () => {
|
|
11640
12725
|
rl.question("\x1B[34mYou:\x1B[0m ", async (input) => {
|
|
@@ -11652,18 +12737,34 @@ async function interactiveChatSession(session, debug) {
|
|
|
11652
12737
|
process.stdout.write("\n\x1B[32mAssistant:\x1B[0m ");
|
|
11653
12738
|
let assistantContent = "";
|
|
11654
12739
|
try {
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
12740
|
+
const { streamText } = await runConciergeAguiLoop(
|
|
12741
|
+
aguiClient,
|
|
12742
|
+
conversationMessages,
|
|
12743
|
+
(event) => {
|
|
12744
|
+
const delta = getTextDelta(event);
|
|
12745
|
+
if (delta) {
|
|
12746
|
+
process.stdout.write(delta);
|
|
12747
|
+
assistantContent += delta;
|
|
12748
|
+
}
|
|
12749
|
+
const errorMsg = getErrorMessage(event);
|
|
12750
|
+
if (errorMsg) console.log(`
|
|
11663
12751
|
\x1B[31mError: ${errorMsg}\x1B[0m`);
|
|
11664
|
-
|
|
11665
|
-
|
|
12752
|
+
},
|
|
12753
|
+
{
|
|
12754
|
+
autoApprove,
|
|
12755
|
+
allowWrite: allowWrite || !!session.allowWrite,
|
|
12756
|
+
debug,
|
|
12757
|
+
trustCwd: session.trustCwd,
|
|
12758
|
+
trustDir: session.trustDir
|
|
12759
|
+
}
|
|
12760
|
+
);
|
|
12761
|
+
if (streamText && !assistantContent) {
|
|
12762
|
+
assistantContent = streamText;
|
|
12763
|
+
process.stdout.write(streamText);
|
|
12764
|
+
}
|
|
12765
|
+
if (assistantContent) {
|
|
11666
12766
|
conversationMessages.push({ role: "assistant", content: assistantContent });
|
|
12767
|
+
}
|
|
11667
12768
|
console.log("\n");
|
|
11668
12769
|
} catch (error) {
|
|
11669
12770
|
console.log(
|
|
@@ -11923,13 +13024,14 @@ async function conciergeCommand(hctx) {
|
|
|
11923
13024
|
const chatIdArg = typeof args["chat-id"] === "string" ? args["chat-id"] : void 0;
|
|
11924
13025
|
const message = typeof args.message === "string" ? args.message : void 0;
|
|
11925
13026
|
const chat = await getOrCreateConciergeChat(ctx, chatIdArg, !!message);
|
|
11926
|
-
const session = buildConciergeSession(ctx, chat);
|
|
13027
|
+
const session = buildConciergeSession(ctx, chat, args);
|
|
11927
13028
|
await runChatSession(session, args);
|
|
11928
13029
|
}
|
|
11929
|
-
function buildConciergeSession(ctx, chat) {
|
|
13030
|
+
function buildConciergeSession(ctx, chat, args) {
|
|
11930
13031
|
const wsBase = ctx.envConfig.apiUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:");
|
|
11931
13032
|
return {
|
|
11932
13033
|
ctx,
|
|
13034
|
+
workspaceUuid: "",
|
|
11933
13035
|
chatId: chat.id,
|
|
11934
13036
|
chatUuid: chat.uuid,
|
|
11935
13037
|
chatTitle: chat.title || chat.uuid,
|
|
@@ -11937,8 +13039,12 @@ function buildConciergeSession(ctx, chat) {
|
|
|
11937
13039
|
wsUrl: `${wsBase}/api/v2/concierge/voice/stream-ws`,
|
|
11938
13040
|
loadMessages: () => getConciergeChatMessages(ctx, chat.id).catch(() => []),
|
|
11939
13041
|
label: "Concierge",
|
|
11940
|
-
model: void 0
|
|
13042
|
+
model: void 0,
|
|
11941
13043
|
// concierge doesn't send model field
|
|
13044
|
+
appContext: buildCliConciergeAppContext(),
|
|
13045
|
+
allowWrite: true,
|
|
13046
|
+
trustCwd: !!args["trust-cwd"],
|
|
13047
|
+
trustDir: typeof args["trust-dir"] === "string" ? args["trust-dir"] : void 0
|
|
11942
13048
|
};
|
|
11943
13049
|
}
|
|
11944
13050
|
async function getOrCreateConciergeChat(ctx, chatIdArg, skipSelection) {
|
|
@@ -11974,6 +13080,95 @@ async function getOrCreateConciergeChat(ctx, chatIdArg, skipSelection) {
|
|
|
11974
13080
|
return selectedChat;
|
|
11975
13081
|
}
|
|
11976
13082
|
|
|
13083
|
+
// src/commands/get-started.ts
|
|
13084
|
+
var WEB_SETUP = "https://app.gravityrail.com/setup";
|
|
13085
|
+
var WEB_GET_STARTED = "https://app.gravityrail.com/get-started";
|
|
13086
|
+
var HUMAN_GUIDE = `Gravity Rail \u2014 getting started
|
|
13087
|
+
|
|
13088
|
+
1. Authenticate: gr login --env prod
|
|
13089
|
+
Confirm: gr whoami
|
|
13090
|
+
|
|
13091
|
+
2. Recommended path \u2014 concierge (no workspace required):
|
|
13092
|
+
gr concierge chat --allow-write
|
|
13093
|
+
The concierge can create your org (auto-created on first login), list products,
|
|
13094
|
+
start Stripe checkout, and create your first workspace.
|
|
13095
|
+
|
|
13096
|
+
3. Verify workspaces: gr workspaces list
|
|
13097
|
+
|
|
13098
|
+
4. Web wizards (human-friendly): ${WEB_SETUP} and ${WEB_GET_STARTED}
|
|
13099
|
+
|
|
13100
|
+
Headless boundary: Stripe checkout needs a human in a browser \u2014 the concierge returns
|
|
13101
|
+
a checkout URL; an agent should hand that link to the user, not loop on payment.
|
|
13102
|
+
|
|
13103
|
+
Agent-readable steps: gr get-started --format llm
|
|
13104
|
+
`;
|
|
13105
|
+
var LLM_GUIDE = `# Gravity Rail CLI bootstrap (agent instructions)
|
|
13106
|
+
|
|
13107
|
+
## Preconditions
|
|
13108
|
+
- Install: \`npm install -g @gravity-rail/cli\` or \`npx @gravity-rail/cli\`
|
|
13109
|
+
- Authenticate: \`gr login --env prod\` (interactive OAuth). Verify with \`gr whoami\`.
|
|
13110
|
+
- **Monorepo dev:** from gravity-team with Docker dev up, use \`yarn gr <cmd> --realm superadmin\` (reads \`dev-realms.json\`; no browser). Rebuild: \`yarn workspace @gravity-rail/gr-cli build\`.
|
|
13111
|
+
|
|
13112
|
+
## Important facts
|
|
13113
|
+
- **Agents/CI:** do not leave stdin open on read-only commands; use \`GRAVITY_RAIL_API_KEY\` or redirect stdin from /dev/null. Commands only read stdin when \`--data\`/\`--file\` is passed.
|
|
13114
|
+
- Use \`--env dev|staging|prod\` (default prod) so credentials match the target; \`gr whoami\` shows the active env.
|
|
13115
|
+
- After login, an **organization is created automatically** \u2014 do not create a duplicate org unless \`gr organizations list\` is empty.
|
|
13116
|
+
- **Workspace is required** for workflow/agent/task commands (\`-w <workspace-uuid>\`).
|
|
13117
|
+
- **Concierge does not require \`-w\`** \u2014 use it for first-run setup.
|
|
13118
|
+
|
|
13119
|
+
## Recommended path (concierge-driven)
|
|
13120
|
+
\`\`\`bash
|
|
13121
|
+
gr concierge chat --allow-write --message "Help me set up my first Gravity Rail workspace"
|
|
13122
|
+
\`\`\`
|
|
13123
|
+
Flags for unattended turns:
|
|
13124
|
+
- \`--message\` / \`-m\`: single-turn (non-interactive)
|
|
13125
|
+
- \`--auto-approve\`: skip HITL tool approval where policy allows
|
|
13126
|
+
- \`--allow-write\`: required for create/update tools
|
|
13127
|
+
|
|
13128
|
+
The concierge can: list/create orgs, list org/workspace products, create Stripe checkout sessions, create workspace specs, and \`create_workspace\`.
|
|
13129
|
+
|
|
13130
|
+
## Manual CLI path (if not using concierge)
|
|
13131
|
+
1. \`gr whoami\` \u2014 confirm account
|
|
13132
|
+
2. \`gr organizations list\` \u2014 note org slug/UUID
|
|
13133
|
+
3. \`gr concierge chat\` OR web: ${WEB_GET_STARTED}
|
|
13134
|
+
4. Product purchase: concierge returns Stripe checkout URL \u2192 **human must complete in browser**
|
|
13135
|
+
5. \`gr workspaces list\` \u2014 pick workspace UUID for \`-w\`
|
|
13136
|
+
|
|
13137
|
+
## Support (account-level, no workspace)
|
|
13138
|
+
\`\`\`bash
|
|
13139
|
+
gr support create --type question --title "..." --description "..." --allow-write
|
|
13140
|
+
gr support list
|
|
13141
|
+
\`\`\`
|
|
13142
|
+
|
|
13143
|
+
## Web fallbacks
|
|
13144
|
+
- Concierge / setup: ${WEB_SETUP}
|
|
13145
|
+
- Wizard: ${WEB_GET_STARTED}
|
|
13146
|
+
|
|
13147
|
+
## Headless limits (do not retry forever)
|
|
13148
|
+
- Stripe payment / checkout: requires human browser
|
|
13149
|
+
- OAuth login: requires human browser
|
|
13150
|
+
- Directory trust for future local-file tools: use \`--trust-cwd\` only after user confirms files are safe
|
|
13151
|
+
|
|
13152
|
+
## Local project files (CLI only)
|
|
13153
|
+
After directory trust + \`--allow-write\`, the concierge can use \`read_local_file\`, \`list_local_dir\`, \`write_local_file\` (client-executed via HITL).
|
|
13154
|
+
|
|
13155
|
+
## Verify success
|
|
13156
|
+
\`gr workspaces list\` then \`gr workflows list -w <uuid>\` or \`gr agents list -w <uuid>\`
|
|
13157
|
+
`;
|
|
13158
|
+
async function getStartedCommand(hctx) {
|
|
13159
|
+
const format = hctx.args.format ?? hctx.args.agent;
|
|
13160
|
+
const isLlm = format === "llm" || format === "agent" || hctx.args["format"] === "llm" || !!hctx.args.agent;
|
|
13161
|
+
if (!isLlm) {
|
|
13162
|
+
try {
|
|
13163
|
+
requireAuth(hctx.ctx);
|
|
13164
|
+
} catch {
|
|
13165
|
+
}
|
|
13166
|
+
process.stdout.write(HUMAN_GUIDE);
|
|
13167
|
+
return;
|
|
13168
|
+
}
|
|
13169
|
+
process.stdout.write(LLM_GUIDE);
|
|
13170
|
+
}
|
|
13171
|
+
|
|
11977
13172
|
// src/commands/dev.ts
|
|
11978
13173
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync3 } from "fs";
|
|
11979
13174
|
import { execSync as execSync3 } from "child_process";
|
|
@@ -12057,8 +13252,17 @@ async function manageCommand(hctx) {
|
|
|
12057
13252
|
const chatIdArg = typeof args["chat-id"] === "string" ? args["chat-id"] : void 0;
|
|
12058
13253
|
const message = typeof args.message === "string" ? args.message : void 0;
|
|
12059
13254
|
const chat = await getOrCreateChat(ctx, workspace, chatIdArg, asMember, "manager", !!message);
|
|
12060
|
-
const session = buildWorkspaceSession(ctx, workspace, chat, "Workspace Manager"
|
|
13255
|
+
const session = buildWorkspaceSession(ctx, workspace, chat, "Workspace Manager", {
|
|
13256
|
+
appContext: buildCliConciergeAppContext(),
|
|
13257
|
+
allowWrite: true,
|
|
13258
|
+
trustCwd: !!args["trust-cwd"],
|
|
13259
|
+
trustDir: typeof args["trust-dir"] === "string" ? args["trust-dir"] : void 0
|
|
13260
|
+
});
|
|
13261
|
+
process.stderr.write(`Chat #${session.chatId}
|
|
13262
|
+
`);
|
|
12061
13263
|
await runChatSession(session, args);
|
|
13264
|
+
process.stderr.write(`Chat #${session.chatId}
|
|
13265
|
+
`);
|
|
12062
13266
|
}
|
|
12063
13267
|
async function chatCommand(hctx) {
|
|
12064
13268
|
const ctx = requireAuth(hctx.ctx);
|
|
@@ -12083,10 +13287,11 @@ async function chatCommand(hctx) {
|
|
|
12083
13287
|
const session = buildWorkspaceSession(ctx, workspace, chat, "Chat");
|
|
12084
13288
|
await runChatSession(session, args);
|
|
12085
13289
|
}
|
|
12086
|
-
function buildWorkspaceSession(ctx, workspace, chat, label) {
|
|
13290
|
+
function buildWorkspaceSession(ctx, workspace, chat, label, extra) {
|
|
12087
13291
|
const wsBase = ctx.envConfig.apiUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:");
|
|
12088
13292
|
return {
|
|
12089
13293
|
ctx,
|
|
13294
|
+
workspaceUuid: workspace,
|
|
12090
13295
|
chatId: chat.id,
|
|
12091
13296
|
chatUuid: chat.uuid,
|
|
12092
13297
|
chatTitle: chat.title || chat.uuid,
|
|
@@ -12094,7 +13299,8 @@ function buildWorkspaceSession(ctx, workspace, chat, label) {
|
|
|
12094
13299
|
wsUrl: `${wsBase}/api/v2/w/${workspace}/realtime/stream-ws`,
|
|
12095
13300
|
loadMessages: () => ctx.apiClient.getChatMessages(workspace, chat.id, 50).catch(() => []),
|
|
12096
13301
|
label,
|
|
12097
|
-
model: "manager"
|
|
13302
|
+
model: "manager",
|
|
13303
|
+
...extra
|
|
12098
13304
|
};
|
|
12099
13305
|
}
|
|
12100
13306
|
async function getOrCreateChat(ctx, workspace, chatIdArg, asMemberId, chatType, skipSelection = false) {
|
|
@@ -12176,7 +13382,7 @@ async function createMemberChat(apiClient, workspace, memberId) {
|
|
|
12176
13382
|
}
|
|
12177
13383
|
|
|
12178
13384
|
// src/commands/organizations.ts
|
|
12179
|
-
var
|
|
13385
|
+
var COLUMNS34 = [
|
|
12180
13386
|
{ header: "NAME", getValue: (o) => o.organizationName || "", maxWidth: 35 },
|
|
12181
13387
|
{ header: "SLUG", getValue: (o) => o.organizationSlug || "", maxWidth: 25 },
|
|
12182
13388
|
{ header: "UUID", getValue: (o) => o.organizationUuid },
|
|
@@ -12184,19 +13390,23 @@ var COLUMNS33 = [
|
|
|
12184
13390
|
];
|
|
12185
13391
|
async function organizationsListCommand(ctx) {
|
|
12186
13392
|
const organizations = await ctx.apiClient.getMyOrganizations();
|
|
12187
|
-
outputData(organizations, ctx,
|
|
13393
|
+
outputData(organizations, ctx, COLUMNS34);
|
|
12188
13394
|
}
|
|
12189
13395
|
|
|
12190
13396
|
// src/commands/whoami.ts
|
|
12191
|
-
var
|
|
13397
|
+
var COLUMNS35 = [
|
|
12192
13398
|
{ header: "NAME", getValue: (a) => a.name || "", maxWidth: 30 },
|
|
12193
13399
|
{ header: "EMAIL", getValue: (a) => a.email || "", maxWidth: 40 },
|
|
12194
13400
|
{ header: "UUID", getValue: (a) => a.uuid },
|
|
13401
|
+
{ header: "ENV", getValue: () => "" },
|
|
12195
13402
|
{ header: "SUPERUSER", getValue: (a) => a.isSuperuser ? "yes" : "no" }
|
|
12196
13403
|
];
|
|
12197
13404
|
async function whoamiCommand(ctx) {
|
|
12198
13405
|
const account = await ctx.apiClient.getUserAccount();
|
|
12199
|
-
|
|
13406
|
+
const columns = COLUMNS35.map(
|
|
13407
|
+
(col) => col.header === "ENV" ? { ...col, getValue: () => ctx.env } : col
|
|
13408
|
+
);
|
|
13409
|
+
outputSingle(account, ctx, columns);
|
|
12200
13410
|
}
|
|
12201
13411
|
export {
|
|
12202
13412
|
AuthRequiredError,
|
|
@@ -12206,6 +13416,7 @@ export {
|
|
|
12206
13416
|
LIST_PAGINATION_ARGS,
|
|
12207
13417
|
PAGINATION_ARGS,
|
|
12208
13418
|
authBrowserCommand,
|
|
13419
|
+
buildCliConciergeAppContext,
|
|
12209
13420
|
chatCommand,
|
|
12210
13421
|
conciergeCommand,
|
|
12211
13422
|
createAuthenticatedContext,
|
|
@@ -12222,8 +13433,10 @@ export {
|
|
|
12222
13433
|
formatPaginationWarning,
|
|
12223
13434
|
getConciergeChat,
|
|
12224
13435
|
getConciergeChatMessages,
|
|
13436
|
+
getStartedCommand,
|
|
12225
13437
|
handleError,
|
|
12226
13438
|
isCursorPaginatedResponse,
|
|
13439
|
+
isDirectoryTrusted,
|
|
12227
13440
|
isListResult,
|
|
12228
13441
|
isPagePaginatedResponse,
|
|
12229
13442
|
isPaginatedResponse,
|
|
@@ -12240,6 +13453,7 @@ export {
|
|
|
12240
13453
|
readJsonInput,
|
|
12241
13454
|
registerPublicDomains,
|
|
12242
13455
|
requireAuth,
|
|
13456
|
+
requireDirectoryTrust,
|
|
12243
13457
|
requireNumber,
|
|
12244
13458
|
requireString,
|
|
12245
13459
|
simpleCrudCommands,
|