@gravity-rail/cli 0.8.0 → 0.11.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 +146 -80
- package/dist/{account-verify-phone-TC73QIIE.js → account-verify-phone-7YQXKU3X.js} +4 -4
- package/dist/{chunk-L5YCPUAM.js → chunk-GRLUFHMI.js} +63 -131
- package/dist/chunk-GRLUFHMI.js.map +1 -0
- package/dist/{chunk-H62EC2CF.js → chunk-SIV2WU2I.js} +307 -54
- package/dist/chunk-SIV2WU2I.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/cli-client-context-C5l8lg7h.d.ts +130 -0
- package/dist/cli-utils.d.ts +65 -4
- package/dist/cli-utils.js +3 -1
- package/dist/{errors-VK4QO3QZ.js → errors-YHGVIDPO.js} +2 -2
- package/dist/gr.js +9425 -3203
- package/dist/gr.js.map +1 -1
- package/dist/lib.d.ts +77 -9
- package/dist/lib.js +3346 -860
- package/dist/lib.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-H62EC2CF.js.map +0 -1
- package/dist/chunk-L5YCPUAM.js.map +0 -1
- package/dist/cli-client-context-9KRUt6O-.d.ts +0 -98
- /package/dist/{account-verify-phone-TC73QIIE.js.map → account-verify-phone-7YQXKU3X.js.map} +0 -0
- /package/dist/{errors-VK4QO3QZ.js.map → errors-YHGVIDPO.js.map} +0 -0
package/README.md
CHANGED
|
@@ -26,7 +26,58 @@ gr whoami # verify your identity
|
|
|
26
26
|
gr logout # clear cached credentials
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
### Multi-account profiles
|
|
30
|
+
|
|
31
|
+
Like `aws --profile` / `gh auth switch`, the public CLI supports multiple named accounts:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
gr auth login --name work # OAuth into a named profile (also sets it current)
|
|
35
|
+
gr auth login --name personal
|
|
36
|
+
gr auth list # list profiles for the current --env
|
|
37
|
+
gr auth switch --name personal # change the default profile
|
|
38
|
+
gr auth logout --name work # clear one profile
|
|
39
|
+
|
|
40
|
+
# Per-invocation selection (does not change the stored default)
|
|
41
|
+
gr whoami --profile work
|
|
42
|
+
gr tasks list -w $WORKSPACE --profile personal
|
|
43
|
+
export GRAVITY_RAIL_PROFILE=work
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`gr login` / `gr logout` remain as shortcuts for the active profile (or `--name`).
|
|
47
|
+
|
|
48
|
+
**Local monorepo / QA (Gravity Rail developers):** after provisioning, import
|
|
49
|
+
dev tokens into the same profile store (this replaces the old `--realm` flag):
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
yarn gr auth import-dev # or: python scripts/sync_gr_cli_profiles.py
|
|
53
|
+
yarn gr whoami --profile superadmin
|
|
54
|
+
yarn gr tasks list --profile acme-demo/acme-admin # auto-sets -w
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Workspace/member names use one slash (`workspace/member`). Provisioning auto-syncs
|
|
58
|
+
profiles; re-run import after re-seed. Worktree API ports get isolated credential files.
|
|
59
|
+
|
|
60
|
+
### Credential store (security)
|
|
61
|
+
|
|
62
|
+
| Item | Detail |
|
|
63
|
+
| --------------- | ---------------------------------------------------------------------------- |
|
|
64
|
+
| Location | `~/.gr-cli/credentials-<env>.json` (e.g. `credentials-prod.json`) |
|
|
65
|
+
| Directory mode | `0700` on `~/.gr-cli` |
|
|
66
|
+
| File mode | `0600` on credential files |
|
|
67
|
+
| Contents | OAuth client id/secret + tokens, and/or token-only dev profiles (+ optional workspace UUID) |
|
|
68
|
+
| Not stored | `GRAVITY_RAIL_API_KEY` (process environment only; always overrides profiles) |
|
|
69
|
+
| Name validation | Profiles allow `name` or `workspace/member`; env names are allowlisted |
|
|
70
|
+
| Logging | CLI code never writes token or secret values to stdout/stderr |
|
|
71
|
+
|
|
72
|
+
**Security review notes:** tokens never leave the local filesystem except on HTTPS calls to the Gravity Rail API. There is no cloud sync of CLI credentials. Rotate by `gr auth logout --name <profile>` (or deleting the store file) and logging in again. Multi-user machines should rely on OS home-directory isolation plus the `0600`/`0700` modes. Do not commit `~/.gr-cli` into repositories or bake it into container images.
|
|
73
|
+
|
|
74
|
+
Profile resolution order for a command:
|
|
75
|
+
|
|
76
|
+
1. `GRAVITY_RAIL_API_KEY` (if set — skips the store entirely)
|
|
77
|
+
2. `--profile <name>` (or `--account`)
|
|
78
|
+
3. `GRAVITY_RAIL_PROFILE`
|
|
79
|
+
4. `currentProfile` in the credential store (from `gr auth switch` / last login)
|
|
80
|
+
5. Profile name `default`
|
|
30
81
|
|
|
31
82
|
### API Key (non-interactive)
|
|
32
83
|
|
|
@@ -35,7 +86,7 @@ export GRAVITY_RAIL_API_KEY=your-api-key
|
|
|
35
86
|
gr tasks list -w $WORKSPACE
|
|
36
87
|
```
|
|
37
88
|
|
|
38
|
-
Useful for scripts, CI/CD, and automation. Create scoped keys from workspace settings or via [`@gravity-rail/sdk`](https://www.npmjs.com/package/@gravity-rail/sdk).
|
|
89
|
+
Useful for scripts, CI/CD, and automation. Create scoped keys from workspace settings or via [`@gravity-rail/sdk`](https://www.npmjs.com/package/@gravity-rail/sdk). API keys are **not** written into the profile store.
|
|
39
90
|
|
|
40
91
|
## Command Pattern
|
|
41
92
|
|
|
@@ -114,35 +165,37 @@ cat member.json | gr members update -w $WORKSPACE --id 42 --allow-write
|
|
|
114
165
|
|
|
115
166
|
## Global Options
|
|
116
167
|
|
|
117
|
-
| Flag
|
|
118
|
-
|
|
119
|
-
| `--api-url <url>`
|
|
120
|
-
|
|
|
121
|
-
| `-
|
|
122
|
-
| `-
|
|
123
|
-
|
|
|
124
|
-
| `--
|
|
125
|
-
|
|
|
126
|
-
| `-
|
|
127
|
-
|
|
|
128
|
-
| `--
|
|
168
|
+
| Flag | Description |
|
|
169
|
+
| --------------------------- | -------------------------------------------- |
|
|
170
|
+
| `--api-url <url>` | Override API base URL |
|
|
171
|
+
| `--profile <name>` | Named account profile (alias: `--account`) |
|
|
172
|
+
| `-w, --workspace <uuid>` | Workspace UUID (required for most commands) |
|
|
173
|
+
| `-o, --output-format <fmt>` | Output format: `json`, `jsonl`, `table` |
|
|
174
|
+
| `-v, --verbose` | Verbose output to stderr |
|
|
175
|
+
| `--allow-write` | Required for create/update/delete operations |
|
|
176
|
+
| `--id <id>` | Entity ID (for get/update/delete) |
|
|
177
|
+
| `-d, --data <json>` | Inline JSON payload |
|
|
178
|
+
| `-f, --file <path>` | Read JSON payload from file |
|
|
179
|
+
| `--help` | Show help |
|
|
180
|
+
| `--version` | Show version |
|
|
129
181
|
|
|
130
182
|
## Environment Variables
|
|
131
183
|
|
|
132
|
-
| Variable
|
|
133
|
-
|
|
134
|
-
| `GRAVITY_RAIL_API_KEY`
|
|
135
|
-
| `
|
|
184
|
+
| Variable | Description |
|
|
185
|
+
| --------------------------- | -------------------------------------------------------------- |
|
|
186
|
+
| `GRAVITY_RAIL_API_KEY` | API key for non-interactive authentication |
|
|
187
|
+
| `GRAVITY_RAIL_PROFILE` | Default named credential profile |
|
|
188
|
+
| `GRAVITY_RAIL_API_URL` | Override API base URL (default: `https://api.gravityrail.com`) |
|
|
136
189
|
| `GRAVITY_RAIL_FRONTEND_URL` | Override frontend URL (default: `https://app.gravityrail.com`) |
|
|
137
190
|
|
|
138
191
|
## Exit Codes
|
|
139
192
|
|
|
140
|
-
| Code | Meaning
|
|
141
|
-
|
|
142
|
-
| `0`
|
|
143
|
-
| `1`
|
|
144
|
-
| `2`
|
|
145
|
-
| `3`
|
|
193
|
+
| Code | Meaning |
|
|
194
|
+
| ---- | -------------------------------------------------------------------------------------------------- |
|
|
195
|
+
| `0` | Success |
|
|
196
|
+
| `1` | General error |
|
|
197
|
+
| `2` | Authentication required (run `gr login` or set `GRAVITY_RAIL_API_KEY`) |
|
|
198
|
+
| `3` | A human-in-the-loop (HITL) interrupt requires out-of-band action before the operation can continue |
|
|
146
199
|
|
|
147
200
|
When a concierge command run with `--message` (non-interactive mode) hits a HITL
|
|
148
201
|
interrupt the CLI cannot resolve on its own, it exits `3` and writes a single
|
|
@@ -150,10 +203,10 @@ machine-readable JSON object to **stdout**:
|
|
|
150
203
|
|
|
151
204
|
```json
|
|
152
205
|
{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
206
|
+
"error": "hitl_interrupt",
|
|
207
|
+
"message": "A human-in-the-loop interrupt requires out-of-band action before the operation can continue.",
|
|
208
|
+
"resume": { "chatId": 42, "chatUuid": "…", "interruptId": "int-abc" },
|
|
209
|
+
"interrupt": { "type": "stripe_checkout", "interrupt_id": "int-abc", "url": "…" }
|
|
157
210
|
}
|
|
158
211
|
```
|
|
159
212
|
|
|
@@ -166,73 +219,76 @@ not forwarded, as it may contain sensitive data. Use `resume.chatUuid` /
|
|
|
166
219
|
|
|
167
220
|
### Core
|
|
168
221
|
|
|
169
|
-
| Domain
|
|
170
|
-
|
|
171
|
-
| `tasks`
|
|
172
|
-
| `chats`
|
|
173
|
-
| `members`
|
|
174
|
-
| `workspaces`
|
|
175
|
-
| `workflows`
|
|
176
|
-
| `agents`
|
|
177
|
-
| `assistants`
|
|
178
|
-
| `assignments` | Task-based conversations — CRUD, messages, tools
|
|
222
|
+
| Domain | Description |
|
|
223
|
+
| ------------- | -------------------------------------------------------------------------- |
|
|
224
|
+
| `tasks` | Workflow tasks — CRUD, archive, clone |
|
|
225
|
+
| `chats` | Conversations — CRUD, labels, filters, messages, export, summaries |
|
|
226
|
+
| `members` | Contacts and team — CRUD, labels, filters, roles, fields, import/export |
|
|
227
|
+
| `workspaces` | Workspace management — CRUD, features, themes, invitations, export/import |
|
|
228
|
+
| `workflows` | Conversational workflows — CRUD, notes, contributors, templates, analytics |
|
|
229
|
+
| `agents` | Autonomous AI members — CRUD, archive |
|
|
230
|
+
| `assistants` | AI personas — CRUD with model and voice configuration |
|
|
231
|
+
| `assignments` | Task-based conversations — CRUD, messages, tools |
|
|
179
232
|
|
|
180
233
|
### Data & Content
|
|
181
234
|
|
|
182
|
-
| Domain
|
|
183
|
-
|
|
184
|
-
| `data-types`
|
|
185
|
-
| `files`
|
|
186
|
-
| `sites`
|
|
187
|
-
| `events`
|
|
188
|
-
| `calendars`
|
|
189
|
-
| `qualifications` | Skills evaluation — requirements, submissions, scoring
|
|
235
|
+
| Domain | Description |
|
|
236
|
+
| ---------------- | ----------------------------------------------------------------------- |
|
|
237
|
+
| `data-types` | Schema-driven forms and records — CRUD, field indexing, computed fields |
|
|
238
|
+
| `files` | Documents and folders — CRUD, sharing, semantic search, labels |
|
|
239
|
+
| `sites` | Customer portals — CRUD, pages, menus, web crawling |
|
|
240
|
+
| `events` | Automation rules — triggers, CEL conditions, delayed actions, webhooks |
|
|
241
|
+
| `calendars` | Scheduling — calendars, events, event types, availability, Google sync |
|
|
242
|
+
| `qualifications` | Skills evaluation — requirements, submissions, scoring |
|
|
243
|
+
| `queries` | Unified Query Engine — fields, validate, preview, run, syntax reference |
|
|
190
244
|
|
|
191
245
|
### Global (no workspace)
|
|
192
246
|
|
|
193
|
-
| Domain
|
|
194
|
-
|
|
195
|
-
| `concierge`
|
|
196
|
-
| `account verify-phone` | Verify your account phone (required before concierge outbound)
|
|
247
|
+
| Domain | Description |
|
|
248
|
+
| ---------------------- | --------------------------------------------------------------- |
|
|
249
|
+
| `concierge` | Global Concierge — interactive chat, PSTN `--call`, SMS `--sms` |
|
|
250
|
+
| `account verify-phone` | Verify your account phone (required before concierge outbound) |
|
|
197
251
|
|
|
198
252
|
### Communication
|
|
199
253
|
|
|
200
|
-
| Domain
|
|
201
|
-
|
|
202
|
-
| `phone-numbers`
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
254
|
+
| Domain | Description |
|
|
255
|
+
| -------------------- | ------------------------------------------------------------------------- |
|
|
256
|
+
| `phone-numbers` | Voice and SMS phone numbers — provisioning, calls, messages |
|
|
257
|
+
| `recordings` | Call recordings — list chunks, get a presigned download URL, save the WAV |
|
|
258
|
+
| `inboxes` | Email inboxes — threads, attachments, routing |
|
|
259
|
+
| `operator-groups` | Live operator routing — groups, presence, strategies |
|
|
260
|
+
| `notification-rules` | Alert configuration — event-based notifications |
|
|
206
261
|
|
|
207
262
|
### Integrations
|
|
208
263
|
|
|
209
|
-
| Domain
|
|
210
|
-
|
|
211
|
-
| `discord-bots`
|
|
212
|
-
| `slack-apps`
|
|
213
|
-
| `fhir-connections` | FHIR healthcare connections — patients, practitioners, encounters
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
264
|
+
| Domain | Description |
|
|
265
|
+
| ------------------ | -------------------------------------------------------------------------------------------------- |
|
|
266
|
+
| `discord-bots` | Discord bot management — slash commands, threads, account linking |
|
|
267
|
+
| `slack-apps` | Slack app management — installations, threads, accounts |
|
|
268
|
+
| `fhir-connections` | FHIR healthcare connections — patients, practitioners, encounters |
|
|
269
|
+
| `patientsync` | PatientSync PSTN-transfer connection — `configure` / `status` / `delete` (singleton per workspace) |
|
|
270
|
+
| `app-connections` | Platform app integrations |
|
|
271
|
+
| `monday` | Monday.com — boards, columns, webhooks |
|
|
216
272
|
|
|
217
273
|
### Platform
|
|
218
274
|
|
|
219
|
-
| Domain
|
|
220
|
-
|
|
221
|
-
| `api-keys`
|
|
222
|
-
| `subscriptions`
|
|
223
|
-
| `reports`
|
|
224
|
-
| `ai-models`
|
|
225
|
-
| `features`
|
|
226
|
-
| `custom-toolkits` | Custom tool definitions
|
|
227
|
-
| `mcp-servers`
|
|
228
|
-
| `milestones`
|
|
229
|
-
| `supervisors`
|
|
230
|
-
| `access-grants`
|
|
231
|
-
| `pronunciations`
|
|
232
|
-
| `apps`
|
|
233
|
-
| `oauth`
|
|
234
|
-
| `auth`
|
|
235
|
-
| `org`
|
|
275
|
+
| Domain | Description |
|
|
276
|
+
| ----------------- | --------------------------------------------------------------- |
|
|
277
|
+
| `api-keys` | API key management — workspace and global scopes |
|
|
278
|
+
| `subscriptions` | Subscription management |
|
|
279
|
+
| `reports` | Usage reports — AI tokens, voice minutes, SMS, storage |
|
|
280
|
+
| `ai-models` | Available AI models (no workspace required) |
|
|
281
|
+
| `features` | Feature registry (no workspace required) |
|
|
282
|
+
| `custom-toolkits` | Custom tool definitions |
|
|
283
|
+
| `mcp-servers` | MCP server connections |
|
|
284
|
+
| `milestones` | Goal tracking |
|
|
285
|
+
| `supervisors` | AI supervisor management |
|
|
286
|
+
| `access-grants` | Temporary access delegation |
|
|
287
|
+
| `pronunciations` | Voice pronunciation overrides |
|
|
288
|
+
| `apps` | OAuth app management |
|
|
289
|
+
| `oauth` | OAuth provider connections |
|
|
290
|
+
| `auth` | Two-factor authentication, credentials, and password management |
|
|
291
|
+
| `org` | Organization membership, invitations, and org domains |
|
|
236
292
|
|
|
237
293
|
## Recipes
|
|
238
294
|
|
|
@@ -277,6 +333,16 @@ gr pronunciations list -w $WORKSPACE
|
|
|
277
333
|
gr pronunciations create -w $WORKSPACE --term "GravityRail" --respelling "GRA-vit-ee rail" --allow-write
|
|
278
334
|
```
|
|
279
335
|
|
|
336
|
+
### Download a call recording
|
|
337
|
+
|
|
338
|
+
```sh
|
|
339
|
+
# Get a single short-lived presigned URL for the whole call (assembled once, cached).
|
|
340
|
+
gr recordings download-url -w $WORKSPACE --chat-id 42
|
|
341
|
+
|
|
342
|
+
# Or download the assembled WAV straight to a file.
|
|
343
|
+
gr recordings download -w $WORKSPACE --chat-id 42 -o call.wav
|
|
344
|
+
```
|
|
345
|
+
|
|
280
346
|
### Register and verify an org domain
|
|
281
347
|
|
|
282
348
|
```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-GRLUFHMI.js";
|
|
6
|
+
import "./chunk-SIV2WU2I.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-7YQXKU3X.js.map
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
getEnvironmentConfig,
|
|
4
4
|
printTable,
|
|
5
5
|
refreshAccessToken
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SIV2WU2I.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) {
|
|
@@ -208,11 +208,11 @@ var COL = {
|
|
|
208
208
|
status: () => ({ header: "STATUS", getValue: (item) => item.status ?? "" }),
|
|
209
209
|
created: () => ({
|
|
210
210
|
header: "CREATED",
|
|
211
|
-
getValue: (item) => item.
|
|
211
|
+
getValue: (item) => item.createdAt ?? ""
|
|
212
212
|
}),
|
|
213
213
|
updated: () => ({
|
|
214
214
|
header: "UPDATED",
|
|
215
|
-
getValue: (item) => item.
|
|
215
|
+
getValue: (item) => item.updatedAt ?? ""
|
|
216
216
|
}),
|
|
217
217
|
description: () => ({
|
|
218
218
|
header: "DESCRIPTION",
|
|
@@ -231,108 +231,21 @@ function stderr(msg) {
|
|
|
231
231
|
import { GravityRailClient } from "@gravity-rail/sdk";
|
|
232
232
|
import { homedir } from "os";
|
|
233
233
|
|
|
234
|
-
// src/lib/realm.ts
|
|
235
|
-
import { execSync } from "child_process";
|
|
236
|
-
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
237
|
-
import { dirname, join } from "path";
|
|
238
|
-
function findDevRealmsFile() {
|
|
239
|
-
let dir = process.cwd();
|
|
240
|
-
while (dir !== dirname(dir)) {
|
|
241
|
-
const candidate = join(dir, "dev-realms.json");
|
|
242
|
-
if (existsSync(candidate)) return candidate;
|
|
243
|
-
dir = dirname(dir);
|
|
244
|
-
}
|
|
245
|
-
throw new Error("Cannot find dev-realms.json in any parent directory");
|
|
246
|
-
}
|
|
247
|
-
var DEV_CLOUD_CACHE_DIR = ".dev-cloud";
|
|
248
|
-
var CACHE_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
249
|
-
function findRepoRoot() {
|
|
250
|
-
let dir = process.cwd();
|
|
251
|
-
while (dir !== dirname(dir)) {
|
|
252
|
-
if (existsSync(join(dir, ".git"))) {
|
|
253
|
-
return dir;
|
|
254
|
-
}
|
|
255
|
-
dir = dirname(dir);
|
|
256
|
-
}
|
|
257
|
-
return void 0;
|
|
258
|
-
}
|
|
259
|
-
function getDevCloudCacheDir(namespace) {
|
|
260
|
-
const repoRoot = findRepoRoot();
|
|
261
|
-
const base = repoRoot ?? process.cwd();
|
|
262
|
-
return join(base, DEV_CLOUD_CACHE_DIR, namespace);
|
|
263
|
-
}
|
|
264
|
-
function fetchDevCloudRealms(namespace) {
|
|
265
|
-
const k8sNamespace = `dev-cloud-${namespace}`;
|
|
266
|
-
const cmd = `kubectl exec -n ${k8sNamespace} deploy/env -c devbox -- cat /workspace/dev-realms.json`;
|
|
267
|
-
try {
|
|
268
|
-
return execSync(cmd, { encoding: "utf-8", timeout: 3e4 });
|
|
269
|
-
} catch (err) {
|
|
270
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
271
|
-
throw new Error(
|
|
272
|
-
`Failed to fetch dev-realms.json from dev-cloud pod "${namespace}": ${message}`
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
function loadDevCloudRealmFile(namespace) {
|
|
277
|
-
const cacheDir = getDevCloudCacheDir(namespace);
|
|
278
|
-
const cachePath = join(cacheDir, "dev-realms.json");
|
|
279
|
-
if (existsSync(cachePath)) {
|
|
280
|
-
const stat = statSync(cachePath);
|
|
281
|
-
const age = Date.now() - stat.mtimeMs;
|
|
282
|
-
if (age < CACHE_MAX_AGE_MS) {
|
|
283
|
-
return cachePath;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
const content = fetchDevCloudRealms(namespace);
|
|
287
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
288
|
-
writeFileSync(cachePath, content, "utf-8");
|
|
289
|
-
return cachePath;
|
|
290
|
-
}
|
|
291
|
-
function loadRealmCredentials(realm, devRealmsPath) {
|
|
292
|
-
const filePath = devRealmsPath ?? findDevRealmsFile();
|
|
293
|
-
let data;
|
|
294
|
-
try {
|
|
295
|
-
data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
296
|
-
} catch {
|
|
297
|
-
throw new Error(`Cannot read dev-realms.json at ${filePath}`);
|
|
298
|
-
}
|
|
299
|
-
const parts = realm.split("/");
|
|
300
|
-
if (parts.length === 1) {
|
|
301
|
-
const accounts = data._accounts;
|
|
302
|
-
const account = accounts?.[parts[0]];
|
|
303
|
-
if (!account?.token) {
|
|
304
|
-
throw new Error(`Realm account "${parts[0]}" not found in dev-realms.json`);
|
|
305
|
-
}
|
|
306
|
-
return { token: account.token };
|
|
307
|
-
} else {
|
|
308
|
-
const [wsName, memberName] = parts;
|
|
309
|
-
const ws = data[wsName];
|
|
310
|
-
if (!ws) {
|
|
311
|
-
throw new Error(`Realm workspace "${wsName}" not found in dev-realms.json`);
|
|
312
|
-
}
|
|
313
|
-
const member = ws.members?.[memberName];
|
|
314
|
-
if (!member?.token) {
|
|
315
|
-
throw new Error(`Realm member "${memberName}" not found in workspace "${wsName}"`);
|
|
316
|
-
}
|
|
317
|
-
return { token: member.token, workspaceUuid: ws.workspaceUuid };
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
234
|
// src/lib/org-auth.ts
|
|
322
235
|
import { execFile } from "child_process";
|
|
323
236
|
var ORG_AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
324
|
-
function
|
|
237
|
+
function extractOrgSessionFromSetCookies(setCookieValues, preferredCookieName) {
|
|
325
238
|
let fallback;
|
|
326
239
|
for (const value of setCookieValues) {
|
|
327
240
|
const nameValue = value.split(";")[0]?.trim() ?? "";
|
|
328
241
|
const eqIdx = nameValue.indexOf("=");
|
|
329
242
|
if (eqIdx === -1) continue;
|
|
330
243
|
const name = nameValue.slice(0, eqIdx).trim();
|
|
331
|
-
const
|
|
332
|
-
if (!
|
|
333
|
-
if (name === preferredCookieName) return
|
|
244
|
+
const credentialValue = nameValue.slice(eqIdx + 1).trim() || void 0;
|
|
245
|
+
if (!credentialValue) continue;
|
|
246
|
+
if (name === preferredCookieName) return credentialValue;
|
|
334
247
|
if (!fallback && (name.startsWith("orgToken") || name.startsWith("orgSession"))) {
|
|
335
|
-
fallback =
|
|
248
|
+
fallback = credentialValue;
|
|
336
249
|
}
|
|
337
250
|
}
|
|
338
251
|
return fallback;
|
|
@@ -357,11 +270,11 @@ async function tryDirectJoin(apiUrl, accessToken, orgAuthInfo) {
|
|
|
357
270
|
if (setCookies.length === 0) {
|
|
358
271
|
throw new Error("Org join succeeded but no Set-Cookie header returned");
|
|
359
272
|
}
|
|
360
|
-
const
|
|
361
|
-
if (!
|
|
362
|
-
throw new Error("Org join succeeded but org
|
|
273
|
+
const orgSession = extractOrgSessionFromSetCookies(setCookies, orgAuthInfo.cookieName);
|
|
274
|
+
if (!orgSession) {
|
|
275
|
+
throw new Error("Org join succeeded but org session cookie not found in Set-Cookie");
|
|
363
276
|
}
|
|
364
|
-
return
|
|
277
|
+
return orgSession;
|
|
365
278
|
}
|
|
366
279
|
function isAllowedAuthUrl(url, apiUrl) {
|
|
367
280
|
try {
|
|
@@ -399,9 +312,9 @@ function openBrowser(url) {
|
|
|
399
312
|
function registerOrgAuthHandler(apiClient, apiUrl, accessToken) {
|
|
400
313
|
apiClient.setOrgAuthHandler(async (orgAuthInfo) => {
|
|
401
314
|
try {
|
|
402
|
-
const
|
|
403
|
-
if (
|
|
404
|
-
apiClient.
|
|
315
|
+
const orgSession = await tryDirectJoin(apiUrl, accessToken, orgAuthInfo);
|
|
316
|
+
if (orgSession !== null) {
|
|
317
|
+
apiClient.setOrgSession(orgSession, orgAuthInfo.orgUuid);
|
|
405
318
|
return;
|
|
406
319
|
}
|
|
407
320
|
} catch (err) {
|
|
@@ -420,7 +333,7 @@ This workspace requires organization SSO authentication.`);
|
|
|
420
333
|
`);
|
|
421
334
|
stderr(`If the browser does not open, visit the URL above manually.`);
|
|
422
335
|
stderr(
|
|
423
|
-
`After signing in, re-run the command \u2014 the CLI cannot receive the SSO
|
|
336
|
+
`After signing in, re-run the command \u2014 the CLI cannot receive the SSO session directly.`
|
|
424
337
|
);
|
|
425
338
|
openBrowser(orgAuthInfo.authUrl);
|
|
426
339
|
await new Promise((_, reject) => {
|
|
@@ -517,55 +430,65 @@ async function tryTokenRefresh(ctx, creds) {
|
|
|
517
430
|
ctx.envConfig,
|
|
518
431
|
creds,
|
|
519
432
|
ctx.credentialsManager,
|
|
520
|
-
ctx.env
|
|
433
|
+
ctx.env,
|
|
434
|
+
ctx.profile
|
|
521
435
|
);
|
|
522
436
|
if (!refreshed?.accessToken) return null;
|
|
523
437
|
const apiClient = makeApiClient(refreshed.accessToken, ctx.envConfig.apiUrl);
|
|
524
|
-
return {
|
|
438
|
+
return {
|
|
439
|
+
...ctx,
|
|
440
|
+
accessToken: refreshed.accessToken,
|
|
441
|
+
apiClient,
|
|
442
|
+
profileWorkspace: refreshed.workspaceUuid ?? creds.workspaceUuid
|
|
443
|
+
};
|
|
525
444
|
}
|
|
526
445
|
function createContext(opts) {
|
|
527
446
|
const baseConfig = getEnvironmentConfig(opts.env);
|
|
528
447
|
const apiUrl = opts.apiUrl || process.env.GRAVITY_RAIL_API_URL || baseConfig.apiUrl;
|
|
529
448
|
const envConfig = apiUrl === baseConfig.apiUrl ? baseConfig : { ...baseConfig, apiUrl };
|
|
530
449
|
const credentialsManager = new CredentialsManager(homedir(), envConfig.apiUrl);
|
|
450
|
+
const profile = credentialsManager.resolveProfileName(opts.profile, opts.env);
|
|
531
451
|
return {
|
|
532
452
|
env: opts.env,
|
|
533
453
|
envConfig,
|
|
534
454
|
outputFormat: detectOutputFormat(opts.outputFormat),
|
|
535
455
|
verbose: opts.verbose ?? false,
|
|
536
456
|
allowWrite: opts.allowWrite ?? false,
|
|
537
|
-
credentialsManager
|
|
457
|
+
credentialsManager,
|
|
458
|
+
profile
|
|
538
459
|
};
|
|
539
460
|
}
|
|
540
461
|
async function createAuthenticatedContext(opts) {
|
|
541
462
|
const ctx = createContext(opts);
|
|
542
|
-
if (opts.realm) {
|
|
543
|
-
let devRealmsPath;
|
|
544
|
-
if (opts.env.startsWith("dev-cloud:")) {
|
|
545
|
-
const namespace = opts.env.slice("dev-cloud:".length);
|
|
546
|
-
devRealmsPath = loadDevCloudRealmFile(namespace);
|
|
547
|
-
}
|
|
548
|
-
const { token, workspaceUuid } = loadRealmCredentials(opts.realm, devRealmsPath);
|
|
549
|
-
const apiClient = makeApiClient(token, ctx.envConfig.apiUrl);
|
|
550
|
-
return { ...ctx, accessToken: token, apiClient, realmWorkspace: workspaceUuid };
|
|
551
|
-
}
|
|
552
463
|
const apiKey = process.env.GRAVITY_RAIL_API_KEY;
|
|
553
464
|
if (apiKey) {
|
|
554
465
|
const apiClient = makeApiClient(apiKey, ctx.envConfig.apiUrl);
|
|
555
466
|
return { ...ctx, accessToken: apiKey, apiClient };
|
|
556
467
|
}
|
|
557
|
-
const creds = ctx.credentialsManager.load(ctx.env);
|
|
468
|
+
const creds = ctx.credentialsManager.load(ctx.env, ctx.profile);
|
|
558
469
|
if (creds?.accessToken) {
|
|
559
470
|
const buffer = 5 * 60 * 1e3;
|
|
560
471
|
const notExpired = !creds.expiresAt || new Date(creds.expiresAt) > new Date(Date.now() + buffer);
|
|
561
472
|
if (notExpired) {
|
|
562
473
|
const apiClient = makeApiClient(creds.accessToken, ctx.envConfig.apiUrl);
|
|
563
|
-
return {
|
|
474
|
+
return {
|
|
475
|
+
...ctx,
|
|
476
|
+
accessToken: creds.accessToken,
|
|
477
|
+
apiClient,
|
|
478
|
+
profileWorkspace: creds.workspaceUuid
|
|
479
|
+
};
|
|
564
480
|
}
|
|
565
481
|
const refreshed = await tryTokenRefresh(ctx, creds);
|
|
566
482
|
if (refreshed) return refreshed;
|
|
567
483
|
}
|
|
568
|
-
|
|
484
|
+
if (ctx.profile !== "default") {
|
|
485
|
+
throw new AuthRequiredError(
|
|
486
|
+
`No credentials for profile "${ctx.profile}" (${ctx.env}). Run: gr auth login --name ${ctx.profile} or gr auth import-dev`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
throw new AuthRequiredError(
|
|
490
|
+
"Not authenticated. Run `gr login`, `gr auth import-dev` (local dev), or set GRAVITY_RAIL_API_KEY."
|
|
491
|
+
);
|
|
569
492
|
}
|
|
570
493
|
function requireAuth(ctx) {
|
|
571
494
|
if (!("apiClient" in ctx)) {
|
|
@@ -580,13 +503,15 @@ function createPublicContext(opts) {
|
|
|
580
503
|
frontendUrl: opts.frontendUrl || process.env.GRAVITY_RAIL_FRONTEND_URL || "https://app.gravityrail.com"
|
|
581
504
|
} : getEnvironmentConfig(env);
|
|
582
505
|
const credentialsManager = new CredentialsManager(homedir(), envConfig.apiUrl);
|
|
506
|
+
const profile = credentialsManager.resolveProfileName(opts.profile, env);
|
|
583
507
|
return {
|
|
584
508
|
env,
|
|
585
509
|
envConfig,
|
|
586
510
|
outputFormat: detectOutputFormat(opts.outputFormat),
|
|
587
511
|
verbose: opts.verbose ?? false,
|
|
588
512
|
allowWrite: opts.allowWrite ?? false,
|
|
589
|
-
credentialsManager
|
|
513
|
+
credentialsManager,
|
|
514
|
+
profile
|
|
590
515
|
};
|
|
591
516
|
}
|
|
592
517
|
async function createPublicAuthenticatedContext(opts) {
|
|
@@ -596,13 +521,18 @@ async function createPublicAuthenticatedContext(opts) {
|
|
|
596
521
|
const apiClient = makeApiClient(apiKey, ctx.envConfig.apiUrl);
|
|
597
522
|
return { ...ctx, accessToken: apiKey, apiClient };
|
|
598
523
|
}
|
|
599
|
-
const creds = ctx.credentialsManager.load(ctx.env);
|
|
524
|
+
const creds = ctx.credentialsManager.load(ctx.env, ctx.profile);
|
|
600
525
|
if (creds?.accessToken) {
|
|
601
526
|
const buffer = 5 * 60 * 1e3;
|
|
602
527
|
const notExpired = !creds.expiresAt || new Date(creds.expiresAt) > new Date(Date.now() + buffer);
|
|
603
528
|
if (notExpired) {
|
|
604
529
|
const apiClient = makeApiClient(creds.accessToken, ctx.envConfig.apiUrl);
|
|
605
|
-
return {
|
|
530
|
+
return {
|
|
531
|
+
...ctx,
|
|
532
|
+
accessToken: creds.accessToken,
|
|
533
|
+
apiClient,
|
|
534
|
+
profileWorkspace: creds.workspaceUuid
|
|
535
|
+
};
|
|
606
536
|
}
|
|
607
537
|
const refreshed = await tryTokenRefresh(ctx, creds);
|
|
608
538
|
if (refreshed) return refreshed;
|
|
@@ -624,11 +554,11 @@ function toCamelChat(raw) {
|
|
|
624
554
|
return {
|
|
625
555
|
id: raw.id,
|
|
626
556
|
uuid: raw.uuid,
|
|
627
|
-
chatType: raw.
|
|
557
|
+
chatType: raw.chatType,
|
|
628
558
|
title: raw.title,
|
|
629
|
-
threadId: raw.
|
|
630
|
-
createdAt: raw.
|
|
631
|
-
updatedAt: raw.
|
|
559
|
+
threadId: raw.threadId,
|
|
560
|
+
createdAt: raw.createdAt,
|
|
561
|
+
updatedAt: raw.updatedAt
|
|
632
562
|
};
|
|
633
563
|
}
|
|
634
564
|
function toCamelMessage(raw) {
|
|
@@ -636,9 +566,10 @@ function toCamelMessage(raw) {
|
|
|
636
566
|
id: raw.id,
|
|
637
567
|
role: raw.role,
|
|
638
568
|
content: raw.content ?? "",
|
|
639
|
-
createdAt: raw.createdAt ??
|
|
640
|
-
|
|
641
|
-
|
|
569
|
+
createdAt: raw.createdAt ?? null,
|
|
570
|
+
// Protocol-exempt snake_case on wire (see ConciergeMessageResponse)
|
|
571
|
+
toolCalls: raw.tool_calls ?? null,
|
|
572
|
+
toolCallId: raw.tool_call_id ?? null
|
|
642
573
|
};
|
|
643
574
|
}
|
|
644
575
|
async function listConciergeChats(ctx) {
|
|
@@ -744,6 +675,7 @@ export {
|
|
|
744
675
|
outputSingle,
|
|
745
676
|
COL,
|
|
746
677
|
stderr,
|
|
678
|
+
createSandboxFetchFromEnv,
|
|
747
679
|
createContext,
|
|
748
680
|
createAuthenticatedContext,
|
|
749
681
|
requireAuth,
|
|
@@ -758,4 +690,4 @@ export {
|
|
|
758
690
|
confirmAccountPhoneVerify,
|
|
759
691
|
getConciergeChatMessages
|
|
760
692
|
};
|
|
761
|
-
//# sourceMappingURL=chunk-
|
|
693
|
+
//# sourceMappingURL=chunk-GRLUFHMI.js.map
|