@gravity-rail/cli 0.10.0 → 0.12.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 +137 -83
- package/dist/{account-verify-phone-XNXA7HNG.js → account-verify-phone-7YQXKU3X.js} +3 -3
- package/dist/{chunk-XJ2EBXJZ.js → chunk-GRLUFHMI.js} +48 -117
- package/dist/chunk-GRLUFHMI.js.map +1 -0
- package/dist/{chunk-GVUGFPCN.js → chunk-SIV2WU2I.js} +284 -45
- package/dist/chunk-SIV2WU2I.js.map +1 -0
- package/dist/cli-client-context-C5l8lg7h.d.ts +130 -0
- package/dist/cli-utils.d.ts +60 -3
- package/dist/cli-utils.js +3 -1
- package/dist/gr.js +5794 -1959
- package/dist/gr.js.map +1 -1
- package/dist/lib.d.ts +65 -9
- package/dist/lib.js +1405 -644
- package/dist/lib.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-GVUGFPCN.js.map +0 -1
- package/dist/chunk-XJ2EBXJZ.js.map +0 -1
- package/dist/cli-client-context-BbyBEGcg.d.ts +0 -99
- /package/dist/{account-verify-phone-XNXA7HNG.js.map → account-verify-phone-7YQXKU3X.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-utils/config.ts","../src/cli-utils/auth.ts","../src/cli-utils/credentials-manager.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 // Prefer api-nginx HTTPS (container 443, or legacy 5054) — never the raw\n // FastAPI container 5050 (plain HTTP; yarn gr uses https://api.gravityrail.test).\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 === 443 || container === 5054) 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 + named profile)\n * - Dynamic client registration\n * - Token refresh\n * - Browser-based authorization\n *\n * Credential persistence lives in `./credentials-manager` (multi-account profiles).\n */\n\nimport { createServer } from 'http';\nimport { createHash, randomBytes } from 'crypto';\nimport { exec } from 'child_process';\nimport { EnvironmentConfig, CALLBACK_PORT, CALLBACK_PATH, REDIRECT_URI } from './config';\nimport { CredentialsManager, type CLICredentials } from './credentials-manager';\n\nexport {\n CredentialsManager,\n DEFAULT_PROFILE,\n type CLICredentials,\n type ProfileSummary,\n} from './credentials-manager';\n\nexport interface AuthResult {\n accessToken: string;\n credentials?: CLICredentials;\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 /** Named account profile to store tokens under (default: resolved current). */\n profile?: 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', profile } = options;\n const profileName = credentialsManager.resolveProfileName(profile, env);\n let hasRetried = false;\n\n while (true) {\n // Reuse registered client credentials if available, otherwise register\n let creds = credentialsManager.load(env, profileName);\n if (!creds || !creds.clientId || !creds.clientSecret) {\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, profileName);\n } else {\n stderr(' Using cached client credentials');\n }\n\n // Token-only (import-dev) profiles cannot start OAuth; re-register if needed.\n if (!creds.clientId || !creds.clientSecret) {\n throw new Error('OAuth client credentials missing after registration');\n }\n const clientId = creds.clientId;\n const clientSecret = creds.clientSecret;\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: 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(` Profile: ${profileName}`);\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, profileName);\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(`${clientId}:${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, profileName);\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, profileName);\n credentialsManager.setCurrentProfile(env, profileName);\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 * Clears only the named profile on failure (other accounts stay intact).\n */\nexport async function refreshAccessToken(\n envConfig: EnvironmentConfig,\n creds: CLICredentials,\n credentialsManager: CredentialsManager,\n env: string,\n profile?: 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 profileName = credentialsManager.resolveProfileName(profile, env);\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, profileName);\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, profileName);\n return updated;\n } catch (err) {\n stderr(` Token refresh error: ${err instanceof Error ? err.message : 'unknown'}`);\n credentialsManager.clear(env, profileName);\n return null;\n }\n}\n","/**\n * CLI credential store with multi-account profile support.\n *\n * Store location: `~/.gr-cli/credentials-<env>.json` (mode 0600; dir mode 0700).\n * Format is versioned JSON with named profiles (aws-style). Flat single-credential\n * files from older CLI versions migrate to profile \"default\" on first read/write.\n *\n * Security notes (for operators / security review):\n * - Tokens and client secrets live only on the local filesystem under the user's home.\n * - Files are written mode 0600; the cache directory is created mode 0700.\n * - Profile and environment names are allowlist-validated with a\n * resolve()/relative() containment check so path traversal cannot escape `.gr-cli`.\n * - Callers must never log credential field values (accessToken, refreshToken, secrets).\n * - `GRAVITY_RAIL_API_KEY` is not persisted here; it remains process-env only and\n * overrides stored profiles for non-interactive automation.\n * - Dev profiles imported from `dev-realms.json` store bearer tokens only (no OAuth\n * client secret). They are local-dev conveniences and must never be committed.\n */\n\nimport {\n readFileSync,\n writeFileSync,\n existsSync,\n mkdirSync,\n chmodSync,\n unlinkSync,\n renameSync,\n} from 'fs';\nimport { join, resolve, relative, isAbsolute } from 'path';\n\nexport const DEFAULT_PROFILE = 'default';\n\n/**\n * Stored credentials for one named profile.\n *\n * OAuth logins have clientId/clientSecret (+ optional refresh).\n * Dev profiles imported from dev-realms.json are token-only (accessToken +\n * optional workspaceUuid) — no OAuth client registration.\n */\nexport interface CLICredentials {\n clientId?: string;\n clientSecret?: string;\n accessToken?: string;\n refreshToken?: string;\n expiresAt?: string;\n /** Default workspace UUID (workspace/member profiles from dev-realms). */\n workspaceUuid?: string;\n}\n\nexport interface ProfileSummary {\n name: string;\n current: boolean;\n hasAccessToken: boolean;\n /** ISO timestamp if known; never includes the token itself. */\n expiresAt?: string;\n /** Present when the profile pins a default workspace. */\n workspaceUuid?: string;\n}\n\ninterface CredentialStoreV2 {\n version: 2;\n currentProfile: string;\n profiles: Record<string, CLICredentials>;\n}\n\n/**\n * Manages CLI credentials storage (one file per environment, multi-profile).\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 /**\n * Validate a profile name. Allows the legacy realm path form\n * `workspace/member` so `acme-demo/acme-admin` works as a profile name.\n * Rejects path traversal and absolute paths.\n */\n validateProfileName(profile: string): void {\n if (!profile) {\n throw new Error('Invalid profile name: (empty).');\n }\n if (profile.includes('..') || profile.startsWith('/') || profile.endsWith('/')) {\n throw new Error(\n `Invalid profile name: ${profile}. Do not use \"..\" or leading/trailing \"/\".`\n );\n }\n // Letters, digits, '_', '-', '.', and a single-level '/' for workspace/member.\n if (!/^[a-zA-Z0-9_.-]+(?:\\/[a-zA-Z0-9_.-]+)?$/.test(profile)) {\n throw new Error(\n `Invalid profile name: ${profile}. Use letters, digits, '_', '-', '.', ` +\n `or workspace/member form (one slash).`\n );\n }\n }\n\n /**\n * Resolve which profile to use for this invocation.\n * Precedence: explicit flag → GRAVITY_RAIL_PROFILE → stored current → \"default\".\n */\n resolveProfileName(explicit?: string | null, env?: string): string {\n if (explicit) {\n this.validateProfileName(explicit);\n return explicit;\n }\n const fromEnv = process.env.GRAVITY_RAIL_PROFILE;\n if (fromEnv) {\n this.validateProfileName(fromEnv);\n return fromEnv;\n }\n if (env) {\n return this.getCurrentProfile(env);\n }\n return DEFAULT_PROFILE;\n }\n\n getCurrentProfile(env: string): string {\n const parsed = this.readStore(env);\n if (!parsed) return DEFAULT_PROFILE;\n return parsed.store.currentProfile || DEFAULT_PROFILE;\n }\n\n setCurrentProfile(env: string, profile: string): void {\n this.validateProfileName(profile);\n const store = this.readStore(env)?.store ?? this.emptyStore(profile);\n if (!store.profiles[profile]) {\n throw new Error(\n `Profile \"${profile}\" has no stored credentials for ${env}. ` +\n `Run: gr auth login --name ${profile} (or gr auth import-dev for local tokens)`\n );\n }\n store.currentProfile = profile;\n this.writeStore(env, store);\n }\n\n listProfiles(env: string): ProfileSummary[] {\n const parsed = this.readStore(env);\n if (!parsed) return [];\n const store = parsed.store;\n const current = store.currentProfile || DEFAULT_PROFILE;\n return Object.keys(store.profiles)\n .sort()\n .map((name) => {\n const creds = store.profiles[name]!;\n return {\n name,\n current: name === current,\n hasAccessToken: Boolean(creds.accessToken || creds.refreshToken),\n expiresAt: creds.expiresAt,\n workspaceUuid: creds.workspaceUuid,\n };\n });\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 // Isolate credential files by non-default API port so worktrees (shifted\n // ports + separate DBs) do not clobber each other's imported tokens.\n if (this.apiUrl) {\n try {\n const url = new URL(this.apiUrl);\n const port = url.port;\n if (port && port !== '80' && port !== '443') {\n const isCanonicalLocalDev =\n (url.hostname === 'localhost' && port === '5050') ||\n (url.hostname.endsWith('gravityrail.test') && port === '5054');\n if (!isCanonicalLocalDev) {\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}-${port}.json`);\n }\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 private emptyStore(currentProfile = DEFAULT_PROFILE): CredentialStoreV2 {\n return { version: 2, currentProfile, profiles: {} };\n }\n\n private isCredentialShape(value: unknown): value is CLICredentials {\n if (!value || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n // Local JSON shape check only — typeof / field presence, not secret comparison.\n // FP (CWE-208): no secret values are compared; short-circuit cannot leak content.\n // bearer:disable javascript_lang_observable_timing\n const hasOAuth = typeof v.clientId === 'string' && typeof v.clientSecret === 'string';\n // bearer:disable javascript_lang_observable_timing\n const hasToken = typeof v.accessToken === 'string' && v.accessToken.length > 0;\n return hasOAuth || hasToken;\n }\n\n /**\n * Read and normalize the on-disk store (migrating legacy formats in memory).\n * `wasLegacy` is true when the file was not already v2 so callers can rewrite once.\n */\n private readStore(env: string): { store: CredentialStoreV2; wasLegacy: boolean } | 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')) as Record<string, unknown>;\n\n // v2 multi-profile\n if (raw.version === 2 && raw.profiles && typeof raw.profiles === 'object') {\n const profiles: Record<string, CLICredentials> = {};\n for (const [name, value] of Object.entries(raw.profiles as Record<string, unknown>)) {\n if (this.isCredentialShape(value)) {\n profiles[name] = value;\n }\n }\n const current =\n typeof raw.currentProfile === 'string' && profiles[raw.currentProfile]\n ? raw.currentProfile\n : Object.keys(profiles)[0] || DEFAULT_PROFILE;\n return {\n store: { version: 2, currentProfile: current, profiles },\n wasLegacy: false,\n };\n }\n\n // Flat single-credential format (pre-multi-account)\n if (this.isCredentialShape(raw)) {\n return {\n store: {\n version: 2,\n currentProfile: DEFAULT_PROFILE,\n profiles: { [DEFAULT_PROFILE]: raw },\n },\n wasLegacy: true,\n };\n }\n\n // Old org-namespaced format { default: {...} }\n const legacyDefault = (raw as { default?: unknown }).default;\n if (this.isCredentialShape(legacyDefault)) {\n return {\n store: {\n version: 2,\n currentProfile: DEFAULT_PROFILE,\n profiles: { [DEFAULT_PROFILE]: legacyDefault },\n },\n wasLegacy: true,\n };\n }\n\n return null;\n } catch {\n return null;\n }\n }\n\n private ensureCacheDir(): void {\n if (!existsSync(this.cacheDir)) {\n mkdirSync(this.cacheDir, { recursive: true, mode: 0o700 });\n try {\n chmodSync(this.cacheDir, 0o700);\n } catch {\n // Ignore permission errors on Windows\n }\n }\n }\n\n private writeStore(env: string, store: CredentialStoreV2): void {\n this.ensureCacheDir();\n const credsPath = this.getCredentialsPath(env);\n // Atomic replace: write a sibling temp file, chmod, then rename into place.\n // POSIX rename is atomic, so a crash mid-write cannot leave a partial JSON\n // that would wipe every profile on the next parse failure.\n const tmpPath = `${credsPath}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(store, null, 2) + '\\n', { mode: 0o600 });\n try {\n chmodSync(tmpPath, 0o600);\n } catch {\n // Ignore permission errors on Windows\n }\n renameSync(tmpPath, credsPath);\n try {\n chmodSync(credsPath, 0o600);\n } catch {\n // Ignore permission errors on Windows\n }\n }\n\n private filterExpired(creds: CLICredentials): CLICredentials | null {\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 return creds;\n }\n\n /**\n * Load credentials for an environment + profile.\n * When `profile` is omitted, uses the resolved current profile for `env`.\n */\n load(env: string, profile?: string): CLICredentials | null {\n const parsed = this.readStore(env);\n if (!parsed) return null;\n\n const { store, wasLegacy } = parsed;\n const name = profile ?? store.currentProfile ?? DEFAULT_PROFILE;\n if (profile) this.validateProfileName(profile);\n const creds = store.profiles[name];\n if (!creds) return null;\n\n // Persist migration from flat/org formats so subsequent multi-profile ops work.\n // wasLegacy comes from the single parse in readStore — no second file read.\n if (wasLegacy) {\n this.writeStore(env, store);\n }\n\n return this.filterExpired(creds);\n }\n\n /**\n * Save credentials for an environment + profile.\n * When `profile` is omitted, uses the current profile (or \"default\").\n * Sets currentProfile to the saved profile when the store was empty.\n */\n save(env: string, creds: CLICredentials, profile?: string): void {\n if (profile) this.validateProfileName(profile);\n const store = this.readStore(env)?.store ?? this.emptyStore();\n const name = profile ?? store.currentProfile ?? DEFAULT_PROFILE;\n if (profile) {\n // Explicit profile on save also becomes current when first created, or when\n // the caller is logging into a named account.\n store.profiles[name] = creds;\n if (!store.profiles[store.currentProfile] || Object.keys(store.profiles).length === 1) {\n store.currentProfile = name;\n }\n } else {\n store.profiles[name] = creds;\n if (!store.currentProfile) {\n store.currentProfile = name;\n }\n }\n this.writeStore(env, store);\n }\n\n /**\n * Bulk-save many profiles in one write (used by dev-realms import).\n * Does not change currentProfile unless the store was empty — then uses\n * `preferCurrent` if provided and present, else `superadmin`, else first.\n */\n saveMany(env: string, profiles: Record<string, CLICredentials>, preferCurrent?: string): number {\n const names = Object.keys(profiles);\n for (const name of names) {\n this.validateProfileName(name);\n }\n const store = this.readStore(env)?.store ?? this.emptyStore();\n const wasEmpty = Object.keys(store.profiles).length === 0;\n for (const [name, creds] of Object.entries(profiles)) {\n store.profiles[name] = creds;\n }\n if (wasEmpty) {\n if (preferCurrent && store.profiles[preferCurrent]) {\n store.currentProfile = preferCurrent;\n } else if (store.profiles.superadmin) {\n store.currentProfile = 'superadmin';\n } else {\n store.currentProfile = names.sort()[0] ?? DEFAULT_PROFILE;\n }\n }\n this.writeStore(env, store);\n return names.length;\n }\n\n /**\n * Clear one profile (default: current profile). Deletes the file when no profiles remain.\n * Pass a profile name to clear a specific account without touching others.\n */\n clear(env: string, profile?: string): void {\n const parsed = this.readStore(env);\n if (!parsed) return;\n const store = parsed.store;\n\n const name = profile ?? store.currentProfile ?? DEFAULT_PROFILE;\n if (profile) this.validateProfileName(profile);\n delete store.profiles[name];\n\n if (Object.keys(store.profiles).length === 0) {\n const credsPath = this.getCredentialsPath(env);\n if (existsSync(credsPath)) {\n try {\n unlinkSync(credsPath);\n } catch {\n // Ignore\n }\n }\n return;\n }\n\n if (store.currentProfile === name) {\n store.currentProfile = Object.keys(store.profiles).sort()[0]!;\n }\n this.writeStore(env, store);\n }\n\n getCacheDir(): string {\n return this.cacheDir;\n }\n\n /** Absolute path of the credentials file for `env` (for docs / `auth list`). */\n getCredentialsFilePath(env: string): string {\n return this.getCredentialsPath(env);\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","resolve","resolve"]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI configuration for remote environments
|
|
3
|
+
*/
|
|
4
|
+
interface EnvironmentConfig {
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
frontendUrl: string;
|
|
7
|
+
}
|
|
8
|
+
declare const ENVIRONMENTS: Record<string, EnvironmentConfig>;
|
|
9
|
+
declare const CALLBACK_PORT = 31337;
|
|
10
|
+
declare const CALLBACK_PATH = "/oauth/callback";
|
|
11
|
+
declare const REDIRECT_URI = "http://localhost:31337/oauth/callback";
|
|
12
|
+
declare function getEnvironmentConfig(env: string): EnvironmentConfig;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* CLI credential store with multi-account profile support.
|
|
16
|
+
*
|
|
17
|
+
* Store location: `~/.gr-cli/credentials-<env>.json` (mode 0600; dir mode 0700).
|
|
18
|
+
* Format is versioned JSON with named profiles (aws-style). Flat single-credential
|
|
19
|
+
* files from older CLI versions migrate to profile "default" on first read/write.
|
|
20
|
+
*
|
|
21
|
+
* Security notes (for operators / security review):
|
|
22
|
+
* - Tokens and client secrets live only on the local filesystem under the user's home.
|
|
23
|
+
* - Files are written mode 0600; the cache directory is created mode 0700.
|
|
24
|
+
* - Profile and environment names are allowlist-validated with a
|
|
25
|
+
* resolve()/relative() containment check so path traversal cannot escape `.gr-cli`.
|
|
26
|
+
* - Callers must never log credential field values (accessToken, refreshToken, secrets).
|
|
27
|
+
* - `GRAVITY_RAIL_API_KEY` is not persisted here; it remains process-env only and
|
|
28
|
+
* overrides stored profiles for non-interactive automation.
|
|
29
|
+
* - Dev profiles imported from `dev-realms.json` store bearer tokens only (no OAuth
|
|
30
|
+
* client secret). They are local-dev conveniences and must never be committed.
|
|
31
|
+
*/
|
|
32
|
+
declare const DEFAULT_PROFILE = "default";
|
|
33
|
+
/**
|
|
34
|
+
* Stored credentials for one named profile.
|
|
35
|
+
*
|
|
36
|
+
* OAuth logins have clientId/clientSecret (+ optional refresh).
|
|
37
|
+
* Dev profiles imported from dev-realms.json are token-only (accessToken +
|
|
38
|
+
* optional workspaceUuid) — no OAuth client registration.
|
|
39
|
+
*/
|
|
40
|
+
interface CLICredentials {
|
|
41
|
+
clientId?: string;
|
|
42
|
+
clientSecret?: string;
|
|
43
|
+
accessToken?: string;
|
|
44
|
+
refreshToken?: string;
|
|
45
|
+
expiresAt?: string;
|
|
46
|
+
/** Default workspace UUID (workspace/member profiles from dev-realms). */
|
|
47
|
+
workspaceUuid?: string;
|
|
48
|
+
}
|
|
49
|
+
interface ProfileSummary {
|
|
50
|
+
name: string;
|
|
51
|
+
current: boolean;
|
|
52
|
+
hasAccessToken: boolean;
|
|
53
|
+
/** ISO timestamp if known; never includes the token itself. */
|
|
54
|
+
expiresAt?: string;
|
|
55
|
+
/** Present when the profile pins a default workspace. */
|
|
56
|
+
workspaceUuid?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Manages CLI credentials storage (one file per environment, multi-profile).
|
|
60
|
+
*/
|
|
61
|
+
declare class CredentialsManager {
|
|
62
|
+
private cacheDir;
|
|
63
|
+
private apiUrl?;
|
|
64
|
+
constructor(rootDir: string, apiUrl?: string);
|
|
65
|
+
/**
|
|
66
|
+
* Validate a profile name. Allows the legacy realm path form
|
|
67
|
+
* `workspace/member` so `acme-demo/acme-admin` works as a profile name.
|
|
68
|
+
* Rejects path traversal and absolute paths.
|
|
69
|
+
*/
|
|
70
|
+
validateProfileName(profile: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Resolve which profile to use for this invocation.
|
|
73
|
+
* Precedence: explicit flag → GRAVITY_RAIL_PROFILE → stored current → "default".
|
|
74
|
+
*/
|
|
75
|
+
resolveProfileName(explicit?: string | null, env?: string): string;
|
|
76
|
+
getCurrentProfile(env: string): string;
|
|
77
|
+
setCurrentProfile(env: string, profile: string): void;
|
|
78
|
+
listProfiles(env: string): ProfileSummary[];
|
|
79
|
+
private getCredentialsPath;
|
|
80
|
+
private buildCredentialsPath;
|
|
81
|
+
private emptyStore;
|
|
82
|
+
private isCredentialShape;
|
|
83
|
+
/**
|
|
84
|
+
* Read and normalize the on-disk store (migrating legacy formats in memory).
|
|
85
|
+
* `wasLegacy` is true when the file was not already v2 so callers can rewrite once.
|
|
86
|
+
*/
|
|
87
|
+
private readStore;
|
|
88
|
+
private ensureCacheDir;
|
|
89
|
+
private writeStore;
|
|
90
|
+
private filterExpired;
|
|
91
|
+
/**
|
|
92
|
+
* Load credentials for an environment + profile.
|
|
93
|
+
* When `profile` is omitted, uses the resolved current profile for `env`.
|
|
94
|
+
*/
|
|
95
|
+
load(env: string, profile?: string): CLICredentials | null;
|
|
96
|
+
/**
|
|
97
|
+
* Save credentials for an environment + profile.
|
|
98
|
+
* When `profile` is omitted, uses the current profile (or "default").
|
|
99
|
+
* Sets currentProfile to the saved profile when the store was empty.
|
|
100
|
+
*/
|
|
101
|
+
save(env: string, creds: CLICredentials, profile?: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Bulk-save many profiles in one write (used by dev-realms import).
|
|
104
|
+
* Does not change currentProfile unless the store was empty — then uses
|
|
105
|
+
* `preferCurrent` if provided and present, else `superadmin`, else first.
|
|
106
|
+
*/
|
|
107
|
+
saveMany(env: string, profiles: Record<string, CLICredentials>, preferCurrent?: string): number;
|
|
108
|
+
/**
|
|
109
|
+
* Clear one profile (default: current profile). Deletes the file when no profiles remain.
|
|
110
|
+
* Pass a profile name to clear a specific account without touching others.
|
|
111
|
+
*/
|
|
112
|
+
clear(env: string, profile?: string): void;
|
|
113
|
+
getCacheDir(): string;
|
|
114
|
+
/** Absolute path of the credentials file for `env` (for docs / `auth list`). */
|
|
115
|
+
getCredentialsFilePath(env: string): string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface ConciergeAppContextPayload {
|
|
119
|
+
clientSurface: 'cli';
|
|
120
|
+
cliContext: {
|
|
121
|
+
shell?: string;
|
|
122
|
+
os: string;
|
|
123
|
+
cwdBasename: string;
|
|
124
|
+
};
|
|
125
|
+
channel: 'text';
|
|
126
|
+
}
|
|
127
|
+
/** Build ConciergeAppContext for AG-UI requests from the CLI. */
|
|
128
|
+
declare function buildCliConciergeAppContext(): ConciergeAppContextPayload;
|
|
129
|
+
|
|
130
|
+
export { CredentialsManager as C, DEFAULT_PROFILE as D, type EnvironmentConfig as E, type ProfileSummary as P, REDIRECT_URI as R, type CLICredentials as a, type ConciergeAppContextPayload as b, buildCliConciergeAppContext as c, CALLBACK_PATH as d, CALLBACK_PORT as e, ENVIRONMENTS as f, getEnvironmentConfig as g };
|
package/dist/cli-utils.d.ts
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
|
-
import { a as ConciergeAppContextPayload } from './cli-client-context-
|
|
2
|
-
export {
|
|
1
|
+
import { a as CLICredentials, E as EnvironmentConfig, C as CredentialsManager, b as ConciergeAppContextPayload } from './cli-client-context-C5l8lg7h.js';
|
|
2
|
+
export { d as CALLBACK_PATH, e as CALLBACK_PORT, D as DEFAULT_PROFILE, f as ENVIRONMENTS, P as ProfileSummary, R as REDIRECT_URI, g as getEnvironmentConfig } from './cli-client-context-C5l8lg7h.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CLI OAuth2 authentication utilities
|
|
6
|
+
*
|
|
7
|
+
* Provides OAuth2 with PKCE flow for CLI tools, including:
|
|
8
|
+
* - Credential caching (per environment + named profile)
|
|
9
|
+
* - Dynamic client registration
|
|
10
|
+
* - Token refresh
|
|
11
|
+
* - Browser-based authorization
|
|
12
|
+
*
|
|
13
|
+
* Credential persistence lives in `./credentials-manager` (multi-account profiles).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface AuthResult {
|
|
17
|
+
accessToken: string;
|
|
18
|
+
credentials?: CLICredentials;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generate PKCE code verifier and challenge
|
|
22
|
+
*/
|
|
23
|
+
declare function generatePKCE(): {
|
|
24
|
+
codeVerifier: string;
|
|
25
|
+
codeChallenge: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Open a URL in the default browser
|
|
29
|
+
*/
|
|
30
|
+
declare function openBrowser(url: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Start a local HTTP server to receive the OAuth callback
|
|
33
|
+
*/
|
|
34
|
+
declare function startCallbackServer(): Promise<{
|
|
35
|
+
code: string;
|
|
36
|
+
state: string;
|
|
37
|
+
}>;
|
|
38
|
+
interface OAuthFlowOptions {
|
|
39
|
+
env: string;
|
|
40
|
+
envConfig: EnvironmentConfig;
|
|
41
|
+
credentialsManager: CredentialsManager;
|
|
42
|
+
clientName?: string;
|
|
43
|
+
/** Named account profile to store tokens under (default: resolved current). */
|
|
44
|
+
profile?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Perform the full OAuth2 browser-based flow with PKCE.
|
|
48
|
+
*
|
|
49
|
+
* This always opens the browser for authorization. It never returns a cached
|
|
50
|
+
* token — that's the job of createAuthenticatedContext. The only thing reused
|
|
51
|
+
* is the registered OAuth client ID (which is identity, not auth).
|
|
52
|
+
*/
|
|
53
|
+
declare function performOAuthFlow(options: OAuthFlowOptions): Promise<AuthResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Refresh an access token using a stored refresh token.
|
|
56
|
+
* Returns updated credentials on success, null on failure.
|
|
57
|
+
* Clears only the named profile on failure (other accounts stay intact).
|
|
58
|
+
*/
|
|
59
|
+
declare function refreshAccessToken(envConfig: EnvironmentConfig, creds: CLICredentials, credentialsManager: CredentialsManager, env: string, profile?: string): Promise<CLICredentials | null>;
|
|
3
60
|
|
|
4
61
|
interface SelectFromListOptions<T> {
|
|
5
62
|
title?: string;
|
|
@@ -143,4 +200,4 @@ declare function getTextDelta(event: AGUIEvent): string | null;
|
|
|
143
200
|
*/
|
|
144
201
|
declare function getErrorMessage(event: AGUIEvent): string | null;
|
|
145
202
|
|
|
146
|
-
export { type ChatMessage as AGUIChatMessage, AGUIClient, type AGUIClientOptions, type AGUIEvent, type AGUIEventType, type SelectFromListOptions, type SelectResult, clearScreen, formatTimestamp, getErrorMessage, getTextDelta, isErrorEvent, isTextContentEvent, printMessage, printTable, selectFromList, wrapText };
|
|
203
|
+
export { type ChatMessage as AGUIChatMessage, AGUIClient, type AGUIClientOptions, type AGUIEvent, type AGUIEventType, type AuthResult, CLICredentials, CredentialsManager, EnvironmentConfig, type OAuthFlowOptions, type SelectFromListOptions, type SelectResult, clearScreen, formatTimestamp, generatePKCE, getErrorMessage, getTextDelta, isErrorEvent, isTextContentEvent, openBrowser, performOAuthFlow, printMessage, printTable, refreshAccessToken, selectFromList, startCallbackServer, wrapText };
|
package/dist/cli-utils.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
CALLBACK_PATH,
|
|
4
4
|
CALLBACK_PORT,
|
|
5
5
|
CredentialsManager,
|
|
6
|
+
DEFAULT_PROFILE,
|
|
6
7
|
ENVIRONMENTS,
|
|
7
8
|
REDIRECT_URI,
|
|
8
9
|
clearScreen,
|
|
@@ -21,12 +22,13 @@ import {
|
|
|
21
22
|
selectFromList,
|
|
22
23
|
startCallbackServer,
|
|
23
24
|
wrapText
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-SIV2WU2I.js";
|
|
25
26
|
export {
|
|
26
27
|
AGUIClient,
|
|
27
28
|
CALLBACK_PATH,
|
|
28
29
|
CALLBACK_PORT,
|
|
29
30
|
CredentialsManager,
|
|
31
|
+
DEFAULT_PROFILE,
|
|
30
32
|
ENVIRONMENTS,
|
|
31
33
|
REDIRECT_URI,
|
|
32
34
|
clearScreen,
|