@gravity-rail/cli 0.4.0 → 0.5.0

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
@@ -45,9 +45,11 @@ function handleError(err) {
45
45
  }
46
46
  }
47
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
- `);
48
+ process.stderr.write(
49
+ `
50
+ Hint: Run "gr login" to authenticate, or set GRAVITY_RAIL_API_KEY for non-interactive use.
51
+ `
52
+ );
51
53
  }
52
54
  process.exit(1);
53
55
  }
@@ -277,7 +279,67 @@ __reExport(dist_exports, internal_star);
277
279
  import * as internal_star from "@gravity-rail/sdk/internal";
278
280
  import { BaseApiClient } from "@gravity-rail/sdk/internal";
279
281
  import { GravityRailClient } from "@gravity-rail/sdk";
282
+ function supportRequestParams(params) {
283
+ const searchParams = new URLSearchParams();
284
+ if (params?.page) searchParams.append("page", params.page.toString());
285
+ if (params?.pageSize) searchParams.append("pageSize", params.pageSize.toString());
286
+ if (params?.status) searchParams.append("status", params.status);
287
+ if (params?.supportRequestType) {
288
+ searchParams.append("supportRequestType", params.supportRequestType);
289
+ }
290
+ if (params?.severity) searchParams.append("severity", params.severity);
291
+ if (params?.source) searchParams.append("source", params.source);
292
+ if (params?.organizationId !== void 0) {
293
+ searchParams.append("organizationId", params.organizationId.toString());
294
+ }
295
+ if (params?.workspaceId !== void 0) {
296
+ searchParams.append("workspaceId", params.workspaceId.toString());
297
+ }
298
+ if (params?.linearLinked !== void 0) {
299
+ searchParams.append("linearLinked", params.linearLinked.toString());
300
+ }
301
+ const query = searchParams.toString();
302
+ return query ? `?${query}` : "";
303
+ }
280
304
  var AdminApi = class extends BaseApiClient {
305
+ // Support Requests API
306
+ async listSupportRequestsAsAdmin(params) {
307
+ return this.fetch(
308
+ `admin/support-requests${supportRequestParams(params)}`
309
+ );
310
+ }
311
+ async getSupportRequestAsAdmin(uuid) {
312
+ return this.fetch(`admin/support-requests/${uuid}`);
313
+ }
314
+ async commentOnSupportRequestAsAdmin(uuid, data) {
315
+ return this.fetch(`admin/support-requests/${uuid}/comments`, {
316
+ method: "POST",
317
+ body: JSON.stringify(data)
318
+ });
319
+ }
320
+ async noteOnSupportRequestAsAdmin(uuid, data) {
321
+ return this.fetch(`admin/support-requests/${uuid}/notes`, {
322
+ method: "POST",
323
+ body: JSON.stringify(data)
324
+ });
325
+ }
326
+ async updateSupportRequestStatusAsAdmin(uuid, data) {
327
+ return this.fetch(`admin/support-requests/${uuid}/status`, {
328
+ method: "POST",
329
+ body: JSON.stringify(data)
330
+ });
331
+ }
332
+ async updateSupportRequestAttributesAsAdmin(uuid, data) {
333
+ return this.fetch(`admin/support-requests/${uuid}/attributes`, {
334
+ method: "POST",
335
+ body: JSON.stringify(data)
336
+ });
337
+ }
338
+ async createSupportRequestLinearIssueAsAdmin(uuid) {
339
+ return this.fetch(`admin/support-requests/${uuid}/linear-issue`, {
340
+ method: "POST"
341
+ });
342
+ }
281
343
  // Admin Accounts API
282
344
  async getAccounts(options) {
283
345
  const params = new URLSearchParams();
@@ -416,11 +478,11 @@ var AdminApi = class extends BaseApiClient {
416
478
  return this.fetch(`o/${idOrUuid}`);
417
479
  }
418
480
  /**
419
- * Get organization by numeric ID via admin endpoint.
481
+ * Get organization by numeric ID or UUID via admin endpoint.
420
482
  * Does NOT require organization membership — superuser-only.
421
483
  */
422
- async getOrganizationAsAdmin(id) {
423
- return this.fetch(`admin/organizations/${id}`);
484
+ async getOrganizationAsAdmin(idOrUuid) {
485
+ return this.fetch(`admin/organizations/${idOrUuid}`);
424
486
  }
425
487
  async getOrganizationMembersAsAdmin(organizationId, options) {
426
488
  const params = new URLSearchParams();
@@ -597,6 +659,26 @@ var AdminApi = class extends BaseApiClient {
597
659
  body: JSON.stringify(request || {})
598
660
  });
599
661
  }
662
+ /**
663
+ * Switch a workspace's plan product on its active SubscriptionItem.
664
+ *
665
+ * Superuser only. Re-syncs ``workspace.features`` from the new product
666
+ * and emits a ``WORKSPACE_PRODUCT_CHANGED`` audit event identifying the
667
+ * actor, target workspace, prior and new product, and feature diff.
668
+ *
669
+ * Stripe is not touched here; price-id changes belong to the dedicated
670
+ * Stripe sync surface (``syncProductToStripe``).
671
+ */
672
+ async changeWorkspaceProduct(workspaceUuid, productId) {
673
+ const body = { productId };
674
+ return this.fetch(
675
+ `admin/workspaces/${workspaceUuid}/product`,
676
+ {
677
+ method: "POST",
678
+ body: JSON.stringify(body)
679
+ }
680
+ );
681
+ }
600
682
  // ===== Workflow Access Validation =====
601
683
  async validateWorkflowAccessAsAdmin(workspaceUuid) {
602
684
  const params = new URLSearchParams();
@@ -629,6 +711,11 @@ var AdminApi = class extends BaseApiClient {
629
711
  body: JSON.stringify(data)
630
712
  });
631
713
  }
714
+ async unarchiveAdminWorkspaceMember(wid) {
715
+ return this.fetch(`admin/workspaces/${wid}/me/unarchive`, {
716
+ method: "POST"
717
+ });
718
+ }
632
719
  async getAdminWorkspaceMemberRoles(wid) {
633
720
  return this.fetch(`admin/workspaces/${wid}/member-roles`);
634
721
  }
@@ -640,8 +727,8 @@ var AdminApi = class extends BaseApiClient {
640
727
  `admin/phone-numbers/workspace/${workspaceId}/unmapped`
641
728
  );
642
729
  }
643
- async getWorkspacePhoneNumbersAsAdmin(workspaceId) {
644
- return this.fetch(`admin/workspace/${workspaceId}/phone-numbers`);
730
+ async getWorkspacePhoneNumbersAsAdmin(workspaceRef) {
731
+ return this.fetch(`admin/workspaces/${workspaceRef}/phone-numbers`);
645
732
  }
646
733
  // Phone Numbers API (Provider phone numbers from Twilio, AWS, etc.)
647
734
  async getProviderPhoneNumbers(options = {}) {
@@ -750,6 +837,11 @@ var AdminApi = class extends BaseApiClient {
750
837
  method: "DELETE"
751
838
  });
752
839
  }
840
+ async getWorkspaceInboxesAsAdmin(workspaceUuid) {
841
+ return this.fetch(
842
+ `admin/workspaces/${workspaceUuid}/inboxes`
843
+ );
844
+ }
753
845
  // Admin Inbox Mappings API
754
846
  async getInboxMappings(options = {}) {
755
847
  const params = new URLSearchParams();
@@ -794,21 +886,30 @@ var AdminApi = class extends BaseApiClient {
794
886
  })
795
887
  });
796
888
  }
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", {
889
+ /**
890
+ * Resolve a Langfuse trace UI URL from a trace ID, chat ID, or message ID.
891
+ *
892
+ * URL-only: never fetches Langfuse payloads, Message content, or
893
+ * tool arguments. Exactly one of ``traceId``, ``chatId``, or
894
+ * ``messageId`` must be set; chat/message forms require a
895
+ * workspaceUuid. See
896
+ * docs/development/observability/llm-call-investigation.md.
897
+ */
898
+ async getTraceUrl(options) {
899
+ return this.fetch("admin/trace/url", {
805
900
  method: "POST",
806
901
  body: JSON.stringify({
807
- dryRun: options.dryRun ?? true,
808
- deleteLegacy: options.deleteLegacy ?? false
902
+ trace_id: options.traceId,
903
+ chat_id: options.chatId,
904
+ message_id: options.messageId,
905
+ workspace_uuid: options.workspaceUuid
809
906
  })
810
907
  });
811
908
  }
909
+ // Admin Themes API
910
+ async listAllThemes() {
911
+ return this.fetch("admin/themes/list");
912
+ }
812
913
  // Missing Memberships API
813
914
  async getMissingMemberships(workspaceId) {
814
915
  return this.fetch(
@@ -1381,6 +1482,13 @@ function appendTemporalWorkspaceParams(params, options) {
1381
1482
  push(options.workspaceUuid);
1382
1483
  }
1383
1484
  function bindAdminMethods(target, admin) {
1485
+ target.listSupportRequestsAsAdmin = admin.listSupportRequestsAsAdmin.bind(admin);
1486
+ target.getSupportRequestAsAdmin = admin.getSupportRequestAsAdmin.bind(admin);
1487
+ target.commentOnSupportRequestAsAdmin = admin.commentOnSupportRequestAsAdmin.bind(admin);
1488
+ target.noteOnSupportRequestAsAdmin = admin.noteOnSupportRequestAsAdmin.bind(admin);
1489
+ target.updateSupportRequestStatusAsAdmin = admin.updateSupportRequestStatusAsAdmin.bind(admin);
1490
+ target.updateSupportRequestAttributesAsAdmin = admin.updateSupportRequestAttributesAsAdmin.bind(admin);
1491
+ target.createSupportRequestLinearIssueAsAdmin = admin.createSupportRequestLinearIssueAsAdmin.bind(admin);
1384
1492
  target.getAccounts = admin.getAccounts.bind(admin);
1385
1493
  target.getAccountDetails = admin.getAccountDetails.bind(admin);
1386
1494
  target.getAccountByUuid = admin.getAccountByUuid.bind(admin);
@@ -1429,11 +1537,13 @@ function bindAdminMethods(target, admin) {
1429
1537
  target.disableMaintenanceMode = admin.disableMaintenanceMode.bind(admin);
1430
1538
  target.getWorkspaceHealth = admin.getWorkspaceHealth.bind(admin);
1431
1539
  target.provisionPlanFeatures = admin.provisionPlanFeatures.bind(admin);
1540
+ target.changeWorkspaceProduct = admin.changeWorkspaceProduct.bind(admin);
1432
1541
  target.validateWorkflowAccessAsAdmin = admin.validateWorkflowAccessAsAdmin.bind(admin);
1433
1542
  target.mergeMembers = admin.mergeMembers.bind(admin);
1434
1543
  target.getAdminWorkspaceMember = admin.getAdminWorkspaceMember.bind(admin);
1435
1544
  target.createAdminWorkspaceMember = admin.createAdminWorkspaceMember.bind(admin);
1436
1545
  target.updateAdminWorkspaceMember = admin.updateAdminWorkspaceMember.bind(admin);
1546
+ target.unarchiveAdminWorkspaceMember = admin.unarchiveAdminWorkspaceMember.bind(admin);
1437
1547
  target.getAdminWorkspaceMemberRoles = admin.getAdminWorkspaceMemberRoles.bind(admin);
1438
1548
  target.getMissingMemberships = admin.getMissingMemberships.bind(admin);
1439
1549
  target.linkMissingMembership = admin.linkMissingMembership.bind(admin);
@@ -1464,8 +1574,8 @@ function bindAdminMethods(target, admin) {
1464
1574
  target.deleteAdminInboxMapping = admin.deleteAdminInboxMapping.bind(admin);
1465
1575
  target.generateChatSummaries = admin.generateChatSummaries.bind(admin);
1466
1576
  target.executeQuery = admin.executeQuery.bind(admin);
1577
+ target.getTraceUrl = admin.getTraceUrl.bind(admin);
1467
1578
  target.listAllThemes = admin.listAllThemes.bind(admin);
1468
- target.migrateRoutineCadenceKeys = admin.migrateRoutineCadenceKeys.bind(admin);
1469
1579
  target.startBulkTOSAcceptance = admin.startBulkTOSAcceptance.bind(admin);
1470
1580
  target.startTokenUsageBackfill = admin.startTokenUsageBackfill.bind(admin);
1471
1581
  target.updateChatMessage = admin.updateChatMessage.bind(admin);
@@ -1525,6 +1635,7 @@ function bindAdminMethods(target, admin) {
1525
1635
  target.getVoiceMetrics = admin.getVoiceMetrics.bind(admin);
1526
1636
  target.getHealthMetrics = admin.getHealthMetrics.bind(admin);
1527
1637
  target.getHealthDetail = admin.getHealthDetail.bind(admin);
1638
+ target.getWorkspaceInboxesAsAdmin = admin.getWorkspaceInboxesAsAdmin.bind(admin);
1528
1639
  target.listWorkspaceChats = admin.listWorkspaceChats.bind(admin);
1529
1640
  target.getWorkspaceChat = admin.getWorkspaceChat.bind(admin);
1530
1641
  target.rateWorkspaceChatMessage = admin.rateWorkspaceChatMessage.bind(admin);
@@ -1543,6 +1654,13 @@ function bindAdminMethods(target, admin) {
1543
1654
  }
1544
1655
  var ApiClient = class extends GravityRailClient {
1545
1656
  // Admin API
1657
+ listSupportRequestsAsAdmin;
1658
+ getSupportRequestAsAdmin;
1659
+ commentOnSupportRequestAsAdmin;
1660
+ noteOnSupportRequestAsAdmin;
1661
+ updateSupportRequestStatusAsAdmin;
1662
+ updateSupportRequestAttributesAsAdmin;
1663
+ createSupportRequestLinearIssueAsAdmin;
1546
1664
  getAccounts;
1547
1665
  getAccountDetails;
1548
1666
  getAccountByUuid;
@@ -1591,11 +1709,13 @@ var ApiClient = class extends GravityRailClient {
1591
1709
  disableMaintenanceMode;
1592
1710
  getWorkspaceHealth;
1593
1711
  provisionPlanFeatures;
1712
+ changeWorkspaceProduct;
1594
1713
  validateWorkflowAccessAsAdmin;
1595
1714
  mergeMembers;
1596
1715
  getAdminWorkspaceMember;
1597
1716
  createAdminWorkspaceMember;
1598
1717
  updateAdminWorkspaceMember;
1718
+ unarchiveAdminWorkspaceMember;
1599
1719
  getAdminWorkspaceMemberRoles;
1600
1720
  getMissingMemberships;
1601
1721
  linkMissingMembership;
@@ -1626,8 +1746,8 @@ var ApiClient = class extends GravityRailClient {
1626
1746
  deleteAdminInboxMapping;
1627
1747
  generateChatSummaries;
1628
1748
  executeQuery;
1749
+ getTraceUrl;
1629
1750
  listAllThemes;
1630
- migrateRoutineCadenceKeys;
1631
1751
  startBulkTOSAcceptance;
1632
1752
  startTokenUsageBackfill;
1633
1753
  updateChatMessage;
@@ -1681,6 +1801,7 @@ var ApiClient = class extends GravityRailClient {
1681
1801
  getVoiceMetrics;
1682
1802
  getHealthMetrics;
1683
1803
  getHealthDetail;
1804
+ getWorkspaceInboxesAsAdmin;
1684
1805
  listWorkspaceChats;
1685
1806
  getWorkspaceChat;
1686
1807
  rateWorkspaceChatMessage;
@@ -1717,6 +1838,10 @@ var ApiClient = class extends GravityRailClient {
1717
1838
  this.adminApi.setActingMember(memberUuid);
1718
1839
  }
1719
1840
  }
1841
+ // setOrgToken is intentionally NOT overridden here. Admin routes
1842
+ // (/api/v2/admin/*) are protected by the bearer token's superuser claim and
1843
+ // ignore X-Org-Token entirely, so propagating an org token to adminApi is
1844
+ // dead code that would only obscure auth flow if admin route logic changes.
1720
1845
  };
1721
1846
 
1722
1847
  // src/lib/context.ts
@@ -1892,7 +2017,131 @@ function loadRealmCredentials(realm, devRealmsPath) {
1892
2017
  }
1893
2018
  }
1894
2019
 
2020
+ // src/lib/org-auth.ts
2021
+ import { execFile } from "child_process";
2022
+ var ORG_AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
2023
+ function extractOrgTokenFromSetCookies(setCookieValues, preferredCookieName) {
2024
+ let fallback;
2025
+ for (const value of setCookieValues) {
2026
+ const nameValue = value.split(";")[0]?.trim() ?? "";
2027
+ const eqIdx = nameValue.indexOf("=");
2028
+ if (eqIdx === -1) continue;
2029
+ const name = nameValue.slice(0, eqIdx).trim();
2030
+ const tokenValue = nameValue.slice(eqIdx + 1).trim() || void 0;
2031
+ if (!tokenValue) continue;
2032
+ if (name === preferredCookieName) return tokenValue;
2033
+ if (!fallback && (name.startsWith("orgToken") || name.startsWith("orgSession"))) {
2034
+ fallback = tokenValue;
2035
+ }
2036
+ }
2037
+ return fallback;
2038
+ }
2039
+ async function tryDirectJoin(apiUrl, accessToken, orgAuthInfo) {
2040
+ const response = await fetch(`${apiUrl}/api/v2/o/${orgAuthInfo.orgUuid}/join`, {
2041
+ method: "POST",
2042
+ headers: {
2043
+ "Content-Type": "application/json",
2044
+ Authorization: `Bearer ${accessToken}`
2045
+ },
2046
+ body: "{}"
2047
+ });
2048
+ if (response.status === 400) {
2049
+ return null;
2050
+ }
2051
+ if (!response.ok) {
2052
+ throw new Error(`Org join failed (HTTP ${response.status})`);
2053
+ }
2054
+ const rawHeaders = response.headers;
2055
+ const setCookies = rawHeaders.getSetCookie?.() ?? (rawHeaders.get("set-cookie") ? [rawHeaders.get("set-cookie")] : []);
2056
+ if (setCookies.length === 0) {
2057
+ throw new Error("Org join succeeded but no Set-Cookie header returned");
2058
+ }
2059
+ const orgToken = extractOrgTokenFromSetCookies(setCookies, orgAuthInfo.cookieName);
2060
+ if (!orgToken) {
2061
+ throw new Error("Org join succeeded but org token cookie not found in Set-Cookie");
2062
+ }
2063
+ return orgToken;
2064
+ }
2065
+ function isAllowedAuthUrl(url, apiUrl) {
2066
+ try {
2067
+ const parsed = new URL(url);
2068
+ const api = new URL(apiUrl);
2069
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return false;
2070
+ if (parsed.hostname === api.hostname) return true;
2071
+ const lastTwo = (host) => {
2072
+ const parts = host.split(".").filter(Boolean);
2073
+ return parts.length >= 2 ? parts.slice(-2).join(".") : host;
2074
+ };
2075
+ const isIp = /^[0-9.]+$|^\[.*\]$/.test(parsed.hostname);
2076
+ if (isIp) return false;
2077
+ return lastTwo(parsed.hostname) === lastTwo(api.hostname);
2078
+ } catch {
2079
+ return false;
2080
+ }
2081
+ }
2082
+ function openBrowser(url) {
2083
+ let command;
2084
+ let args;
2085
+ if (process.platform === "darwin") {
2086
+ command = "open";
2087
+ args = [url];
2088
+ } else if (process.platform === "win32") {
2089
+ command = "cmd.exe";
2090
+ args = ["/c", "start", "", url];
2091
+ } else {
2092
+ command = "xdg-open";
2093
+ args = [url];
2094
+ }
2095
+ execFile(command, args, (_err) => {
2096
+ });
2097
+ }
2098
+ function registerOrgAuthHandler(apiClient, apiUrl, accessToken) {
2099
+ apiClient.setOrgAuthHandler(async (orgAuthInfo) => {
2100
+ try {
2101
+ const orgToken = await tryDirectJoin(apiUrl, accessToken, orgAuthInfo);
2102
+ if (orgToken !== null) {
2103
+ apiClient.setOrgToken(orgToken, orgAuthInfo.orgUuid);
2104
+ return;
2105
+ }
2106
+ } catch (err) {
2107
+ const msg = err instanceof Error ? err.message : String(err);
2108
+ stderr(`Warning: org direct-join failed (${msg}); trying browser auth`);
2109
+ }
2110
+ if (!isAllowedAuthUrl(orgAuthInfo.authUrl, apiUrl)) {
2111
+ throw new Error(
2112
+ `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.`
2113
+ );
2114
+ }
2115
+ stderr(`
2116
+ This workspace requires organization SSO authentication.`);
2117
+ stderr(`Opening browser to complete authentication...`);
2118
+ stderr(` ${orgAuthInfo.authUrl}
2119
+ `);
2120
+ stderr(`If the browser does not open, visit the URL above manually.`);
2121
+ stderr(
2122
+ `After signing in, re-run the command \u2014 the CLI cannot receive the SSO token directly.`
2123
+ );
2124
+ openBrowser(orgAuthInfo.authUrl);
2125
+ await new Promise((_, reject) => {
2126
+ const t = setTimeout(
2127
+ () => reject(
2128
+ new Error(
2129
+ "Organization authentication timed out.\nPlease complete authentication in the browser, then re-run the command."
2130
+ )
2131
+ ),
2132
+ ORG_AUTH_TIMEOUT_MS
2133
+ );
2134
+ t.unref?.();
2135
+ });
2136
+ });
2137
+ }
2138
+
1895
2139
  // src/lib/context.ts
2140
+ function makeApiClient(accessToken, apiUrl) {
2141
+ const apiClient = new ApiClient(accessToken, apiUrl);
2142
+ registerOrgAuthHandler(apiClient, apiUrl, accessToken);
2143
+ return apiClient;
2144
+ }
1896
2145
  async function tryTokenRefresh(ctx, creds) {
1897
2146
  if (!creds.refreshToken) return null;
1898
2147
  const refreshed = await refreshAccessToken(
@@ -1902,7 +2151,7 @@ async function tryTokenRefresh(ctx, creds) {
1902
2151
  ctx.env
1903
2152
  );
1904
2153
  if (!refreshed?.accessToken) return null;
1905
- const apiClient = new ApiClient(refreshed.accessToken, ctx.envConfig.apiUrl);
2154
+ const apiClient = makeApiClient(refreshed.accessToken, ctx.envConfig.apiUrl);
1906
2155
  return { ...ctx, accessToken: refreshed.accessToken, apiClient };
1907
2156
  }
1908
2157
  function createContext(opts) {
@@ -1926,12 +2175,12 @@ async function createAuthenticatedContext(opts) {
1926
2175
  devRealmsPath = loadDevCloudRealmFile(namespace);
1927
2176
  }
1928
2177
  const { token, workspaceUuid } = loadRealmCredentials(opts.realm, devRealmsPath);
1929
- const apiClient = new ApiClient(token, ctx.envConfig.apiUrl);
2178
+ const apiClient = makeApiClient(token, ctx.envConfig.apiUrl);
1930
2179
  return { ...ctx, accessToken: token, apiClient, realmWorkspace: workspaceUuid };
1931
2180
  }
1932
2181
  const apiKey = process.env.GRAVITY_RAIL_API_KEY;
1933
2182
  if (apiKey) {
1934
- const apiClient = new ApiClient(apiKey, ctx.envConfig.apiUrl);
2183
+ const apiClient = makeApiClient(apiKey, ctx.envConfig.apiUrl);
1935
2184
  return { ...ctx, accessToken: apiKey, apiClient };
1936
2185
  }
1937
2186
  const creds = ctx.credentialsManager.load(ctx.env);
@@ -1939,7 +2188,7 @@ async function createAuthenticatedContext(opts) {
1939
2188
  const buffer = 5 * 60 * 1e3;
1940
2189
  const notExpired = !creds.expiresAt || new Date(creds.expiresAt) > new Date(Date.now() + buffer);
1941
2190
  if (notExpired) {
1942
- const apiClient = new ApiClient(creds.accessToken, ctx.envConfig.apiUrl);
2191
+ const apiClient = makeApiClient(creds.accessToken, ctx.envConfig.apiUrl);
1943
2192
  return { ...ctx, accessToken: creds.accessToken, apiClient };
1944
2193
  }
1945
2194
  const refreshed = await tryTokenRefresh(ctx, creds);
@@ -1971,7 +2220,7 @@ async function createPublicAuthenticatedContext(opts) {
1971
2220
  const ctx = createPublicContext(opts);
1972
2221
  const apiKey = process.env.GRAVITY_RAIL_API_KEY;
1973
2222
  if (apiKey) {
1974
- const apiClient = new ApiClient(apiKey, ctx.envConfig.apiUrl);
2223
+ const apiClient = makeApiClient(apiKey, ctx.envConfig.apiUrl);
1975
2224
  return { ...ctx, accessToken: apiKey, apiClient };
1976
2225
  }
1977
2226
  const creds = ctx.credentialsManager.load(ctx.env);
@@ -1979,7 +2228,7 @@ async function createPublicAuthenticatedContext(opts) {
1979
2228
  const buffer = 5 * 60 * 1e3;
1980
2229
  const notExpired = !creds.expiresAt || new Date(creds.expiresAt) > new Date(Date.now() + buffer);
1981
2230
  if (notExpired) {
1982
- const apiClient = new ApiClient(creds.accessToken, ctx.envConfig.apiUrl);
2231
+ const apiClient = makeApiClient(creds.accessToken, ctx.envConfig.apiUrl);
1983
2232
  return { ...ctx, accessToken: creds.accessToken, apiClient };
1984
2233
  }
1985
2234
  const refreshed = await tryTokenRefresh(ctx, creds);
@@ -4975,8 +5224,8 @@ var dataTypesDomain = {
4975
5224
  var RULE_COLUMNS = [
4976
5225
  COL.id(),
4977
5226
  COL.name(),
4978
- { header: "EVENT", getValue: (item) => item.event_type ?? "" },
4979
- COL.enabled()
5227
+ { header: "EVENT", getValue: (item) => item.eventType ?? "" },
5228
+ { header: "ACTIVE", getValue: (item) => String(item.active ?? "") }
4980
5229
  ];
4981
5230
  var EVENT_COLUMNS2 = [
4982
5231
  COL.id(),
@@ -4984,19 +5233,13 @@ var EVENT_COLUMNS2 = [
4984
5233
  { header: "SLUG", getValue: (item) => item.slug ?? "" },
4985
5234
  COL.status()
4986
5235
  ];
4987
- var eventRulesCrud = crudCommands({
5236
+ var eventRulesCrudNoList = crudCommands({
4988
5237
  prefix: ["events", "rules"],
4989
5238
  entityName: "event rule",
4990
- listMethod: "getEventRules",
4991
5239
  getMethod: "getEventRule",
4992
5240
  createMethod: "createEventRule",
4993
5241
  updateMethod: "updateEventRule",
4994
5242
  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
5243
  columns: RULE_COLUMNS,
5001
5244
  createExamples: [
5002
5245
  `gr events rules create -w $WID --data '{"name":"Label on step","objectType":"journey",`,
@@ -5015,6 +5258,57 @@ var eventRulesCrud = crudCommands({
5015
5258
  'member:label actionParams: {"addLabelIds":["uuid"],"removeLabelIds":["uuid"]}'
5016
5259
  ]
5017
5260
  });
5261
+ var eventRulesListCmd = {
5262
+ path: ["events", "rules", "list"],
5263
+ description: "List event rules (use --journey-step-id or --event-type to filter)",
5264
+ requiresWorkspace: true,
5265
+ columns: RULE_COLUMNS,
5266
+ args: [
5267
+ { name: "journey-step-id", description: "Filter by journey step ID" },
5268
+ { name: "event-type", description: "Filter by event type (e.g. journey_step:execute)" },
5269
+ { name: "object-type", description: "Filter by object type (e.g. journey, task)" },
5270
+ { name: "include-disabled", description: "Include inactive rules", isFlag: true },
5271
+ { name: "page", description: "Page number (without filters)" },
5272
+ { name: "page-size", description: "Page size (without filters)" },
5273
+ {
5274
+ name: "all",
5275
+ description: "Fetch all pages (without filters)",
5276
+ isFlag: true
5277
+ }
5278
+ ],
5279
+ handler: async ({ ctx, workspace, args }) => {
5280
+ const client = requireAuth(ctx).apiClient;
5281
+ const journeyStepId = args["journey-step-id"] ? requireNumber(args, "journey-step-id") : void 0;
5282
+ const eventType = args["event-type"];
5283
+ const objectType = args["object-type"];
5284
+ const includeDisabled = args["include-disabled"] === true;
5285
+ if (journeyStepId !== void 0 || eventType !== void 0 || objectType !== void 0 || includeDisabled) {
5286
+ const result2 = await client.searchEventRules(workspace, {
5287
+ ...journeyStepId !== void 0 ? { journeyStepId } : {},
5288
+ ...eventType ? { eventType } : {},
5289
+ ...objectType ? { objectType } : {},
5290
+ ...includeDisabled ? { includeDisabled: true } : {}
5291
+ });
5292
+ outputData(listResult(result2, false), ctx, RULE_COLUMNS);
5293
+ return;
5294
+ }
5295
+ if (args["all"]) {
5296
+ const allItems = await fetchAllPages(
5297
+ async ({ page: page2, pageSize: pageSize2 }) => client.getEventRules(workspace, { page: page2, pageSize: pageSize2 })
5298
+ );
5299
+ outputData(allItems, ctx, RULE_COLUMNS);
5300
+ return;
5301
+ }
5302
+ const page = args["page"] ? requireNumber(args, "page") : void 0;
5303
+ const pageSize = args["page-size"] ? requireNumber(args, "page-size") : void 0;
5304
+ const result = await client.getEventRules(workspace, {
5305
+ ...page !== void 0 ? { page } : {},
5306
+ ...pageSize !== void 0 ? { pageSize } : {}
5307
+ });
5308
+ outputData(listResult(result, true), ctx, RULE_COLUMNS);
5309
+ }
5310
+ };
5311
+ var eventRulesCrud = [eventRulesListCmd, ...eventRulesCrudNoList];
5018
5312
  var eventRulesAliases = eventRulesCrud.filter((cmd) => {
5019
5313
  const verb = cmd.path[cmd.path.length - 1];
5020
5314
  return verb === "list" || verb === "get";
@@ -5224,6 +5518,7 @@ var filesDomain = {
5224
5518
  };
5225
5519
 
5226
5520
  // src/domains/journeys.ts
5521
+ import { EventRuleEventType } from "@gravity-rail/sdk";
5227
5522
  var JOURNEY_COLUMNS = [
5228
5523
  COL.id(),
5229
5524
  { header: "UUID", getValue: (item) => (item.uuid ?? "").slice(0, 8) },
@@ -5287,6 +5582,15 @@ var NOTE_COLUMNS2 = [
5287
5582
  { header: "CONTENT", getValue: (item) => (item.contents ?? "").slice(0, 60) },
5288
5583
  { header: "CREATED", getValue: (item) => item.createdAt ?? "" }
5289
5584
  ];
5585
+ var ACTION_COLUMNS = [
5586
+ COL.id(),
5587
+ { header: "UUID", getValue: (item) => (item.uuid ?? "").slice(0, 8) },
5588
+ COL.name(),
5589
+ { header: "EVENT", getValue: (item) => item.eventType ?? "" },
5590
+ { header: "ACTION", getValue: (item) => item.actionType ?? "" },
5591
+ { header: "ACTIVE", getValue: (item) => String(item.active ?? "") },
5592
+ { header: "CONDITIONS", getValue: (item) => (item.conditions ?? "").slice(0, 40) }
5593
+ ];
5290
5594
  function baseUrl(ctx, wid) {
5291
5595
  return `${ctx.envConfig.apiUrl}/api/v2/w/${wid}/journeys`;
5292
5596
  }
@@ -6001,6 +6305,70 @@ var journeysDomain = {
6001
6305
  process.stdout.write(`Deleted note ${noteId}
6002
6306
  `);
6003
6307
  }
6308
+ },
6309
+ // ── Step execute actions (event rules scoped to a step) ────────
6310
+ {
6311
+ path: ["journeys", "steps", "actions", "list"],
6312
+ description: "List event rules (actions) attached to a journey step",
6313
+ requiresWorkspace: true,
6314
+ args: [
6315
+ { name: "step-id", description: "Journey step ID (numeric)", required: true },
6316
+ {
6317
+ name: "include-disabled",
6318
+ description: "Include inactive/disabled rules",
6319
+ isFlag: true
6320
+ }
6321
+ ],
6322
+ examples: [
6323
+ "gr journeys steps actions list -w $WID --step-id <step-id>",
6324
+ "gr journeys steps actions list -w $WID --step-id <step-id> --include-disabled"
6325
+ ],
6326
+ handler: async ({ ctx, workspace, args }) => {
6327
+ const stepId = requireNumber(args, "step-id");
6328
+ const includeDisabled = args["include-disabled"] === true;
6329
+ const result = await requireAuth(ctx).apiClient.searchEventRules(workspace, {
6330
+ eventType: EventRuleEventType.JOURNEY_STEP_EXECUTE,
6331
+ journeyStepId: stepId,
6332
+ ...includeDisabled ? { includeDisabled: true } : {}
6333
+ });
6334
+ outputData(listResult(result, false), ctx, ACTION_COLUMNS);
6335
+ }
6336
+ },
6337
+ {
6338
+ path: ["journeys", "steps", "actions", "get"],
6339
+ description: "Get a journey step action (event rule) by ID",
6340
+ requiresWorkspace: true,
6341
+ args: [{ name: "id", description: "Event rule ID", required: true }],
6342
+ examples: ["gr journeys steps actions get -w $WID --id <rule-id>"],
6343
+ handler: async ({ ctx, workspace, args }) => {
6344
+ const id = requireNumber(args, "id");
6345
+ const result = await requireAuth(ctx).apiClient.getEventRule(workspace, id);
6346
+ outputSingle(result, ctx, ACTION_COLUMNS);
6347
+ }
6348
+ },
6349
+ {
6350
+ path: ["journeys", "steps", "actions", "update"],
6351
+ description: "Update a journey step action \u2014 modify template, conditions, or active state",
6352
+ mutates: true,
6353
+ requiresWorkspace: true,
6354
+ args: [{ name: "id", description: "Event rule ID", required: true }],
6355
+ examples: [
6356
+ "gr journeys steps actions update -w $WID --id <rule-id> \\",
6357
+ ` --data '{"actionParams":{"messageTemplate":"Hello {{member.name}}"}}' --allow-write`,
6358
+ "",
6359
+ "Updatable fields: name, conditions (CEL), actionParams, active, delay, order",
6360
+ "Non-updatable: objectType, eventType, actionType, journeyStepId"
6361
+ ],
6362
+ handler: async ({ ctx, workspace, args, data }) => {
6363
+ if (!data) throw new CLIError("JSON data required (--data, --file, or stdin)");
6364
+ const id = requireNumber(args, "id");
6365
+ const result = await requireAuth(ctx).apiClient.updateEventRule(
6366
+ workspace,
6367
+ id,
6368
+ data
6369
+ );
6370
+ outputSingle(result, ctx, ACTION_COLUMNS);
6371
+ }
6004
6372
  }
6005
6373
  ]
6006
6374
  };
@@ -6626,6 +6994,13 @@ var COLUMNS16 = [
6626
6994
  { header: "PREFIX", getValue: (item) => item.prefix ?? "" },
6627
6995
  COL.created()
6628
6996
  ];
6997
+ var CREATE_COLUMNS = [
6998
+ COL.id(),
6999
+ COL.name(),
7000
+ { header: "KEY", getValue: (item) => item.key ?? "" },
7001
+ { header: "PREFIX", getValue: (item) => item.prefix ?? "" },
7002
+ COL.created()
7003
+ ];
6629
7004
  var apiKeysDomain = {
6630
7005
  name: "api-keys",
6631
7006
  description: "Manage API keys",
@@ -6646,7 +7021,10 @@ var apiKeysDomain = {
6646
7021
  requiresWorkspace: true,
6647
7022
  handler: async ({ ctx, workspace, data }) => {
6648
7023
  const result = await requireAuth(ctx).apiClient.createApiKey(workspace, data);
6649
- outputSingle(result, ctx, COLUMNS16);
7024
+ outputSingle(result, ctx, CREATE_COLUMNS);
7025
+ if (ctx.outputFormat === "table") {
7026
+ process.stdout.write("\n\u26A0 This key will not be shown again. Copy it now.\n");
7027
+ }
6650
7028
  }
6651
7029
  },
6652
7030
  {
@@ -6678,7 +7056,10 @@ var apiKeysDomain = {
6678
7056
  requiresWorkspace: false,
6679
7057
  handler: async ({ ctx, data }) => {
6680
7058
  const result = await requireAuth(ctx).apiClient.createGlobalApiKey(data);
6681
- outputSingle(result, ctx, COLUMNS16);
7059
+ outputSingle(result, ctx, CREATE_COLUMNS);
7060
+ if (ctx.outputFormat === "table") {
7061
+ process.stdout.write("\n\u26A0 This key will not be shown again. Copy it now.\n");
7062
+ }
6682
7063
  }
6683
7064
  },
6684
7065
  {
@@ -7584,7 +7965,7 @@ var milestonesDomain = {
7584
7965
  var COLUMNS23 = [
7585
7966
  COL.id(),
7586
7967
  COL.name(),
7587
- { header: "TYPE", getValue: (item) => item.type ?? "" },
7968
+ { header: "TYPE", getValue: (item) => item.noticeType ?? "" },
7588
7969
  COL.enabled()
7589
7970
  ];
7590
7971
  var notificationRulesDomain = {
@@ -8603,20 +8984,20 @@ var promptsDomain = {
8603
8984
  "gr prompts inspect --chat-id 789 --turn 3"
8604
8985
  ],
8605
8986
  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) {
8987
+ const taskId = args["task-id"] !== void 0 ? requireNumber(args, "task-id") : void 0;
8988
+ const chatId = args["chat-id"] !== void 0 ? requireNumber(args, "chat-id") : void 0;
8989
+ if (taskId === void 0 && chatId === void 0) {
8609
8990
  throw new Error("Provide --task-id or --chat-id");
8610
8991
  }
8611
- if (taskId && chatId) {
8992
+ if (taskId !== void 0 && chatId !== void 0) {
8612
8993
  throw new Error("Provide --task-id or --chat-id, not both");
8613
8994
  }
8614
8995
  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 } : {}
8996
+ const turn = args.turn !== void 0 ? requireNumber(args, "turn") : void 0;
8997
+ const result = await requireAuth(ctx).apiClient.inspectPrompt(workspace, {
8998
+ ...taskId !== void 0 ? { taskId } : { chatId },
8999
+ ...channel ? { channel } : {},
9000
+ ...turn !== void 0 ? { turn } : {}
8620
9001
  });
8621
9002
  outputSingle(result, ctx);
8622
9003
  }
@@ -8624,6 +9005,128 @@ var promptsDomain = {
8624
9005
  ]
8625
9006
  };
8626
9007
 
9008
+ // src/domains/support.ts
9009
+ var SUPPORT_COLUMNS = [
9010
+ COL.uuid(),
9011
+ { header: "STATUS", getValue: (item) => item.status ?? "" },
9012
+ { header: "TYPE", getValue: (item) => item.supportRequestType ?? "" },
9013
+ { header: "SEVERITY", getValue: (item) => item.severity ?? "" },
9014
+ { header: "SOURCE", getValue: (item) => item.source ?? "" },
9015
+ { header: "CREATED", getValue: (item) => (item.createdAt ?? "").slice(0, 19) },
9016
+ { header: "TITLE", getValue: (item) => item.title ?? "" }
9017
+ ];
9018
+ var CONTEXT_ARGS = [
9019
+ { name: "workspace", aliases: ["w"], description: "Workspace UUID" },
9020
+ { name: "org-uuid", description: "Organization UUID" }
9021
+ ];
9022
+ function listParams(args) {
9023
+ return {
9024
+ page: args.page ? Number(args.page) : void 0,
9025
+ pageSize: args["page-size"] ? Number(args["page-size"]) : void 0,
9026
+ status: args.status,
9027
+ supportRequestType: args.type,
9028
+ severity: args.severity,
9029
+ source: args.source
9030
+ };
9031
+ }
9032
+ function requestPayload(data, args) {
9033
+ const input = data ?? {};
9034
+ return {
9035
+ supportRequestType: input.supportRequestType ?? input.support_request_type ?? args.type ?? "question",
9036
+ title: input.title ?? requireString(args, "title"),
9037
+ description: input.description ?? requireString(args, "description"),
9038
+ severity: input.severity ?? args.severity
9039
+ };
9040
+ }
9041
+ function commentPayload(data, args) {
9042
+ const input = data ?? {};
9043
+ return {
9044
+ body: input.body ?? requireString(args, "body")
9045
+ };
9046
+ }
9047
+ var supportDomain = {
9048
+ name: "support",
9049
+ description: "Manage support requests",
9050
+ commands: [
9051
+ {
9052
+ path: ["support", "list"],
9053
+ description: "List support requests",
9054
+ args: [
9055
+ ...CONTEXT_ARGS,
9056
+ { name: "status", description: "Filter by status" },
9057
+ { name: "type", description: "Filter by type: bug, feature_request, question" },
9058
+ { name: "severity", description: "Filter by severity" },
9059
+ { name: "source", description: "Filter by source" },
9060
+ { name: "page", description: "Page number" },
9061
+ { name: "page-size", description: "Page size" }
9062
+ ],
9063
+ columns: SUPPORT_COLUMNS,
9064
+ handler: async ({ ctx, args, workspace }) => {
9065
+ const client = requireAuth(ctx).apiClient;
9066
+ const wid = args.workspace ?? workspace;
9067
+ const orgUuid = args["org-uuid"];
9068
+ const result = wid ? await client.listWorkspaceSupportRequests(wid, listParams(args)) : orgUuid ? await client.listOrgSupportRequests(orgUuid, listParams(args)) : await client.listSupportRequests(listParams(args));
9069
+ outputData(result, ctx, SUPPORT_COLUMNS);
9070
+ }
9071
+ },
9072
+ {
9073
+ path: ["support", "get"],
9074
+ description: "Get a support request",
9075
+ args: [
9076
+ ...CONTEXT_ARGS,
9077
+ { name: "id", aliases: ["uuid"], description: "Support request UUID", required: true }
9078
+ ],
9079
+ handler: async ({ ctx, args, workspace }) => {
9080
+ const client = requireAuth(ctx).apiClient;
9081
+ const id = requireString(args, "id");
9082
+ const wid = args.workspace ?? workspace;
9083
+ const orgUuid = args["org-uuid"];
9084
+ const result = wid ? await client.getWorkspaceSupportRequest(wid, id) : orgUuid ? await client.getOrgSupportRequest(orgUuid, id) : await client.getSupportRequest(id);
9085
+ outputSingle(result, ctx, SUPPORT_COLUMNS);
9086
+ }
9087
+ },
9088
+ {
9089
+ path: ["support", "create"],
9090
+ description: "Create a support request",
9091
+ mutates: true,
9092
+ args: [
9093
+ ...CONTEXT_ARGS,
9094
+ { name: "type", description: "Request type: bug, feature_request, question" },
9095
+ { name: "title", description: "Support request title" },
9096
+ { name: "description", description: "Support request description" },
9097
+ { name: "severity", description: "Optional severity" }
9098
+ ],
9099
+ handler: async ({ ctx, args, workspace, data }) => {
9100
+ const client = requireAuth(ctx).apiClient;
9101
+ const wid = args.workspace ?? workspace;
9102
+ const orgUuid = args["org-uuid"];
9103
+ const payload = requestPayload(data, args);
9104
+ const result = wid ? await client.createWorkspaceSupportRequest(wid, payload) : orgUuid ? await client.createOrgSupportRequest(orgUuid, payload) : await client.createSupportRequest(payload);
9105
+ outputSingle(result, ctx, SUPPORT_COLUMNS);
9106
+ }
9107
+ },
9108
+ {
9109
+ path: ["support", "comment"],
9110
+ description: "Add a customer comment to a support request",
9111
+ mutates: true,
9112
+ args: [
9113
+ ...CONTEXT_ARGS,
9114
+ { name: "id", aliases: ["uuid"], description: "Support request UUID", required: true },
9115
+ { name: "body", description: "Comment body" }
9116
+ ],
9117
+ handler: async ({ ctx, args, workspace, data }) => {
9118
+ const client = requireAuth(ctx).apiClient;
9119
+ const id = requireString(args, "id");
9120
+ const wid = args.workspace ?? workspace;
9121
+ const orgUuid = args["org-uuid"];
9122
+ const payload = commentPayload(data, args);
9123
+ const result = wid ? await client.commentOnWorkspaceSupportRequest(wid, id, payload) : orgUuid ? await client.commentOnOrgSupportRequest(orgUuid, id, payload) : await client.commentOnSupportRequest(id, payload);
9124
+ outputSingle(result, ctx, SUPPORT_COLUMNS);
9125
+ }
9126
+ }
9127
+ ]
9128
+ };
9129
+
8627
9130
  // src/domains/supervisors.ts
8628
9131
  var COLUMNS28 = [
8629
9132
  COL.id(),
@@ -8740,6 +9243,10 @@ var syncDomain = {
8740
9243
  {
8741
9244
  name: "board-id",
8742
9245
  description: "Scope to a single board mapping (requires --entity-type)"
9246
+ },
9247
+ {
9248
+ name: "record-limit",
9249
+ description: "Cap the run to a preview sample (1..200). Requires --dry-run and --entity-type."
8743
9250
  }
8744
9251
  ],
8745
9252
  examples: [
@@ -8748,7 +9255,8 @@ var syncDomain = {
8748
9255
  "sync run --connection-id 6 --full-resync",
8749
9256
  "sync run --connection-id 6 --quiet",
8750
9257
  "sync run --connection-id 6 --entity-type member",
8751
- "sync run --connection-id 6 --entity-type member --board-id 18406694561"
9258
+ "sync run --connection-id 6 --entity-type member --board-id 18406694561",
9259
+ "sync run --connection-id 6 --dry-run --entity-type member --record-limit 20"
8752
9260
  ],
8753
9261
  mutates: true,
8754
9262
  handler: async ({ ctx, args, workspace }) => {
@@ -8768,12 +9276,39 @@ var syncDomain = {
8768
9276
  entityType = rawEntityType;
8769
9277
  }
8770
9278
  const boardId = args["board-id"];
9279
+ const rawRecordLimit = args["record-limit"];
9280
+ let recordLimit;
9281
+ if (rawRecordLimit !== void 0 && rawRecordLimit !== "") {
9282
+ const parsed = Number(rawRecordLimit);
9283
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 200) {
9284
+ throw new CLIError(
9285
+ `Invalid --record-limit "${rawRecordLimit}". Must be an integer between 1 and 200.`
9286
+ );
9287
+ }
9288
+ if (!dryRun) {
9289
+ throw new CLIError(
9290
+ "--record-limit requires --dry-run: capped runs are preview-only."
9291
+ );
9292
+ }
9293
+ if (entityType === void 0) {
9294
+ throw new CLIError(
9295
+ "--record-limit requires --entity-type: capped previews must target a single entity type."
9296
+ );
9297
+ }
9298
+ recordLimit = parsed;
9299
+ }
9300
+ if (fullResync && (dryRun || recordLimit !== void 0)) {
9301
+ throw new CLIError(
9302
+ "--full-resync is incompatible with --dry-run / --record-limit: checkpoint reset must happen on a non-preview run."
9303
+ );
9304
+ }
8771
9305
  if (!quiet) {
8772
9306
  const flags = [
8773
9307
  dryRun && "(dry run)",
8774
9308
  fullResync && "(full re-sync)",
8775
9309
  entityType && `(${entityType} only)`,
8776
- boardId && `(board ${boardId})`
9310
+ boardId && `(board ${boardId})`,
9311
+ recordLimit !== void 0 && `(limit ${recordLimit})`
8777
9312
  ].filter(Boolean).join(" ");
8778
9313
  console.error(
8779
9314
  `Starting sync for connection ${connectionId}${flags ? ` ${flags}` : ""}...`
@@ -8786,7 +9321,8 @@ var syncDomain = {
8786
9321
  dryRun,
8787
9322
  resetCheckpoints: fullResync,
8788
9323
  entityType,
8789
- boardId
9324
+ boardId,
9325
+ recordLimit
8790
9326
  }
8791
9327
  );
8792
9328
  if (!response.ok) {
@@ -8980,6 +9516,31 @@ var appConnectionsDomain = {
8980
9516
  );
8981
9517
  outputSingle(result, ctx);
8982
9518
  }
9519
+ },
9520
+ {
9521
+ path: ["app-connections", "relink-member"],
9522
+ 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.",
9523
+ mutates: true,
9524
+ requiresWorkspace: true,
9525
+ args: [
9526
+ { name: "id", description: "Connection ID (numeric)", required: true },
9527
+ { name: "member-id", description: "Member ID (numeric)", required: true },
9528
+ {
9529
+ name: "board-id",
9530
+ description: "Optional Monday board ID. Required for Monday connections where exclusions are board-scoped; omit for single-scope connectors."
9531
+ }
9532
+ ],
9533
+ handler: async ({ ctx, workspace, args }) => {
9534
+ const boardIdRaw = args["board-id"];
9535
+ const options = boardIdRaw !== void 0 && boardIdRaw !== "" ? { boardId: String(boardIdRaw) } : void 0;
9536
+ const result = await requireAuth(ctx).apiClient.clearConnectionMemberExclusion(
9537
+ workspace,
9538
+ requireNumber(args, "id"),
9539
+ requireNumber(args, "member-id"),
9540
+ options
9541
+ );
9542
+ outputSingle(result, ctx);
9543
+ }
8983
9544
  }
8984
9545
  ]
8985
9546
  };
@@ -9672,49 +10233,6 @@ var mondayDomain = {
9672
10233
  outputData(result, ctx);
9673
10234
  }
9674
10235
  },
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
10236
  {
9719
10237
  path: ["monday", "user-info"],
9720
10238
  description: "Get current Monday.com user info",
@@ -10054,6 +10572,113 @@ var slackAppsDomain = {
10054
10572
  ]
10055
10573
  };
10056
10574
 
10575
+ // src/domains/integrations/teams-apps.ts
10576
+ var COLUMNS33 = [COL.id(), COL.name(), COL.status()];
10577
+ function addTeamsUrls(data, apiUrl, workspaceUuid) {
10578
+ const base = `${apiUrl}/api/v2/w/${workspaceUuid}/app-connections/teams`;
10579
+ return {
10580
+ ...data,
10581
+ _urls: {
10582
+ eventsEndpoint: `${base}/events`,
10583
+ oauthUserAuthorize: `${base}/oauth/user/authorize?app_id=${data.id ?? ""}`,
10584
+ oauthUserCallback: `${base}/oauth/user/callback`
10585
+ }
10586
+ };
10587
+ }
10588
+ var teamsAppsDomain = {
10589
+ name: "teams-apps",
10590
+ description: "Manage Microsoft Teams app integrations",
10591
+ commands: [
10592
+ ...crudCommands({
10593
+ prefix: ["teams-apps"],
10594
+ entityName: "Teams app",
10595
+ listMethod: "getTeamsApps",
10596
+ deleteMethod: "deleteTeamsApp",
10597
+ columns: COLUMNS33,
10598
+ paginationSupport: "full"
10599
+ }),
10600
+ {
10601
+ path: ["teams-apps", "get"],
10602
+ description: "Get a Teams app by ID",
10603
+ args: [{ name: "id", description: "Teams app ID", required: true }],
10604
+ requiresWorkspace: true,
10605
+ handler: async ({ ctx, workspace, args }) => {
10606
+ const result = await requireAuth(ctx).apiClient.getTeamsApp(
10607
+ workspace,
10608
+ requireNumber(args, "id")
10609
+ );
10610
+ outputSingle(addTeamsUrls(result, ctx.envConfig.apiUrl, workspace), ctx);
10611
+ }
10612
+ },
10613
+ {
10614
+ path: ["teams-apps", "create"],
10615
+ description: "Create a Teams app",
10616
+ mutates: true,
10617
+ requiresWorkspace: true,
10618
+ handler: async ({ ctx, workspace, data }) => {
10619
+ if (!data) throw new CLIError("JSON data required (--data, --file, or stdin)");
10620
+ const result = await requireAuth(ctx).apiClient.createTeamsApp(workspace, data);
10621
+ outputSingle(addTeamsUrls(result, ctx.envConfig.apiUrl, workspace), ctx);
10622
+ }
10623
+ },
10624
+ {
10625
+ path: ["teams-apps", "update"],
10626
+ description: "Update a Teams app",
10627
+ mutates: true,
10628
+ args: [{ name: "id", description: "Teams app ID", required: true }],
10629
+ requiresWorkspace: true,
10630
+ handler: async ({ ctx, workspace, args, data }) => {
10631
+ if (!data) throw new CLIError("JSON data required (--data, --file, or stdin)");
10632
+ const result = await requireAuth(ctx).apiClient.updateTeamsApp(
10633
+ workspace,
10634
+ requireNumber(args, "id"),
10635
+ data
10636
+ );
10637
+ outputSingle(addTeamsUrls(result, ctx.envConfig.apiUrl, workspace), ctx);
10638
+ }
10639
+ },
10640
+ {
10641
+ path: ["teams-apps", "conversations"],
10642
+ description: "List Teams conversations linked to chats",
10643
+ requiresWorkspace: true,
10644
+ args: [{ name: "id", description: "Teams app ID", required: true }],
10645
+ handler: async ({ ctx, workspace, args }) => {
10646
+ const result = await requireAuth(ctx).apiClient.getTeamsConversations(
10647
+ workspace,
10648
+ requireNumber(args, "id")
10649
+ );
10650
+ outputData(result, ctx);
10651
+ }
10652
+ },
10653
+ {
10654
+ path: ["teams-apps", "installations"],
10655
+ description: "List per-tenant Teams installations",
10656
+ requiresWorkspace: true,
10657
+ args: [{ name: "id", description: "Teams app ID", required: true }],
10658
+ handler: async ({ ctx, workspace, args }) => {
10659
+ const result = await requireAuth(ctx).apiClient.getTeamsInstallations(
10660
+ workspace,
10661
+ requireNumber(args, "id")
10662
+ );
10663
+ outputData(result, ctx);
10664
+ }
10665
+ },
10666
+ {
10667
+ path: ["teams-apps", "accounts"],
10668
+ description: "List linked Teams (Entra) accounts for this app",
10669
+ requiresWorkspace: true,
10670
+ args: [{ name: "id", description: "Teams app ID", required: true }],
10671
+ handler: async ({ ctx, workspace, args }) => {
10672
+ const result = await requireAuth(ctx).apiClient.getTeamsAppAccounts(
10673
+ workspace,
10674
+ requireNumber(args, "id")
10675
+ );
10676
+ outputData(result, ctx);
10677
+ }
10678
+ }
10679
+ ]
10680
+ };
10681
+
10057
10682
  // src/domains/index.ts
10058
10683
  function registerPublicDomains(registry) {
10059
10684
  registry.register(tasksDomain);
@@ -10085,6 +10710,7 @@ function registerPublicDomains(registry) {
10085
10710
  registry.register(mcpServersDomain);
10086
10711
  registry.register(apiKeysDomain);
10087
10712
  registry.register(subscriptionsDomain);
10713
+ registry.register(supportDomain);
10088
10714
  registry.register(reportsDomain);
10089
10715
  registry.register(aiModelsDomain);
10090
10716
  registry.register(featuresDomain);
@@ -10100,6 +10726,7 @@ function registerPublicDomains(registry) {
10100
10726
  registry.register(confluenceDomain);
10101
10727
  registry.register(discordBotsDomain);
10102
10728
  registry.register(slackAppsDomain);
10729
+ registry.register(teamsAppsDomain);
10103
10730
  registry.register(fhirConnectionsDomain);
10104
10731
  registry.register(appConnectionsDomain);
10105
10732
  registry.register(hubspotDomain);
@@ -10109,7 +10736,7 @@ function registerPublicDomains(registry) {
10109
10736
  }
10110
10737
 
10111
10738
  // src/commands/auth-browser.ts
10112
- import { execFile } from "child_process";
10739
+ import { execFile as execFile2 } from "child_process";
10113
10740
  async function authBrowserCommand(ctx, args) {
10114
10741
  const noOpen = Boolean(args["no-open"]);
10115
10742
  const account = await ctx.apiClient.getUserAccount();
@@ -10145,7 +10772,7 @@ function openUrl(url) {
10145
10772
  command = "xdg-open";
10146
10773
  args = [url];
10147
10774
  }
10148
- execFile(command, args, (err) => {
10775
+ execFile2(command, args, (err) => {
10149
10776
  if (err) {
10150
10777
  process.stderr.write("Warning: Could not open browser automatically\n");
10151
10778
  }
@@ -12176,7 +12803,7 @@ async function createMemberChat(apiClient, workspace, memberId) {
12176
12803
  }
12177
12804
 
12178
12805
  // src/commands/organizations.ts
12179
- var COLUMNS33 = [
12806
+ var COLUMNS34 = [
12180
12807
  { header: "NAME", getValue: (o) => o.organizationName || "", maxWidth: 35 },
12181
12808
  { header: "SLUG", getValue: (o) => o.organizationSlug || "", maxWidth: 25 },
12182
12809
  { header: "UUID", getValue: (o) => o.organizationUuid },
@@ -12184,11 +12811,11 @@ var COLUMNS33 = [
12184
12811
  ];
12185
12812
  async function organizationsListCommand(ctx) {
12186
12813
  const organizations = await ctx.apiClient.getMyOrganizations();
12187
- outputData(organizations, ctx, COLUMNS33);
12814
+ outputData(organizations, ctx, COLUMNS34);
12188
12815
  }
12189
12816
 
12190
12817
  // src/commands/whoami.ts
12191
- var COLUMNS34 = [
12818
+ var COLUMNS35 = [
12192
12819
  { header: "NAME", getValue: (a) => a.name || "", maxWidth: 30 },
12193
12820
  { header: "EMAIL", getValue: (a) => a.email || "", maxWidth: 40 },
12194
12821
  { header: "UUID", getValue: (a) => a.uuid },
@@ -12196,7 +12823,7 @@ var COLUMNS34 = [
12196
12823
  ];
12197
12824
  async function whoamiCommand(ctx) {
12198
12825
  const account = await ctx.apiClient.getUserAccount();
12199
- outputSingle(account, ctx, COLUMNS34);
12826
+ outputSingle(account, ctx, COLUMNS35);
12200
12827
  }
12201
12828
  export {
12202
12829
  AuthRequiredError,