@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 +40 -0
- package/dist/{account-verify-phone-TDQNZ5XN.js → account-verify-phone-XNXA7HNG.js} +4 -4
- package/dist/{chunk-H62EC2CF.js → chunk-GVUGFPCN.js} +25 -11
- package/dist/chunk-GVUGFPCN.js.map +1 -0
- package/dist/{chunk-7VZ4JLAJ.js → chunk-WZVMWN3S.js} +2 -1
- package/dist/{chunk-7VZ4JLAJ.js.map → chunk-WZVMWN3S.js.map} +1 -1
- package/dist/{chunk-KTTHOL3B.js → chunk-XJ2EBXJZ.js} +22 -18
- package/dist/chunk-XJ2EBXJZ.js.map +1 -0
- package/dist/{cli-client-context-9KRUt6O-.d.ts → cli-client-context-BbyBEGcg.d.ts} +1 -0
- package/dist/cli-utils.d.ts +7 -3
- package/dist/cli-utils.js +1 -1
- package/dist/{errors-VK4QO3QZ.js → errors-YHGVIDPO.js} +2 -2
- package/dist/gr.js +4621 -1347
- package/dist/gr.js.map +1 -1
- package/dist/lib.d.ts +15 -3
- package/dist/lib.js +2911 -486
- package/dist/lib.js.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-H62EC2CF.js.map +0 -1
- package/dist/chunk-KTTHOL3B.js.map +0 -1
- /package/dist/{account-verify-phone-TDQNZ5XN.js.map → account-verify-phone-XNXA7HNG.js.map} +0 -0
- /package/dist/{errors-VK4QO3QZ.js.map → errors-YHGVIDPO.js.map} +0 -0
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-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
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-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
553
|
+
resolve3({ item: null, isNew: true });
|
|
542
554
|
} else {
|
|
543
|
-
|
|
555
|
+
resolve3({ item: selected, isNew: false });
|
|
544
556
|
}
|
|
545
557
|
} else {
|
|
546
|
-
|
|
558
|
+
resolve3({ item: null, isNew: false });
|
|
547
559
|
}
|
|
548
560
|
} else if (key.name === "escape" || key.ctrl && key.name === "c") {
|
|
549
561
|
cleanup();
|
|
550
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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;
|
|
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-
|
|
6
|
+
} from "./chunk-GVUGFPCN.js";
|
|
7
7
|
import {
|
|
8
8
|
AuthRequiredError,
|
|
9
9
|
CLIError
|
|
10
|
-
} from "./chunk-
|
|
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
|
|
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
|
|
332
|
-
if (!
|
|
333
|
-
if (name === preferredCookieName) return
|
|
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 =
|
|
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
|
|
361
|
-
if (!
|
|
362
|
-
throw new Error("Org join succeeded but org
|
|
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
|
|
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
|
|
403
|
-
if (
|
|
404
|
-
apiClient.
|
|
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
|
|
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(
|
|
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-
|
|
762
|
+
//# sourceMappingURL=chunk-XJ2EBXJZ.js.map
|