@gravity-rail/cli 0.7.3 → 0.10.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/README.md CHANGED
@@ -135,6 +135,33 @@ cat member.json | gr members update -w $WORKSPACE --id 42 --allow-write
135
135
  | `GRAVITY_RAIL_API_URL` | Override API base URL (default: `https://api.gravityrail.com`) |
136
136
  | `GRAVITY_RAIL_FRONTEND_URL` | Override frontend URL (default: `https://app.gravityrail.com`) |
137
137
 
138
+ ## Exit Codes
139
+
140
+ | Code | Meaning |
141
+ |---|---|
142
+ | `0` | Success |
143
+ | `1` | General error |
144
+ | `2` | Authentication required (run `gr login` or set `GRAVITY_RAIL_API_KEY`) |
145
+ | `3` | A human-in-the-loop (HITL) interrupt requires out-of-band action before the operation can continue |
146
+
147
+ When a concierge command run with `--message` (non-interactive mode) hits a HITL
148
+ interrupt the CLI cannot resolve on its own, it exits `3` and writes a single
149
+ machine-readable JSON object to **stdout**:
150
+
151
+ ```json
152
+ {
153
+ "error": "hitl_interrupt",
154
+ "message": "A human-in-the-loop interrupt requires out-of-band action before the operation can continue.",
155
+ "resume": { "chatId": 42, "chatUuid": "…", "interruptId": "int-abc" },
156
+ "interrupt": { "type": "stripe_checkout", "interrupt_id": "int-abc", "url": "…" }
157
+ }
158
+ ```
159
+
160
+ The `interrupt` object is intentionally limited to routing fields (`type`,
161
+ `interrupt_id`, and a redirect `url` when present); the raw server payload is
162
+ not forwarded, as it may contain sensitive data. Use `resume.chatUuid` /
163
+ `resume.interruptId` to continue the flow out-of-band (e.g. in the web app).
164
+
138
165
  ## Commands
139
166
 
140
167
  ### Core
@@ -160,6 +187,7 @@ cat member.json | gr members update -w $WORKSPACE --id 42 --allow-write
160
187
  | `events` | Automation rules — triggers, CEL conditions, delayed actions, webhooks |
161
188
  | `calendars` | Scheduling — calendars, events, event types, availability, Google sync |
162
189
  | `qualifications` | Skills evaluation — requirements, submissions, scoring |
190
+ | `queries` | Unified Query Engine — fields, validate, preview, run, syntax reference |
163
191
 
164
192
  ### Global (no workspace)
165
193
 
@@ -173,6 +201,7 @@ cat member.json | gr members update -w $WORKSPACE --id 42 --allow-write
173
201
  | Domain | Description |
174
202
  |---|---|
175
203
  | `phone-numbers` | Voice and SMS phone numbers — provisioning, calls, messages |
204
+ | `recordings` | Call recordings — list chunks, get a presigned download URL, save the WAV |
176
205
  | `inboxes` | Email inboxes — threads, attachments, routing |
177
206
  | `operator-groups` | Live operator routing — groups, presence, strategies |
178
207
  | `notification-rules` | Alert configuration — event-based notifications |
@@ -184,6 +213,7 @@ cat member.json | gr members update -w $WORKSPACE --id 42 --allow-write
184
213
  | `discord-bots` | Discord bot management — slash commands, threads, account linking |
185
214
  | `slack-apps` | Slack app management — installations, threads, accounts |
186
215
  | `fhir-connections` | FHIR healthcare connections — patients, practitioners, encounters |
216
+ | `patientsync` | PatientSync PSTN-transfer connection — `configure` / `status` / `delete` (singleton per workspace) |
187
217
  | `app-connections` | Platform app integrations |
188
218
  | `monday` | Monday.com — boards, columns, webhooks |
189
219
 
@@ -250,6 +280,16 @@ gr pronunciations list -w $WORKSPACE
250
280
  gr pronunciations create -w $WORKSPACE --term "GravityRail" --respelling "GRA-vit-ee rail" --allow-write
251
281
  ```
252
282
 
283
+ ### Download a call recording
284
+
285
+ ```sh
286
+ # Get a single short-lived presigned URL for the whole call (assembled once, cached).
287
+ gr recordings download-url -w $WORKSPACE --chat-id 42
288
+
289
+ # Or download the assembled WAV straight to a file.
290
+ gr recordings download -w $WORKSPACE --chat-id 42 -o call.wav
291
+ ```
292
+
253
293
  ### Register and verify an org domain
254
294
 
255
295
  ```sh
@@ -2,9 +2,9 @@ import {
2
2
  confirmAccountPhoneVerify,
3
3
  requireAuth,
4
4
  startAccountPhoneVerify
5
- } from "./chunk-KTTHOL3B.js";
6
- import "./chunk-H62EC2CF.js";
7
- import "./chunk-7VZ4JLAJ.js";
5
+ } from "./chunk-XJ2EBXJZ.js";
6
+ import "./chunk-GVUGFPCN.js";
7
+ import "./chunk-WZVMWN3S.js";
8
8
 
9
9
  // src/commands/account-verify-phone.ts
10
10
  import * as readline from "readline/promises";
@@ -38,4 +38,4 @@ async function promptLine(question) {
38
38
  export {
39
39
  accountVerifyPhoneCommand
40
40
  };
41
- //# sourceMappingURL=account-verify-phone-TDQNZ5XN.js.map
41
+ //# sourceMappingURL=account-verify-phone-XNXA7HNG.js.map
@@ -87,7 +87,7 @@ function getEnvironmentConfig(env) {
87
87
  import { createServer } from "http";
88
88
  import { createHash, randomBytes } from "crypto";
89
89
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync, chmodSync, unlinkSync } from "fs";
90
- import { join as join2 } from "path";
90
+ import { join as join2, resolve as resolve2, relative, isAbsolute } from "path";
91
91
  import { exec } from "child_process";
92
92
  var CredentialsManager = class {
93
93
  cacheDir;
@@ -97,6 +97,18 @@ var CredentialsManager = class {
97
97
  this.apiUrl = apiUrl;
98
98
  }
99
99
  getCredentialsPath(env) {
100
+ if (!/^[a-zA-Z0-9_:.-]+$/.test(env)) {
101
+ throw new Error(`Invalid environment name: ${env}`);
102
+ }
103
+ const candidate = this.buildCredentialsPath(env);
104
+ const resolvedDir = resolve2(this.cacheDir);
105
+ const rel = relative(resolvedDir, resolve2(candidate));
106
+ if (rel.startsWith("..") || isAbsolute(rel)) {
107
+ throw new Error(`Invalid environment name: ${env}`);
108
+ }
109
+ return candidate;
110
+ }
111
+ buildCredentialsPath(env) {
100
112
  if (this.apiUrl) {
101
113
  try {
102
114
  const url = new URL(this.apiUrl);
@@ -218,7 +230,7 @@ function openBrowser(url) {
218
230
  });
219
231
  }
220
232
  function startCallbackServer() {
221
- return new Promise((resolve2, reject) => {
233
+ return new Promise((resolve3, reject) => {
222
234
  let authCode = null;
223
235
  let error = null;
224
236
  let receivedState = null;
@@ -260,7 +272,7 @@ function startCallbackServer() {
260
272
  </html>
261
273
  `);
262
274
  server.close();
263
- resolve2({ code: authCode, state: receivedState });
275
+ resolve3({ code: authCode, state: receivedState });
264
276
  } else {
265
277
  res.writeHead(400);
266
278
  res.end("Missing code parameter");
@@ -438,7 +450,7 @@ async function refreshAccessToken(envConfig, creds, credentialsManager, env) {
438
450
 
439
451
  // src/cli-utils/ui.ts
440
452
  import * as readline from "readline";
441
- var NEW_ITEM_MARKER = Symbol("NEW_ITEM");
453
+ var NEW_ITEM_MARKER = /* @__PURE__ */ Symbol("NEW_ITEM");
442
454
  async function selectFromList(items, options = {}) {
443
455
  const {
444
456
  title = "Select an item",
@@ -455,7 +467,7 @@ async function selectFromList(items, options = {}) {
455
467
  return { item: null, isNew: false };
456
468
  }
457
469
  const displayItems = allowNew ? [NEW_ITEM_MARKER, ...items] : items;
458
- return new Promise((resolve2) => {
470
+ return new Promise((resolve3) => {
459
471
  let selectedIndex = allowNew ? 1 : 0;
460
472
  let filterText = "";
461
473
  const getFilteredItems = () => {
@@ -538,16 +550,16 @@ async function selectFromList(items, options = {}) {
538
550
  if (filtered.length > 0 && selectedIndex < filtered.length) {
539
551
  const selected = filtered[selectedIndex];
540
552
  if (selected === NEW_ITEM_MARKER) {
541
- resolve2({ item: null, isNew: true });
553
+ resolve3({ item: null, isNew: true });
542
554
  } else {
543
- resolve2({ item: selected, isNew: false });
555
+ resolve3({ item: selected, isNew: false });
544
556
  }
545
557
  } else {
546
- resolve2({ item: null, isNew: false });
558
+ resolve3({ item: null, isNew: false });
547
559
  }
548
560
  } else if (key.name === "escape" || key.ctrl && key.name === "c") {
549
561
  cleanup();
550
- resolve2({ item: null, isNew: false });
562
+ resolve3({ item: null, isNew: false });
551
563
  } else if (key.name === "backspace") {
552
564
  filterText = filterText.slice(0, -1);
553
565
  selectedIndex = allowNew ? 0 : 0;
@@ -678,7 +690,8 @@ var AGUIClient = class {
678
690
  this.appContext = options.appContext;
679
691
  }
680
692
  inferenceStatusUrl() {
681
- if (!this.workspaceUuid || this.chatId <= 0) return null;
693
+ const noChat = typeof this.chatId === "number" ? this.chatId <= 0 : this.chatId.length === 0;
694
+ if (!this.workspaceUuid || noChat) return null;
682
695
  return `${this.baseUrl}/api/v2/w/${this.workspaceUuid}/chats/${this.chatId}/inference/status`;
683
696
  }
684
697
  async waitForInferenceInactive(maxWaitMs = 6e4) {
@@ -721,6 +734,7 @@ var AGUIClient = class {
721
734
  ...this.appContext ? { appContext: this.appContext } : {},
722
735
  ...request.resumeValue !== void 0 ? { resumeValue: request.resumeValue } : {},
723
736
  ...request.resumeInterruptId ? { resumeInterruptId: request.resumeInterruptId } : {},
737
+ ...request.resume !== void 0 ? { resume: request.resume } : {},
724
738
  ...options?.autoApprove ? { context: { autoApprove: true } } : {}
725
739
  });
726
740
  if (this.debug) {
@@ -832,4 +846,4 @@ export {
832
846
  getTextDelta,
833
847
  getErrorMessage
834
848
  };
835
- //# sourceMappingURL=chunk-H62EC2CF.js.map
849
+ //# sourceMappingURL=chunk-GVUGFPCN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-utils/config.ts","../src/cli-utils/auth.ts","../src/cli-utils/ui.ts","../src/cli-utils/agui.ts"],"sourcesContent":["/**\n * CLI configuration for remote environments\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { dirname, join, resolve } from 'path';\n\nexport interface EnvironmentConfig {\n apiUrl: string;\n frontendUrl: string;\n}\n\nexport const ENVIRONMENTS: Record<string, EnvironmentConfig> = {\n dev: {\n apiUrl: 'https://api.gravityrail.test:5054',\n frontendUrl: 'https://app.gravityrail.test:3000',\n },\n prod: {\n apiUrl: 'https://api.gravityrail.com',\n frontendUrl: 'https://app.gravityrail.com',\n },\n staging: {\n apiUrl: 'https://api.gr-staging.com',\n frontendUrl: 'https://app.gr-staging.com',\n },\n};\n\nexport const CALLBACK_PORT = 31337;\nexport const CALLBACK_PATH = '/oauth/callback';\nexport const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;\n\n/**\n * Detect worktree port overrides from docker-compose.override.yml.\n * The override file is generated by worktree-setup.sh and contains lines like:\n * - '5250:5050' (API)\n * - '3200:3000' (Frontend)\n * We extract the host port for the default internal ports (5050, 3000).\n */\nfunction detectWorktreePorts(): { apiPort?: number; frontendPort?: number } {\n // Walk up from cwd to find docker-compose.override.yml\n let dir = resolve(process.cwd());\n let overridePath: string | undefined;\n while (true) {\n const candidate = join(dir, 'docker-compose.override.yml');\n\n if (existsSync(candidate)) {\n overridePath = candidate;\n break;\n }\n const parent = dirname(dir);\n if (parent === dir) break; // reached filesystem root\n dir = parent;\n }\n if (!overridePath) return {};\n\n let content: string;\n try {\n content = readFileSync(overridePath, 'utf-8');\n } catch {\n return {};\n }\n\n // Only use this if it's a worktree override (generated by worktree-setup.sh)\n if (!content.includes('worktree-setup.sh')) return {};\n\n // Match port mappings: - 'HOST:CONTAINER'\n let apiPort: number | undefined;\n let frontendPort: number | undefined;\n for (const match of content.matchAll(/-\\s*'(\\d+):(\\d+)'/g)) {\n const host = parseInt(match[1]!, 10);\n const container = parseInt(match[2]!, 10);\n if (container === 5050) apiPort = host;\n if (container === 3000) frontendPort = host;\n }\n return { apiPort, frontendPort };\n}\n\nexport function getEnvironmentConfig(env: string): EnvironmentConfig {\n // Handle dev-cloud environments: --env dev-cloud:NAMESPACE\n if (env.startsWith('dev-cloud:')) {\n const namespace = env.slice('dev-cloud:'.length);\n if (!namespace) {\n throw new Error('dev-cloud environment requires a namespace: --env dev-cloud:NAMESPACE');\n }\n return {\n apiUrl: `https://${namespace}-api.gravityrail-dev.com`,\n frontendUrl: `https://${namespace}-app.gravityrail-dev.com`,\n };\n }\n\n const config = ENVIRONMENTS[env];\n if (!config) {\n throw new Error(\n `Invalid environment: ${env}. Use 'dev', 'staging', 'prod', or 'dev-cloud:NAMESPACE'.`\n );\n }\n\n // Allow env var overrides (used by code interpreter sandboxes)\n const apiUrl = process.env.GRAVITY_RAIL_API_URL || config.apiUrl;\n const frontendUrl = process.env.GRAVITY_RAIL_FRONTEND_URL || config.frontendUrl;\n if (apiUrl !== config.apiUrl || frontendUrl !== config.frontendUrl) {\n return { apiUrl, frontendUrl };\n }\n\n // For dev, detect worktree port overrides\n if (env === 'dev') {\n const ports = detectWorktreePorts();\n if (ports.apiPort || ports.frontendPort) {\n return {\n apiUrl: ports.apiPort ? `https://api.gravityrail.test:${ports.apiPort}` : config.apiUrl,\n frontendUrl: ports.frontendPort\n ? `https://app.gravityrail.test:${ports.frontendPort}`\n : config.frontendUrl,\n };\n }\n }\n\n return config;\n}\n","/**\n * CLI OAuth2 authentication utilities\n *\n * Provides OAuth2 with PKCE flow for CLI tools, including:\n * - Credential caching (per environment)\n * - Dynamic client registration\n * - Token refresh\n * - Browser-based authorization\n */\n\nimport { createServer } from 'http';\nimport { createHash, randomBytes } from 'crypto';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, unlinkSync } from 'fs';\nimport { join, resolve, relative, isAbsolute } from 'path';\nimport { exec } from 'child_process';\nimport { EnvironmentConfig, CALLBACK_PORT, CALLBACK_PATH, REDIRECT_URI } from './config';\n\nexport interface CLICredentials {\n clientId: string;\n clientSecret: string;\n accessToken?: string;\n refreshToken?: string;\n expiresAt?: string;\n}\n\nexport interface AuthResult {\n accessToken: string;\n credentials?: CLICredentials;\n}\n\n/**\n * Manages CLI credentials storage (flat format, one file per environment)\n */\nexport class CredentialsManager {\n private cacheDir: string;\n private apiUrl?: string;\n\n constructor(rootDir: string, apiUrl?: string) {\n // `rootDir` is supplied only by CLI bootstrap (context.ts passes\n // os.homedir()) — the local user's own home directory, never remote or\n // request input — joined with the constant '.gr-cli'. FP (CWE-22).\n // bearer:disable javascript_lang_path_traversal\n // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal\n this.cacheDir = join(rootDir, '.gr-cli');\n this.apiUrl = apiUrl;\n }\n\n private getCredentialsPath(env: string): string {\n // `env` comes from --env and the dev-cloud:NAMESPACE branch accepts an\n // arbitrary namespace, so it could contain `..` or a path separator and\n // escape the cache directory. Validate against an allowlist first (real\n // env names are alphanumerics, `-`, `_`, `.`, and the dev-cloud `:`\n // separator). This rejects path separators outright — including the\n // `foo/bar` case that would otherwise resolve inside .gr-cli but fail\n // later with a cryptic ENOENT from the un-created subdirectory.\n if (!/^[a-zA-Z0-9_:.-]+$/.test(env)) {\n throw new Error(`Invalid environment name: ${env}`);\n }\n // Defense-in-depth: also contain the resolved path before any fs\n // operation, so a future change to buildCredentialsPath can't reintroduce\n // an escape even if the allowlist is loosened.\n const candidate = this.buildCredentialsPath(env);\n // `this.cacheDir` is rooted at os.homedir() (see constructor) — a constant\n // resolve of the user's own dir, no attacker input. FP (CWE-22).\n // bearer:disable javascript_lang_path_traversal\n const resolvedDir = resolve(this.cacheDir);\n // This resolve()/relative() pair IS the containment sanitizer — `env` was\n // already allowlist-validated above. Semgrep's taint tracker flags the\n // resolve() of the env-derived candidate without modelling the guard.\n // bearer:disable javascript_lang_path_traversal\n // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal\n const rel = relative(resolvedDir, resolve(candidate));\n if (rel.startsWith('..') || isAbsolute(rel)) {\n throw new Error(`Invalid environment name: ${env}`);\n }\n return candidate;\n }\n\n private buildCredentialsPath(env: string): string {\n // For localhost dev URLs with non-default ports (worktrees), include the\n // port in the filename so each worktree has isolated credentials.\n if (this.apiUrl) {\n try {\n const url = new URL(this.apiUrl);\n if (url.hostname === 'localhost' && url.port && url.port !== '5050') {\n // `env` is allowlist-validated in getCredentialsPath before this\n // builder runs, cacheDir is homedir-rooted, and url.port is a\n // parsed URL port — no traversal component. FP (CWE-22).\n // bearer:disable javascript_lang_path_traversal\n // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal\n return join(this.cacheDir, `credentials-${env}-${url.port}.json`);\n }\n } catch {\n // ignore invalid URLs\n }\n }\n // `env` is allowlist-validated in getCredentialsPath before this builder\n // runs and cacheDir is homedir-rooted — no traversal component. FP (CWE-22).\n // bearer:disable javascript_lang_path_traversal\n // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal\n return join(this.cacheDir, `credentials-${env}.json`);\n }\n\n load(env: string): CLICredentials | null {\n const credsPath = this.getCredentialsPath(env);\n if (!existsSync(credsPath)) {\n return null;\n }\n\n try {\n const raw = JSON.parse(readFileSync(credsPath, 'utf-8'));\n\n let creds: CLICredentials;\n if (raw.clientId !== undefined) {\n creds = raw;\n } else if (raw.default) {\n // Migrate from old org-namespaced format to flat\n creds = raw.default;\n this.save(env, creds);\n } else {\n return null;\n }\n\n // Check if access token is expired\n if (creds.expiresAt) {\n const expiresAt = new Date(creds.expiresAt);\n if (expiresAt <= new Date()) {\n if (creds.refreshToken) {\n return creds;\n }\n return null;\n }\n }\n\n return creds;\n } catch {\n return null;\n }\n }\n\n save(env: string, creds: CLICredentials): void {\n if (!existsSync(this.cacheDir)) {\n mkdirSync(this.cacheDir, { recursive: true });\n }\n const credsPath = this.getCredentialsPath(env);\n writeFileSync(credsPath, JSON.stringify(creds, null, 2));\n try {\n chmodSync(credsPath, 0o600);\n } catch {\n // Ignore permission errors on Windows\n }\n }\n\n clear(env: string): void {\n const credsPath = this.getCredentialsPath(env);\n if (!existsSync(credsPath)) {\n return;\n }\n try {\n unlinkSync(credsPath);\n } catch {\n // Ignore\n }\n }\n\n getCacheDir(): string {\n return this.cacheDir;\n }\n}\n\nfunction stderr(msg: string): void {\n process.stderr.write(msg + '\\n');\n}\n\n/**\n * Parse an OAuth error response body, checking the JSON `error` field first,\n * falling back to text matching.\n */\nfunction isOAuthError(body: string, errorCode: string): boolean {\n try {\n const parsed = JSON.parse(body);\n if (parsed.error === errorCode) return true;\n } catch {\n // Not JSON — fall through to text match\n }\n return body.includes(errorCode);\n}\n\n/**\n * Validate and extract fields from an OAuth token response.\n * Throws on malformed responses per RFC 6749 §5.1.\n */\nfunction validateTokenResponse(tokenData: Record<string, unknown>): {\n accessToken: string;\n refreshToken?: string;\n expiresIn: number;\n} {\n if (!tokenData.access_token || typeof tokenData.access_token !== 'string') {\n throw new Error('Token response missing access_token');\n }\n\n if (tokenData.token_type && String(tokenData.token_type).toLowerCase() !== 'bearer') {\n throw new Error(`Unsupported token type: ${tokenData.token_type}`);\n }\n\n let expiresIn = 86400; // default 24h\n if (tokenData.expires_in != null) {\n const raw = Number(tokenData.expires_in);\n if (!Number.isFinite(raw) || raw < 60 || raw > 31536000) {\n stderr(` Warning: unusual expires_in value (${tokenData.expires_in}), using default`);\n } else {\n expiresIn = raw;\n }\n }\n\n return {\n accessToken: tokenData.access_token,\n refreshToken:\n typeof tokenData.refresh_token === 'string' ? tokenData.refresh_token : undefined,\n expiresIn,\n };\n}\n\n/**\n * Generate PKCE code verifier and challenge\n */\nexport function generatePKCE(): { codeVerifier: string; codeChallenge: string } {\n const codeVerifier = randomBytes(64).toString('base64url').slice(0, 128);\n const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');\n return { codeVerifier, codeChallenge };\n}\n\n/**\n * Open a URL in the default browser\n */\nexport function openBrowser(url: string): void {\n const platform = process.platform;\n let cmd: string;\n if (platform === 'darwin') {\n cmd = `open \"${url}\"`;\n } else if (platform === 'win32') {\n cmd = `start \"\" \"${url}\"`;\n } else {\n cmd = `xdg-open \"${url}\"`;\n }\n\n exec(cmd, (err) => {\n if (err) {\n stderr(' Could not open browser automatically.');\n }\n });\n}\n\n/**\n * Start a local HTTP server to receive the OAuth callback\n */\nexport function startCallbackServer(): Promise<{ code: string; state: string }> {\n return new Promise((resolve, reject) => {\n let authCode: string | null = null;\n let error: string | null = null;\n let receivedState: string | null = null;\n\n const server = createServer((req, res) => {\n const url = new URL(req.url!, `http://localhost:${CALLBACK_PORT}`);\n\n if (url.pathname !== CALLBACK_PATH) {\n res.writeHead(404);\n res.end('Not found');\n return;\n }\n\n if (url.searchParams.has('error')) {\n error = url.searchParams.get('error');\n const errorDesc = url.searchParams.get('error_description') || 'Unknown error';\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <head><title>Authorization Failed</title></head>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1 style=\"color: #dc2626;\">Authorization Failed</h1>\n <p>${errorDesc}</p>\n <p>You can close this window.</p>\n </body>\n </html>\n `);\n server.close();\n reject(new Error(`Authorization failed: ${error}`));\n } else if (url.searchParams.has('code')) {\n authCode = url.searchParams.get('code')!;\n receivedState = url.searchParams.get('state')!;\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <head><title>Authorization Successful</title></head>\n <body style=\"font-family: system-ui; text-align: center; padding: 50px;\">\n <h1 style=\"color: #16a34a;\">Authorization Successful!</h1>\n <p>You can close this window and return to the terminal.</p>\n <script>setTimeout(() => window.close(), 2000);</script>\n </body>\n </html>\n `);\n server.close();\n resolve({ code: authCode, state: receivedState });\n } else {\n res.writeHead(400);\n res.end('Missing code parameter');\n }\n });\n\n server.listen(CALLBACK_PORT, 'localhost', () => {\n // Server started\n });\n\n // Timeout after 5 minutes (unref so it doesn't keep the process alive)\n const timeout = setTimeout(\n () => {\n if (!authCode && !error) {\n server.close();\n reject(new Error('Authorization timeout - no response received'));\n }\n },\n 5 * 60 * 1000\n );\n timeout.unref();\n });\n}\n\n/**\n * Register a new OAuth client dynamically\n */\nasync function dynamicClientRegistration(\n envConfig: EnvironmentConfig,\n clientName: string = 'Gravity CLI'\n): Promise<{ client_id: string; client_secret: string }> {\n stderr(' Registering OAuth client...');\n\n const registrationUrl = `${envConfig.apiUrl}/api/v2/oauth/register`;\n\n const registrationData = {\n client_name: clientName,\n redirect_uris: [REDIRECT_URI],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'client_secret_basic',\n scope: 'workspace:admin',\n };\n\n const response = await fetch(registrationUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(registrationData),\n });\n\n if (!response.ok) {\n throw new Error(`Client registration failed: ${response.status} - ${await response.text()}`);\n }\n\n return (await response.json()) as { client_id: string; client_secret: string };\n}\n\nexport interface OAuthFlowOptions {\n env: string;\n envConfig: EnvironmentConfig;\n credentialsManager: CredentialsManager;\n clientName?: string;\n}\n\n/**\n * Perform the full OAuth2 browser-based flow with PKCE.\n *\n * This always opens the browser for authorization. It never returns a cached\n * token — that's the job of createAuthenticatedContext. The only thing reused\n * is the registered OAuth client ID (which is identity, not auth).\n */\nexport async function performOAuthFlow(options: OAuthFlowOptions): Promise<AuthResult> {\n const { env, envConfig, credentialsManager, clientName = 'Gravity CLI' } = options;\n let hasRetried = false;\n\n while (true) {\n // Reuse registered client credentials if available, otherwise register\n let creds = credentialsManager.load(env);\n if (!creds || !creds.clientId) {\n const registration = await dynamicClientRegistration(envConfig, clientName);\n creds = {\n clientId: registration.client_id,\n clientSecret: registration.client_secret,\n };\n credentialsManager.save(env, creds);\n } else {\n stderr(' Using cached client credentials');\n }\n\n const { codeVerifier, codeChallenge } = generatePKCE();\n const state = randomBytes(32).toString('base64url');\n\n const authParams = new URLSearchParams({\n response_type: 'code',\n client_id: creds.clientId,\n redirect_uri: REDIRECT_URI,\n scope: 'workspace:admin',\n state: state,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n const authUrl = `${envConfig.apiUrl}/api/v2/oauth/authorize?${authParams}`;\n\n const callbackPromise = startCallbackServer();\n\n stderr('\\n Opening browser for authorization...');\n stderr(` If browser doesn't open, visit:\\n ${authUrl}\\n`);\n openBrowser(authUrl);\n\n stderr(' Waiting for authorization (timeout: 5 minutes)...');\n let code: string;\n let receivedState: string;\n try {\n ({ code, state: receivedState } = await callbackPromise);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!hasRetried && isOAuthError(msg, 'invalid_client')) {\n stderr(' Cached client credentials expired, re-registering...');\n credentialsManager.clear(env);\n hasRetried = true;\n continue;\n }\n throw err;\n }\n\n if (receivedState !== state) {\n throw new Error('State mismatch - possible CSRF attack');\n }\n\n stderr(' Authorization code received!');\n stderr(' Exchanging code for tokens...');\n\n const tokenUrl = `${envConfig.apiUrl}/api/v2/oauth/token`;\n const authHeader = Buffer.from(`${creds.clientId}:${creds.clientSecret}`).toString('base64');\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${authHeader}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: REDIRECT_URI,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const body = await tokenResponse.text();\n if (!hasRetried && isOAuthError(body, 'invalid_client')) {\n stderr(' Cached client credentials expired, re-registering...');\n credentialsManager.clear(env);\n hasRetried = true;\n continue;\n }\n throw new Error(`Token exchange failed: ${tokenResponse.status} - ${body}`);\n }\n\n const tokenData = (await tokenResponse.json()) as Record<string, unknown>;\n const { accessToken, refreshToken, expiresIn } = validateTokenResponse(tokenData);\n\n creds.accessToken = accessToken;\n creds.refreshToken = refreshToken ?? creds.refreshToken;\n creds.expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();\n\n credentialsManager.save(env, creds);\n stderr(' Authentication successful!');\n\n return { accessToken, credentials: creds };\n }\n}\n\n/**\n * Refresh an access token using a stored refresh token.\n * Returns updated credentials on success, null on failure.\n */\nexport async function refreshAccessToken(\n envConfig: EnvironmentConfig,\n creds: CLICredentials,\n credentialsManager: CredentialsManager,\n env: string\n): Promise<CLICredentials | null> {\n if (!creds.refreshToken || !creds.clientId || !creds.clientSecret) {\n stderr(' Cannot refresh: missing credentials (re-login required)');\n return null;\n }\n\n const tokenUrl = `${envConfig.apiUrl}/api/v2/oauth/token`;\n const authHeader = Buffer.from(`${creds.clientId}:${creds.clientSecret}`).toString('base64');\n\n try {\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n Authorization: `Basic ${authHeader}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: creds.refreshToken,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n const isExpiredRefresh = isOAuthError(body, 'invalid_grant');\n if (isExpiredRefresh) {\n stderr(' Refresh token expired, re-login required');\n } else {\n stderr(` Token refresh failed (${response.status}), clearing credentials`);\n }\n credentialsManager.clear(env);\n return null;\n }\n\n const tokenData = (await response.json()) as Record<string, unknown>;\n const { accessToken, refreshToken, expiresIn } = validateTokenResponse(tokenData);\n\n const updated: CLICredentials = {\n ...creds,\n accessToken,\n refreshToken: refreshToken ?? creds.refreshToken,\n expiresAt: new Date(Date.now() + expiresIn * 1000).toISOString(),\n };\n\n credentialsManager.save(env, updated);\n return updated;\n } catch (err) {\n stderr(` Token refresh error: ${err instanceof Error ? err.message : 'unknown'}`);\n credentialsManager.clear(env);\n return null;\n }\n}\n","/**\n * CLI terminal UI utilities\n *\n * Provides interactive terminal UI components for CLI tools\n */\n\nimport * as readline from 'readline';\n\nexport interface SelectOption<T> {\n item: T;\n label: string;\n detail?: string;\n}\n\nexport interface SelectFromListOptions<T> {\n title?: string;\n labelFn?: (item: T) => string;\n detailFn?: (item: T) => string | null;\n env?: string;\n emptyMessage?: string;\n allowNew?: boolean;\n newLabel?: string;\n}\n\nexport interface SelectResult<T> {\n item: T | null;\n isNew: boolean;\n}\n\nconst NEW_ITEM_MARKER = Symbol('NEW_ITEM');\n\n/**\n * Interactive list picker using arrow keys\n */\nexport async function selectFromList<T>(\n items: T[],\n options: SelectFromListOptions<T> = {}\n): Promise<SelectResult<T>> {\n const {\n title = 'Select an item',\n labelFn = (item: T) => String((item as any).name || (item as any).title || item),\n detailFn = null,\n env = null,\n emptyMessage = 'No items found.',\n allowNew = false,\n newLabel = 'Create new...',\n } = options;\n\n if ((!items || items.length === 0) && !allowNew) {\n console.log(`\\n ${emptyMessage}`);\n return { item: null, isNew: false };\n }\n\n // Prepend \"Create new\" option if allowed\n const displayItems: (T | typeof NEW_ITEM_MARKER)[] = allowNew\n ? [NEW_ITEM_MARKER, ...items]\n : items;\n\n return new Promise((resolve) => {\n let selectedIndex = allowNew ? 1 : 0;\n let filterText = '';\n\n const getFilteredItems = (): (T | typeof NEW_ITEM_MARKER)[] => {\n if (!filterText) return displayItems;\n const lower = filterText.toLowerCase();\n return displayItems.filter((item) => {\n if (item === NEW_ITEM_MARKER) return true;\n const label = labelFn(item);\n return label.toLowerCase().includes(lower);\n });\n };\n\n const render = (): void => {\n const filtered = getFilteredItems();\n\n // Clear screen and move cursor to top\n process.stdout.write('\\x1B[2J\\x1B[H');\n\n console.log('='.repeat(60));\n console.log(`${title} (↑/↓ to move, Enter to select, Esc to cancel)`);\n console.log('='.repeat(60));\n if (env) {\n console.log(`Environment: ${env}`);\n }\n console.log(`Filter: ${filterText}${filterText ? '' : '(type to filter)'}`);\n console.log('');\n\n if (filtered.length === 0) {\n console.log(` ${emptyMessage}`);\n console.log('');\n return;\n }\n\n // Show up to 15 items around the selected one\n const maxVisible = 15;\n let startIdx = Math.max(0, selectedIndex - Math.floor(maxVisible / 2));\n let endIdx = Math.min(filtered.length, startIdx + maxVisible);\n if (endIdx - startIdx < maxVisible && startIdx > 0) {\n startIdx = Math.max(0, endIdx - maxVisible);\n }\n\n if (startIdx > 0) {\n console.log(` ... ${startIdx} more above ...`);\n }\n\n for (let i = startIdx; i < endIdx; i++) {\n const item = filtered[i]!;\n const prefix = i === selectedIndex ? '> ' : ' ';\n\n let line: string;\n if (item === NEW_ITEM_MARKER) {\n line = `\\x1B[32m${newLabel}\\x1B[0m`; // Green color for \"Create new\"\n } else {\n const typedItem = item as T;\n line = labelFn(typedItem);\n if (detailFn) {\n const detail = detailFn(typedItem);\n if (detail) {\n line = `${line} \\x1B[90m${detail}\\x1B[0m`;\n }\n }\n }\n\n if (i === selectedIndex) {\n console.log(`\\x1B[7m${prefix}${line}\\x1B[0m`);\n } else {\n console.log(`${prefix}${line}`);\n }\n }\n\n if (endIdx < filtered.length) {\n console.log(` ... ${filtered.length - endIdx} more below ...`);\n }\n\n console.log('');\n console.log(` ${filtered.length} item(s)`);\n };\n\n // Set up raw mode for arrow key input\n readline.emitKeypressEvents(process.stdin);\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n\n render();\n\n const handleKeypress = (str: string | undefined, key: readline.Key): void => {\n const filtered = getFilteredItems();\n\n if (key.name === 'up') {\n selectedIndex = Math.max(0, selectedIndex - 1);\n render();\n } else if (key.name === 'down') {\n selectedIndex = Math.min(filtered.length - 1, selectedIndex + 1);\n render();\n } else if (key.name === 'return') {\n cleanup();\n if (filtered.length > 0 && selectedIndex < filtered.length) {\n const selected = filtered[selectedIndex]!;\n if (selected === NEW_ITEM_MARKER) {\n resolve({ item: null, isNew: true });\n } else {\n resolve({ item: selected as T, isNew: false });\n }\n } else {\n resolve({ item: null, isNew: false });\n }\n } else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {\n cleanup();\n resolve({ item: null, isNew: false });\n } else if (key.name === 'backspace') {\n filterText = filterText.slice(0, -1);\n selectedIndex = allowNew ? 0 : 0;\n render();\n } else if (str && str.length === 1 && !key.ctrl && !key.meta) {\n filterText += str;\n selectedIndex = allowNew ? 0 : 0;\n render();\n }\n };\n\n const cleanup = (): void => {\n process.stdin.removeListener('keypress', handleKeypress);\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.pause();\n process.stdout.write('\\x1B[2J\\x1B[H');\n };\n\n process.stdin.on('keypress', handleKeypress);\n process.stdin.resume();\n });\n}\n\n/**\n * Print a table of items\n */\nexport function printTable<T>(\n items: T[],\n columns: Array<{\n header: string;\n getValue: (item: T) => string;\n maxWidth?: number;\n }>,\n options: { title?: string; env?: string } = {}\n): void {\n if (!items || items.length === 0) {\n console.log('\\nNo items found.');\n return;\n }\n\n if (options.title) {\n console.log(`\\n=== ${options.title} ===`);\n }\n if (options.env) {\n console.log(`Environment: ${options.env}`);\n }\n console.log('');\n\n // Calculate column widths\n const widths = columns.map((col) => {\n const maxContent = Math.max(...items.map((item) => String(col.getValue(item) ?? '').length));\n const width = Math.max(col.header.length, maxContent);\n return col.maxWidth ? Math.min(width, col.maxWidth) : width;\n });\n\n // Print header\n const headerLine = columns.map((col, i) => col.header.padEnd(widths[i]!)).join(' ');\n console.log(headerLine);\n console.log(widths.map((w) => '-'.repeat(w)).join(' '));\n\n // Print rows\n for (const item of items) {\n const row = columns\n .map((col, i) => {\n const value = String(col.getValue(item) ?? '');\n const width = widths[i]!;\n return value.slice(0, width).padEnd(width);\n })\n .join(' ');\n console.log(row);\n }\n\n console.log('');\n console.log(`Total: ${items.length} item(s)`);\n}\n\n/**\n * Format a timestamp for display\n */\nexport function formatTimestamp(isoString: string | null | undefined): string {\n if (!isoString) return '';\n const date = new Date(isoString);\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n\n if (diff < 60000) return 'just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n if (diff < 604800000) return `${Math.floor(diff / 86400000)}d ago`;\n\n return date.toLocaleDateString();\n}\n\n/**\n * Wrap text to a maximum width\n */\nexport function wrapText(text: string, width: number = 70): string[] {\n const words = text.split(' ');\n const lines: string[] = [];\n let currentLine = '';\n\n for (const word of words) {\n if (currentLine.length + word.length + 1 <= width) {\n currentLine += (currentLine ? ' ' : '') + word;\n } else {\n if (currentLine) lines.push(currentLine);\n currentLine = word;\n }\n }\n if (currentLine) lines.push(currentLine);\n return lines;\n}\n\n/**\n * Print a chat message with formatting\n */\nexport function printMessage(\n content: string,\n role: 'user' | 'assistant' | 'system',\n indent: string = ' '\n): void {\n const roleColors: Record<string, string> = {\n user: '\\x1B[34m', // Blue\n assistant: '\\x1B[32m', // Green\n system: '\\x1B[90m', // Gray\n };\n const resetColor = '\\x1B[0m';\n const color = roleColors[role] || resetColor;\n\n const label = role === 'user' ? 'You' : role === 'assistant' ? 'Assistant' : 'System';\n console.log(`${indent}${color}[${label}]${resetColor}`);\n\n const lines = content.split('\\n');\n for (const line of lines) {\n const wrapped = wrapText(line, 70);\n for (const wrappedLine of wrapped) {\n console.log(`${indent} ${wrappedLine}`);\n }\n }\n console.log('');\n}\n\n/**\n * Clear the terminal screen\n */\nexport function clearScreen(): void {\n process.stdout.write('\\x1B[2J\\x1B[H');\n}\n","/**\n * AG-UI Client for CLI chat interactions\n *\n * Implements the AG-UI (Agent-User Interaction) protocol for streaming\n * chat responses. See: https://docs.ag-ui.com/concepts/events\n */\n\nimport type { ConciergeAppContextPayload } from '../lib/cli-client-context';\n\n/**\n * AG-UI event types\n */\nexport type AGUIEventType =\n | 'RUN_STARTED'\n | 'RUN_FINISHED'\n | 'RUN_ERROR'\n | 'TEXT_MESSAGE_START'\n | 'TEXT_MESSAGE_CONTENT'\n | 'TEXT_MESSAGE_END'\n | 'TOOL_CALL_START'\n | 'TOOL_CALL_ARGS'\n | 'TOOL_CALL_END'\n | 'TOOL_CALL_RESULT'\n | 'STATE_SNAPSHOT'\n | 'STATE_DELTA'\n // Lowercase variants (some backends use these)\n | 'run_started'\n | 'run_finished'\n | 'run_error'\n | 'text_message_start'\n | 'text_message_content'\n | 'text_message_end'\n | 'tool_call_start'\n | 'tool_call_args'\n | 'tool_call_end'\n | 'tool_call_result'\n | 'state_snapshot'\n | 'state_delta'\n | 'done';\n\nexport interface AGUIEvent {\n type: AGUIEventType;\n messageId?: string;\n role?: string;\n delta?: string;\n content?: string;\n message?: string;\n code?: string;\n toolCallId?: string;\n toolCallName?: string;\n result?: any;\n snapshot?: any;\n operations?: any[];\n [key: string]: any;\n}\n\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\nexport interface AGUIClientOptions {\n baseUrl: string;\n workspaceUuid?: string;\n chatId?: number | string; // Numeric chat id (documented) or chat uuid (accepted)\n /** Full URL override — when provided, baseUrl/workspaceUuid/chatId are ignored for POST */\n endpointUrl?: string;\n accessToken: string;\n debug?: boolean;\n /** Model field sent in request body (default: 'manager'). Set to undefined to omit. */\n model?: string;\n /** Concierge / client context forwarded to the API (e.g. CLI surface hints). */\n appContext?: ConciergeAppContextPayload;\n}\n\n/**\n * AG-UI Client for streaming chat responses\n */\nexport class AGUIClient {\n private endpointUrl: string;\n private baseUrl: string;\n private workspaceUuid: string;\n private chatId: number | string;\n private accessToken: string;\n private debug: boolean;\n private model: string | undefined;\n private appContext: ConciergeAppContextPayload | undefined;\n\n constructor(options: AGUIClientOptions) {\n this.baseUrl = options.baseUrl;\n this.workspaceUuid = options.workspaceUuid ?? '';\n this.chatId = options.chatId ?? 0;\n if (!options.endpointUrl && (!options.workspaceUuid || options.chatId === undefined)) {\n throw new Error('AGUIClient requires workspaceUuid and chatId, or endpointUrl');\n }\n this.endpointUrl =\n options.endpointUrl ??\n `${options.baseUrl}/api/v2/w/${options.workspaceUuid}/web-chat/${options.chatId}/chat/agui`;\n this.accessToken = options.accessToken;\n this.debug = options.debug ?? false;\n this.model = 'model' in options ? options.model : 'manager';\n this.appContext = options.appContext;\n }\n\n private inferenceStatusUrl(): string | null {\n // Numeric chatId: treat 0 / negative as \"no chat\" (<= 0 rather than a\n // falsy check so a deliberate sentinel of 0 doesn't mean \"valid chat\").\n // String chatId is a uuid; empty string means \"no chat\".\n const noChat = typeof this.chatId === 'number' ? this.chatId <= 0 : this.chatId.length === 0;\n if (!this.workspaceUuid || noChat) return null;\n return `${this.baseUrl}/api/v2/w/${this.workspaceUuid}/chats/${this.chatId}/inference/status`;\n }\n\n private async waitForInferenceInactive(maxWaitMs = 60_000): Promise<boolean> {\n const url = this.inferenceStatusUrl();\n if (!url) return false;\n const deadline = Date.now() + maxWaitMs;\n while (Date.now() < deadline) {\n try {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${this.accessToken}` },\n });\n if (res.ok) {\n const body = (await res.json()) as { active?: boolean };\n if (!body.active) return true;\n }\n } catch (err) {\n // Transient network errors (DNS blips, connection resets) shouldn't\n // crash the 409-retry loop. Log under --debug and keep polling\n // until the deadline; the caller will fall back to the original\n // 409 error if we can't confirm inactive in time.\n if (this.debug) {\n console.log(\n `[DEBUG] waitForInferenceInactive fetch failed: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n await new Promise((r) => setTimeout(r, 2000));\n }\n return false;\n }\n\n /**\n * Send messages and stream the response\n */\n async sendMessage(\n messages: ChatMessage[],\n onEvent: (event: AGUIEvent) => void,\n options?: AguiStreamOptions\n ): Promise<void> {\n return this.streamRequest({ messages }, onEvent, options);\n }\n\n /**\n * Stream an AG-UI request (initial messages or HITL resume).\n */\n async streamRequest(\n request: AguiRequestBody,\n onEvent: (event: AGUIEvent) => void,\n options?: AguiStreamOptions\n ): Promise<void> {\n const body = JSON.stringify({\n ...(this.model ? { model: this.model } : {}),\n messages: request.messages ?? [],\n ...(this.appContext ? { appContext: this.appContext } : {}),\n ...(request.resumeValue !== undefined ? { resumeValue: request.resumeValue } : {}),\n ...(request.resumeInterruptId ? { resumeInterruptId: request.resumeInterruptId } : {}),\n ...(request.resume !== undefined ? { resume: request.resume } : {}),\n ...(options?.autoApprove ? { context: { autoApprove: true } } : {}),\n });\n\n if (this.debug) {\n console.log(`\\n[DEBUG] POST ${this.endpointUrl}`);\n console.log(\n `[DEBUG] Body: ${request.resumeValue !== undefined ? 'resume' : `${(request.messages ?? []).length} message(s)`}`\n );\n }\n\n await this.postStream(body, onEvent, options);\n }\n\n private async postStream(\n body: string,\n onEvent: (event: AGUIEvent) => void,\n options?: AguiStreamOptions,\n conflictRetries = 0\n ): Promise<void> {\n const response = await fetch(this.endpointUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n },\n body,\n });\n\n if (this.debug) {\n console.log(`[DEBUG] Response status: ${response.status}`);\n }\n\n if (response.status === 409 && conflictRetries < 3) {\n const ready = await this.waitForInferenceInactive();\n if (ready) {\n await this.postStream(body, onEvent, options, conflictRetries + 1);\n return;\n }\n }\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => '');\n if (this.debug) {\n console.log(`[DEBUG] Error response: ${errorText}`);\n }\n throw new Error(\n `Chat request failed: ${response.status}${errorText ? `: ${errorText.slice(0, 200)}` : ''}`\n );\n }\n\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') {\n onEvent({ type: 'done' });\n continue;\n }\n try {\n const event = JSON.parse(data) as AGUIEvent;\n onEvent(event);\n } catch {\n // Ignore parse errors for malformed events\n }\n }\n }\n }\n }\n}\n\nexport interface AguiStreamOptions {\n autoApprove?: boolean;\n}\n\nexport interface AguiRequestBody {\n messages?: ChatMessage[];\n /** Concierge AGUI resume fields. */\n resumeValue?: string;\n resumeInterruptId?: string;\n /** Workspace-chat HITL resume (ChatCompletionRequest.resume shape:\n * { decisions: [{type: 'approve'|'edit'|'reject', ...}], interruptId }). */\n resume?: unknown;\n}\n\n/**\n * Helper to check if an event is a text content event\n */\nexport function isTextContentEvent(event: AGUIEvent): boolean {\n const type = event.type.toLowerCase();\n return type === 'text_message_content';\n}\n\n/**\n * Helper to check if an event is an error event\n */\nexport function isErrorEvent(event: AGUIEvent): boolean {\n const type = event.type.toLowerCase();\n return type === 'run_error';\n}\n\n/**\n * Helper to extract text delta from an event\n */\nexport function getTextDelta(event: AGUIEvent): string | null {\n if (isTextContentEvent(event) && event.delta) {\n return event.delta;\n }\n return null;\n}\n\n/**\n * Helper to extract error message from an event\n */\nexport function getErrorMessage(event: AGUIEvent): string | null {\n if (isErrorEvent(event)) {\n return event.message || event.code || 'Unknown error';\n }\n return null;\n}\n"],"mappings":"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","names":["readFileSync","existsSync","join","resolve","join","resolve","existsSync","readFileSync","resolve"]}
@@ -5,6 +5,7 @@ var CLIError = class extends Error {
5
5
  this.exitCode = exitCode;
6
6
  this.name = "CLIError";
7
7
  }
8
+ exitCode;
8
9
  };
9
10
  var AuthRequiredError = class extends CLIError {
10
11
  constructor(message = "Not authenticated. Run `gr login` or set GRAVITY_RAIL_API_KEY.") {
@@ -56,4 +57,4 @@ export {
56
57
  AuthRequiredError,
57
58
  handleError
58
59
  };
59
- //# sourceMappingURL=chunk-7VZ4JLAJ.js.map
60
+ //# sourceMappingURL=chunk-WZVMWN3S.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/errors.ts"],"sourcesContent":["export class CLIError extends Error {\n constructor(\n message: string,\n public exitCode: number = 1\n ) {\n super(message);\n this.name = 'CLIError';\n }\n}\n\nexport class AuthRequiredError extends CLIError {\n constructor(message = 'Not authenticated. Run `gr login` or set GRAVITY_RAIL_API_KEY.') {\n super(message, 2);\n this.name = 'AuthRequiredError';\n }\n}\n\nexport function handleError(err: unknown): never {\n if (err instanceof CLIError) {\n process.stderr.write(`Error: ${err.message}\\n`);\n process.exit(err.exitCode);\n }\n\n if (err instanceof Error) {\n process.stderr.write(`Error: ${err.message}\\n`);\n if ('errors' in err && Array.isArray((err as any).errors)) {\n for (const e of (err as any).errors) {\n process.stderr.write(` - ${e}\\n`);\n }\n }\n if ('statusCode' in err && (err as any).statusCode === 401) {\n process.stderr.write(\n `\\nHint: Run \"gr login\" to authenticate, or set GRAVITY_RAIL_API_KEY for non-interactive use.\\n`\n );\n }\n // In the manager sandbox a 403 almost always means the service token lacks the\n // write/admin scope for this command. The token's scopes are granted by the exec\n // tool's `scopes=[...]` (HITL-approved), so tell the agent how to re-request them.\n if (\n 'statusCode' in err &&\n (err as any).statusCode === 403 &&\n process.env.GRAVITY_RAIL_SANDBOX === '1'\n ) {\n process.stderr.write(\n `\\nHint: This is a write/admin operation and the sandbox token lacks the required scope.\\n` +\n `Re-run the exec tool with the needed write scope, e.g.:\\n` +\n ` exec(code=\"<your gr command>\", language=\"bash\", scopes=[\"datatypes:write\"])\\n` +\n `Common scopes: datatypes:write, workflows:write, members:write, automations:admin, agents:write\\n`\n );\n }\n process.exit(1);\n }\n\n process.stderr.write(`Error: ${String(err)}\\n`);\n process.exit(1);\n}\n"],"mappings":";AAAO,IAAM,WAAN,cAAuB,MAAM;AAAA,EACjC,YACG,SACO,WAAmB,GAC3B;AACC,UAAM,OAAO;AAFN;AAGP,SAAK,OAAO;AAAA,EACf;AACH;AAEO,IAAM,oBAAN,cAAgC,SAAS;AAAA,EAC7C,YAAY,UAAU,kEAAkE;AACrF,UAAM,SAAS,CAAC;AAChB,SAAK,OAAO;AAAA,EACf;AACH;AAEO,SAAS,YAAY,KAAqB;AAC9C,MAAI,eAAe,UAAU;AAC1B,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA,CAAI;AAC9C,YAAQ,KAAK,IAAI,QAAQ;AAAA,EAC5B;AAEA,MAAI,eAAe,OAAO;AACvB,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA,CAAI;AAC9C,QAAI,YAAY,OAAO,MAAM,QAAS,IAAY,MAAM,GAAG;AACxD,iBAAW,KAAM,IAAY,QAAQ;AAClC,gBAAQ,OAAO,MAAM,OAAO,CAAC;AAAA,CAAI;AAAA,MACpC;AAAA,IACH;AACA,QAAI,gBAAgB,OAAQ,IAAY,eAAe,KAAK;AACzD,cAAQ,OAAO;AAAA,QACZ;AAAA;AAAA;AAAA,MACH;AAAA,IACH;AAIA,QACG,gBAAgB,OACf,IAAY,eAAe,OAC5B,QAAQ,IAAI,yBAAyB,KACtC;AACC,cAAQ,OAAO;AAAA,QACZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAIH;AAAA,IACH;AACA,YAAQ,KAAK,CAAC;AAAA,EACjB;AAEA,UAAQ,OAAO,MAAM,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AAC9C,UAAQ,KAAK,CAAC;AACjB;","names":[]}
1
+ {"version":3,"sources":["../src/lib/errors.ts"],"sourcesContent":["export class CLIError extends Error {\n constructor(\n message: string,\n public exitCode: number = 1\n ) {\n super(message);\n this.name = 'CLIError';\n }\n}\n\nexport class AuthRequiredError extends CLIError {\n constructor(message = 'Not authenticated. Run `gr login` or set GRAVITY_RAIL_API_KEY.') {\n super(message, 2);\n this.name = 'AuthRequiredError';\n }\n}\n\nexport function handleError(err: unknown): never {\n if (err instanceof CLIError) {\n process.stderr.write(`Error: ${err.message}\\n`);\n process.exit(err.exitCode);\n }\n\n if (err instanceof Error) {\n process.stderr.write(`Error: ${err.message}\\n`);\n if ('errors' in err && Array.isArray((err as any).errors)) {\n for (const e of (err as any).errors) {\n process.stderr.write(` - ${e}\\n`);\n }\n }\n if ('statusCode' in err && (err as any).statusCode === 401) {\n process.stderr.write(\n `\\nHint: Run \"gr login\" to authenticate, or set GRAVITY_RAIL_API_KEY for non-interactive use.\\n`\n );\n }\n // In the manager sandbox a 403 almost always means the service token lacks the\n // write/admin scope for this command. The token's scopes are granted by the exec\n // tool's `scopes=[...]` (HITL-approved), so tell the agent how to re-request them.\n if (\n 'statusCode' in err &&\n (err as any).statusCode === 403 &&\n process.env.GRAVITY_RAIL_SANDBOX === '1'\n ) {\n process.stderr.write(\n `\\nHint: This is a write/admin operation and the sandbox token lacks the required scope.\\n` +\n `Re-run the exec tool with the needed write scope, e.g.:\\n` +\n ` exec(code=\"<your gr command>\", language=\"bash\", scopes=[\"datatypes:write\"])\\n` +\n `Common scopes: datatypes:write, workflows:write, members:write, automations:admin, agents:write\\n`\n );\n }\n process.exit(1);\n }\n\n process.stderr.write(`Error: ${String(err)}\\n`);\n process.exit(1);\n}\n"],"mappings":";AAAO,IAAM,WAAN,cAAuB,MAAM;AAAA,EACjC,YACG,SACO,WAAmB,GAC3B;AACC,UAAM,OAAO;AAFN;AAGP,SAAK,OAAO;AAAA,EACf;AAAA,EAJU;AAKb;AAEO,IAAM,oBAAN,cAAgC,SAAS;AAAA,EAC7C,YAAY,UAAU,kEAAkE;AACrF,UAAM,SAAS,CAAC;AAChB,SAAK,OAAO;AAAA,EACf;AACH;AAEO,SAAS,YAAY,KAAqB;AAC9C,MAAI,eAAe,UAAU;AAC1B,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA,CAAI;AAC9C,YAAQ,KAAK,IAAI,QAAQ;AAAA,EAC5B;AAEA,MAAI,eAAe,OAAO;AACvB,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA,CAAI;AAC9C,QAAI,YAAY,OAAO,MAAM,QAAS,IAAY,MAAM,GAAG;AACxD,iBAAW,KAAM,IAAY,QAAQ;AAClC,gBAAQ,OAAO,MAAM,OAAO,CAAC;AAAA,CAAI;AAAA,MACpC;AAAA,IACH;AACA,QAAI,gBAAgB,OAAQ,IAAY,eAAe,KAAK;AACzD,cAAQ,OAAO;AAAA,QACZ;AAAA;AAAA;AAAA,MACH;AAAA,IACH;AAIA,QACG,gBAAgB,OACf,IAAY,eAAe,OAC5B,QAAQ,IAAI,yBAAyB,KACtC;AACC,cAAQ,OAAO;AAAA,QACZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAIH;AAAA,IACH;AACA,YAAQ,KAAK,CAAC;AAAA,EACjB;AAEA,UAAQ,OAAO,MAAM,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AAC9C,UAAQ,KAAK,CAAC;AACjB;","names":[]}
@@ -3,11 +3,11 @@ import {
3
3
  getEnvironmentConfig,
4
4
  printTable,
5
5
  refreshAccessToken
6
- } from "./chunk-H62EC2CF.js";
6
+ } from "./chunk-GVUGFPCN.js";
7
7
  import {
8
8
  AuthRequiredError,
9
9
  CLIError
10
- } from "./chunk-7VZ4JLAJ.js";
10
+ } from "./chunk-WZVMWN3S.js";
11
11
 
12
12
  // src/lib/pagination.ts
13
13
  function isListResult(data) {
@@ -321,18 +321,18 @@ function loadRealmCredentials(realm, devRealmsPath) {
321
321
  // src/lib/org-auth.ts
322
322
  import { execFile } from "child_process";
323
323
  var ORG_AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
324
- function extractOrgTokenFromSetCookies(setCookieValues, preferredCookieName) {
324
+ function extractOrgSessionFromSetCookies(setCookieValues, preferredCookieName) {
325
325
  let fallback;
326
326
  for (const value of setCookieValues) {
327
327
  const nameValue = value.split(";")[0]?.trim() ?? "";
328
328
  const eqIdx = nameValue.indexOf("=");
329
329
  if (eqIdx === -1) continue;
330
330
  const name = nameValue.slice(0, eqIdx).trim();
331
- const tokenValue = nameValue.slice(eqIdx + 1).trim() || void 0;
332
- if (!tokenValue) continue;
333
- if (name === preferredCookieName) return tokenValue;
331
+ const credentialValue = nameValue.slice(eqIdx + 1).trim() || void 0;
332
+ if (!credentialValue) continue;
333
+ if (name === preferredCookieName) return credentialValue;
334
334
  if (!fallback && (name.startsWith("orgToken") || name.startsWith("orgSession"))) {
335
- fallback = tokenValue;
335
+ fallback = credentialValue;
336
336
  }
337
337
  }
338
338
  return fallback;
@@ -357,11 +357,11 @@ async function tryDirectJoin(apiUrl, accessToken, orgAuthInfo) {
357
357
  if (setCookies.length === 0) {
358
358
  throw new Error("Org join succeeded but no Set-Cookie header returned");
359
359
  }
360
- const orgToken = extractOrgTokenFromSetCookies(setCookies, orgAuthInfo.cookieName);
361
- if (!orgToken) {
362
- throw new Error("Org join succeeded but org token cookie not found in Set-Cookie");
360
+ const orgSession = extractOrgSessionFromSetCookies(setCookies, orgAuthInfo.cookieName);
361
+ if (!orgSession) {
362
+ throw new Error("Org join succeeded but org session cookie not found in Set-Cookie");
363
363
  }
364
- return orgToken;
364
+ return orgSession;
365
365
  }
366
366
  function isAllowedAuthUrl(url, apiUrl) {
367
367
  try {
@@ -399,9 +399,9 @@ function openBrowser(url) {
399
399
  function registerOrgAuthHandler(apiClient, apiUrl, accessToken) {
400
400
  apiClient.setOrgAuthHandler(async (orgAuthInfo) => {
401
401
  try {
402
- const orgToken = await tryDirectJoin(apiUrl, accessToken, orgAuthInfo);
403
- if (orgToken !== null) {
404
- apiClient.setOrgToken(orgToken, orgAuthInfo.orgUuid);
402
+ const orgSession = await tryDirectJoin(apiUrl, accessToken, orgAuthInfo);
403
+ if (orgSession !== null) {
404
+ apiClient.setOrgSession(orgSession, orgAuthInfo.orgUuid);
405
405
  return;
406
406
  }
407
407
  } catch (err) {
@@ -420,7 +420,7 @@ This workspace requires organization SSO authentication.`);
420
420
  `);
421
421
  stderr(`If the browser does not open, visit the URL above manually.`);
422
422
  stderr(
423
- `After signing in, re-run the command \u2014 the CLI cannot receive the SSO token directly.`
423
+ `After signing in, re-run the command \u2014 the CLI cannot receive the SSO session directly.`
424
424
  );
425
425
  openBrowser(orgAuthInfo.authUrl);
426
426
  await new Promise((_, reject) => {
@@ -661,11 +661,14 @@ async function getConciergeChat(ctx, chatId) {
661
661
  if (!res.ok) throw new Error(`Failed to get concierge chat ${chatId}: ${res.status}`);
662
662
  return toCamelChat(await res.json());
663
663
  }
664
- async function originateConciergeCall(ctx, kind = "concierge_setup") {
664
+ async function originateConciergeCall(ctx, kind = "concierge_setup", outboundMessage, clientSurface) {
665
+ const body = { kind };
666
+ if (outboundMessage) body.outbound_message = outboundMessage;
667
+ if (clientSurface) body.client_surface = clientSurface;
665
668
  const res = await fetch(`${baseUrl(ctx)}/calls`, {
666
669
  method: "POST",
667
670
  headers: authHeaders(ctx),
668
- body: JSON.stringify({ kind })
671
+ body: JSON.stringify(body)
669
672
  });
670
673
  if (res.status === 412) {
671
674
  throw new Error("Your account phone is not verified. Run `gr account verify-phone` first.");
@@ -741,6 +744,7 @@ export {
741
744
  outputSingle,
742
745
  COL,
743
746
  stderr,
747
+ createSandboxFetchFromEnv,
744
748
  createContext,
745
749
  createAuthenticatedContext,
746
750
  requireAuth,
@@ -755,4 +759,4 @@ export {
755
759
  confirmAccountPhoneVerify,
756
760
  getConciergeChatMessages
757
761
  };
758
- //# sourceMappingURL=chunk-KTTHOL3B.js.map
762
+ //# sourceMappingURL=chunk-XJ2EBXJZ.js.map