@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/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-C6D4FO6A.js";
17
-
18
- // src/lib/errors.ts
19
- var CLIError = class extends Error {
20
- constructor(message, exitCode = 1) {
21
- super(message);
22
- this.exitCode = exitCode;
23
- this.name = "CLIError";
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 = [...COMMON_ARGS, ...defs];
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
- process.stderr.write(`Warning: unknown flag "${flagPart}" (ignored)
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 && result[def.name] === void 0) {
251
- throw new CLIError(`Missing required argument: --${def.name}`);
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(id) {
423
- return this.fetch(`admin/organizations/${id}`);
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(workspaceId) {
644
- return this.fetch(`admin/workspace/${workspaceId}/phone-numbers`);
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
- // Admin Themes API
798
- async listAllThemes() {
799
- return this.fetch("admin/themes/list");
800
- }
801
- // One-off Redis cadence-key migration (periodiq_* → routine_*). See
802
- // lib/routes/admin/admin_routine_cadence_api.py for context.
803
- async migrateRoutineCadenceKeys(options) {
804
- return this.fetch("admin/redis/routine-cadence/migrate", {
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
- dryRun: options.dryRun ?? true,
808
- deleteLegacy: options.deleteLegacy ?? false
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 = new ApiClient(refreshed.accessToken, ctx.envConfig.apiUrl);
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 = new ApiClient(token, ctx.envConfig.apiUrl);
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 = new ApiClient(apiKey, ctx.envConfig.apiUrl);
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 = new ApiClient(creds.accessToken, ctx.envConfig.apiUrl);
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 apiUrl = opts.apiUrl || process.env.GRAVITY_RAIL_API_URL || "https://api.gravityrail.com";
1958
- const frontendUrl = opts.frontendUrl || process.env.GRAVITY_RAIL_FRONTEND_URL || "https://app.gravityrail.com";
1959
- const envConfig = { apiUrl, frontendUrl };
1960
- const credentialsManager = new CredentialsManager(homedir(), apiUrl);
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: "prod",
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 = new ApiClient(apiKey, ctx.envConfig.apiUrl);
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 = new ApiClient(creds.accessToken, ctx.envConfig.apiUrl);
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 (!process.stdin.isTTY) {
2015
- return readStdin();
2230
+ if (process.stdin.isTTY === true) {
2231
+ return void 0;
2016
2232
  }
2017
- return void 0;
2233
+ return readStdinIfPiped();
2018
2234
  }
2019
- function readStdin() {
2235
+ function readStdinIfPiped() {
2020
2236
  return new Promise((resolve, reject) => {
2021
2237
  let data = "";
2022
- process.stdin.setEncoding("utf-8");
2023
- process.stdin.on("data", (chunk) => {
2024
- data += chunk;
2025
- });
2026
- process.stdin.on("end", () => {
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
- resolve(void 0);
2246
+ finish(void 0);
2029
2247
  return;
2030
2248
  }
2031
2249
  try {
2032
- resolve(JSON.parse(data));
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
- requireNumber(args, "chat-id"),
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 = requireNumber(args, "chat-id");
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
- requireNumber(args, "chat-id"),
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.event_type ?? "" },
4979
- COL.enabled()
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 eventRulesCrud = crudCommands({
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, journey:completed,",
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, COLUMNS16);
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, COLUMNS16);
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.type ?? "" },
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(path, description) {
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 ${path.join(" ")} --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write`
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(path, description) {
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 ${path.join(" ")} --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write`
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(path) {
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 ${path.join(" ")} --org-uuid $ORG_UUID --domain example.com --allow-write`
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
- { name: "phone-number-id", description: "Phone number ID", required: true },
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
- requireNumber(args, "phone-number-id"),
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
- { name: "phone-number-id", description: "Phone number ID", required: true },
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
- requireNumber(args, "phone-number-id"),
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: "WORD", getValue: (item) => item.word ?? "" },
8356
- { header: "PRONUNCIATION", getValue: (item) => item.pronunciation ?? "" },
8357
- { header: "TYPE", getValue: (item) => item.type ?? "" }
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 result = await requireAuth(ctx).apiClient.getPronunciationEntries(workspace);
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 result = await requireAuth(ctx).apiClient.createPronunciationEntry(
8379
- workspace,
8380
- data
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 result = await requireAuth(ctx).apiClient.updatePronunciationEntry(
8393
- workspace,
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
- data
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 requireAuth(ctx).apiClient.deletePronunciationEntry(workspace, id);
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 result = await requireAuth(ctx).apiClient.createMyPronunciation(data);
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: [{ name: "word", description: "Word to get suggestions for", required: true }],
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 result = await requireAuth(ctx).apiClient.suggestPronunciations(
8439
- requireString(args, "word")
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 (!taskId && !chatId) {
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, taskId ?? 0, {
8617
- channel,
8618
- ...chatId ? { chatId } : {},
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
- execFile(command, args, (err) => {
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 readline2 from "readline";
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 readline from "readline";
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 = readline.createInterface({
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
- let streamText = "";
11542
- let hasToolCalls = false;
11543
- let eventCount = 0;
11544
- const logFile = process.env.GR_SSE_LOG;
11545
- const fs = logFile ? await import("fs") : null;
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
- const errorMsg = getErrorMessage(event);
11562
- if (errorMsg) process.stderr.write(`\x1B[31mError: ${errorMsg}\x1B[0m
11563
- `);
11564
- },
11565
- { autoApprove }
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 = readline2.createInterface({ input: process.stdin, output: process.stdout });
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
- await aguiClient.sendMessage(conversationMessages, (event) => {
11656
- const delta = getTextDelta(event);
11657
- if (delta) {
11658
- process.stdout.write(delta);
11659
- assistantContent += delta;
11660
- }
11661
- const errorMsg = getErrorMessage(event);
11662
- if (errorMsg) console.log(`
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
- if (assistantContent)
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 COLUMNS33 = [
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, COLUMNS33);
13393
+ outputData(organizations, ctx, COLUMNS34);
12188
13394
  }
12189
13395
 
12190
13396
  // src/commands/whoami.ts
12191
- var COLUMNS34 = [
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
- outputSingle(account, ctx, COLUMNS34);
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,