@gravity-rail/cli 0.10.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 CHANGED
@@ -26,7 +26,58 @@ gr whoami # verify your identity
26
26
  gr logout # clear cached credentials
27
27
  ```
28
28
 
29
- Credentials are cached locally per environment.
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 | Description |
118
- |---|---|
119
- | `--api-url <url>` | Override API base URL |
120
- | `-w, --workspace <uuid>` | Workspace UUID (required for most commands) |
121
- | `-o, --output-format <fmt>` | Output format: `json`, `jsonl`, `table` |
122
- | `-v, --verbose` | Verbose output to stderr |
123
- | `--allow-write` | Required for create/update/delete operations |
124
- | `--id <id>` | Entity ID (for get/update/delete) |
125
- | `-d, --data <json>` | Inline JSON payload |
126
- | `-f, --file <path>` | Read JSON payload from file |
127
- | `--help` | Show help |
128
- | `--version` | Show version |
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 | Description |
133
- |---|---|
134
- | `GRAVITY_RAIL_API_KEY` | API key for non-interactive authentication |
135
- | `GRAVITY_RAIL_API_URL` | Override API base URL (default: `https://api.gravityrail.com`) |
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` | 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 |
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
- "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": "…" }
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,76 +219,76 @@ not forwarded, as it may contain sensitive data. Use `resume.chatUuid` /
166
219
 
167
220
  ### Core
168
221
 
169
- | Domain | Description |
170
- |---|---|
171
- | `tasks` | Workflow tasks — CRUD, archive, clone |
172
- | `chats` | Conversations — CRUD, labels, filters, messages, export, summaries |
173
- | `members` | Contacts and team — CRUD, labels, filters, roles, fields, import/export |
174
- | `workspaces` | Workspace management — CRUD, features, themes, invitations, export/import |
175
- | `workflows` | Conversational workflows — CRUD, notes, contributors, templates, analytics |
176
- | `agents` | Autonomous AI members — CRUD, archive |
177
- | `assistants` | AI personas — CRUD with model and voice configuration |
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 | Description |
183
- |---|---|
184
- | `data-types` | Schema-driven forms and records — CRUD, field indexing, computed fields |
185
- | `files` | Documents and folders — CRUD, sharing, semantic search, labels |
186
- | `sites` | Customer portals — CRUD, pages, menus, web crawling |
187
- | `events` | Automation rules — triggers, CEL conditions, delayed actions, webhooks |
188
- | `calendars` | Scheduling — calendars, events, event types, availability, Google sync |
189
- | `qualifications` | Skills evaluation — requirements, submissions, scoring |
190
- | `queries` | Unified Query Engine — fields, validate, preview, run, syntax reference |
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 |
191
244
 
192
245
  ### Global (no workspace)
193
246
 
194
- | Domain | Description |
195
- |---|---|
196
- | `concierge` | Global Concierge — interactive chat, PSTN `--call`, SMS `--sms` |
197
- | `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) |
198
251
 
199
252
  ### Communication
200
253
 
201
- | Domain | Description |
202
- |---|---|
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 |
205
- | `inboxes` | Email inboxes — threads, attachments, routing |
206
- | `operator-groups` | Live operator routing — groups, presence, strategies |
207
- | `notification-rules` | Alert configuration — event-based notifications |
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 |
208
261
 
209
262
  ### Integrations
210
263
 
211
- | Domain | Description |
212
- |---|---|
213
- | `discord-bots` | Discord bot management — slash commands, threads, account linking |
214
- | `slack-apps` | Slack app management — installations, threads, accounts |
215
- | `fhir-connections` | FHIR healthcare connections — patients, practitioners, encounters |
216
- | `patientsync` | PatientSync PSTN-transfer connection — `configure` / `status` / `delete` (singleton per workspace) |
217
- | `app-connections` | Platform app integrations |
218
- | `monday` | Monday.com — boards, columns, webhooks |
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 |
219
272
 
220
273
  ### Platform
221
274
 
222
- | Domain | Description |
223
- |---|---|
224
- | `api-keys` | API key management — workspace and global scopes |
225
- | `subscriptions` | Subscription management |
226
- | `reports` | Usage reports — AI tokens, voice minutes, SMS, storage |
227
- | `ai-models` | Available AI models (no workspace required) |
228
- | `features` | Feature registry (no workspace required) |
229
- | `custom-toolkits` | Custom tool definitions |
230
- | `mcp-servers` | MCP server connections |
231
- | `milestones` | Goal tracking |
232
- | `supervisors` | AI supervisor management |
233
- | `access-grants` | Temporary access delegation |
234
- | `pronunciations` | Voice pronunciation overrides |
235
- | `apps` | OAuth app management |
236
- | `oauth` | OAuth provider connections |
237
- | `auth` | Two-factor authentication, credentials, and password management |
238
- | `org` | Organization membership, invitations, and org domains |
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 |
239
292
 
240
293
  ## Recipes
241
294
 
@@ -2,8 +2,8 @@ import {
2
2
  confirmAccountPhoneVerify,
3
3
  requireAuth,
4
4
  startAccountPhoneVerify
5
- } from "./chunk-XJ2EBXJZ.js";
6
- import "./chunk-GVUGFPCN.js";
5
+ } from "./chunk-GRLUFHMI.js";
6
+ import "./chunk-SIV2WU2I.js";
7
7
  import "./chunk-WZVMWN3S.js";
8
8
 
9
9
  // src/commands/account-verify-phone.ts
@@ -38,4 +38,4 @@ async function promptLine(question) {
38
38
  export {
39
39
  accountVerifyPhoneCommand
40
40
  };
41
- //# sourceMappingURL=account-verify-phone-XNXA7HNG.js.map
41
+ //# sourceMappingURL=account-verify-phone-7YQXKU3X.js.map
@@ -3,7 +3,7 @@ import {
3
3
  getEnvironmentConfig,
4
4
  printTable,
5
5
  refreshAccessToken
6
- } from "./chunk-GVUGFPCN.js";
6
+ } from "./chunk-SIV2WU2I.js";
7
7
  import {
8
8
  AuthRequiredError,
9
9
  CLIError
@@ -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.created_at ?? ""
211
+ getValue: (item) => item.createdAt ?? ""
212
212
  }),
213
213
  updated: () => ({
214
214
  header: "UPDATED",
215
- getValue: (item) => item.updated_at ?? ""
215
+ getValue: (item) => item.updatedAt ?? ""
216
216
  }),
217
217
  description: () => ({
218
218
  header: "DESCRIPTION",
@@ -231,93 +231,6 @@ 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;
@@ -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 { ...ctx, accessToken: refreshed.accessToken, apiClient };
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 { ...ctx, accessToken: creds.accessToken, apiClient };
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
- throw new AuthRequiredError();
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 { ...ctx, accessToken: creds.accessToken, apiClient };
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.chat_type,
557
+ chatType: raw.chatType,
628
558
  title: raw.title,
629
- threadId: raw.thread_id,
630
- createdAt: raw.created_at,
631
- updatedAt: raw.updated_at
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 ?? raw.created_at ?? null,
640
- toolCalls: raw.toolCalls ?? raw.tool_calls ?? null,
641
- toolCallId: raw.toolCallId ?? raw.tool_call_id ?? null
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) {
@@ -759,4 +690,4 @@ export {
759
690
  confirmAccountPhoneVerify,
760
691
  getConciergeChatMessages
761
692
  };
762
- //# sourceMappingURL=chunk-XJ2EBXJZ.js.map
693
+ //# sourceMappingURL=chunk-GRLUFHMI.js.map