@ratio-app/cli 0.1.1
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 +504 -0
- package/bin/run.mjs +5 -0
- package/dist/commands/__test__/throw.d.ts +25 -0
- package/dist/commands/__test__/throw.js +238 -0
- package/dist/commands/__test__/throw.js.map +1 -0
- package/dist/commands/app/create.d.ts +55 -0
- package/dist/commands/app/create.js +609 -0
- package/dist/commands/app/create.js.map +1 -0
- package/dist/commands/app/deploy.d.ts +14 -0
- package/dist/commands/app/deploy.js +179 -0
- package/dist/commands/app/deploy.js.map +1 -0
- package/dist/commands/app/dev.d.ts +28 -0
- package/dist/commands/app/dev.js +701 -0
- package/dist/commands/app/dev.js.map +1 -0
- package/dist/commands/app/generate.d.ts +14 -0
- package/dist/commands/app/generate.js +179 -0
- package/dist/commands/app/generate.js.map +1 -0
- package/dist/commands/app/info.d.ts +14 -0
- package/dist/commands/app/info.js +179 -0
- package/dist/commands/app/info.js.map +1 -0
- package/dist/commands/app/link.d.ts +118 -0
- package/dist/commands/app/link.js +1120 -0
- package/dist/commands/app/link.js.map +1 -0
- package/dist/commands/auth/login.d.ts +43 -0
- package/dist/commands/auth/login.js +987 -0
- package/dist/commands/auth/login.js.map +1 -0
- package/dist/commands/auth/logout.d.ts +15 -0
- package/dist/commands/auth/logout.js +486 -0
- package/dist/commands/auth/logout.js.map +1 -0
- package/dist/commands/auth/whoami.d.ts +16 -0
- package/dist/commands/auth/whoami.js +531 -0
- package/dist/commands/auth/whoami.js.map +1 -0
- package/dist/commands/dev/listen.d.ts +84 -0
- package/dist/commands/dev/listen.js +1187 -0
- package/dist/commands/dev/listen.js.map +1 -0
- package/dist/commands/dev/trigger.d.ts +88 -0
- package/dist/commands/dev/trigger.js +729 -0
- package/dist/commands/dev/trigger.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +617 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth/authorize-code-exchange.d.ts +50 -0
- package/dist/lib/auth/authorize-code-exchange.js +114 -0
- package/dist/lib/auth/authorize-code-exchange.js.map +1 -0
- package/dist/lib/auth/authorize-url.d.ts +42 -0
- package/dist/lib/auth/authorize-url.js +35 -0
- package/dist/lib/auth/authorize-url.js.map +1 -0
- package/dist/lib/auth/browser.d.ts +16 -0
- package/dist/lib/auth/browser.js +48 -0
- package/dist/lib/auth/browser.js.map +1 -0
- package/dist/lib/auth/config.d.ts +41 -0
- package/dist/lib/auth/config.js +21 -0
- package/dist/lib/auth/config.js.map +1 -0
- package/dist/lib/auth/http-refresher.d.ts +30 -0
- package/dist/lib/auth/http-refresher.js +103 -0
- package/dist/lib/auth/http-refresher.js.map +1 -0
- package/dist/lib/auth/identity.d.ts +26 -0
- package/dist/lib/auth/identity.js +10 -0
- package/dist/lib/auth/identity.js.map +1 -0
- package/dist/lib/auth/index.d.ts +13 -0
- package/dist/lib/auth/index.js +527 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/auth/loopback-server.d.ts +65 -0
- package/dist/lib/auth/loopback-server.js +245 -0
- package/dist/lib/auth/loopback-server.js.map +1 -0
- package/dist/lib/auth/manual-prompt.d.ts +17 -0
- package/dist/lib/auth/manual-prompt.js +20 -0
- package/dist/lib/auth/manual-prompt.js.map +1 -0
- package/dist/lib/auth/pkce.d.ts +24 -0
- package/dist/lib/auth/pkce.js +15 -0
- package/dist/lib/auth/pkce.js.map +1 -0
- package/dist/lib/auth/state.d.ts +20 -0
- package/dist/lib/auth/state.js +19 -0
- package/dist/lib/auth/state.js.map +1 -0
- package/dist/lib/auth/verbose-trace.d.ts +25 -0
- package/dist/lib/auth/verbose-trace.js +17 -0
- package/dist/lib/auth/verbose-trace.js.map +1 -0
- package/dist/lib/credentials/clock.d.ts +18 -0
- package/dist/lib/credentials/clock.js +10 -0
- package/dist/lib/credentials/clock.js.map +1 -0
- package/dist/lib/credentials/index.d.ts +5 -0
- package/dist/lib/credentials/index.js +340 -0
- package/dist/lib/credentials/index.js.map +1 -0
- package/dist/lib/credentials/path.d.ts +26 -0
- package/dist/lib/credentials/path.js +32 -0
- package/dist/lib/credentials/path.js.map +1 -0
- package/dist/lib/credentials/refresher.d.ts +26 -0
- package/dist/lib/credentials/refresher.js +34 -0
- package/dist/lib/credentials/refresher.js.map +1 -0
- package/dist/lib/credentials/schema.d.ts +138 -0
- package/dist/lib/credentials/schema.js +85 -0
- package/dist/lib/credentials/schema.js.map +1 -0
- package/dist/lib/credentials/store.d.ts +102 -0
- package/dist/lib/credentials/store.js +337 -0
- package/dist/lib/credentials/store.js.map +1 -0
- package/dist/lib/credentials/types.d.ts +65 -0
- package/dist/lib/credentials/types.js +1 -0
- package/dist/lib/credentials/types.js.map +1 -0
- package/dist/lib/dev-store-gate.d.ts +66 -0
- package/dist/lib/dev-store-gate.js +15 -0
- package/dist/lib/dev-store-gate.js.map +1 -0
- package/dist/lib/errors.d.ts +60 -0
- package/dist/lib/errors.js +101 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/manifest/index.d.ts +2 -0
- package/dist/lib/manifest/index.js +144 -0
- package/dist/lib/manifest/index.js.map +1 -0
- package/dist/lib/manifest/read-manifest.d.ts +32 -0
- package/dist/lib/manifest/read-manifest.js +87 -0
- package/dist/lib/manifest/read-manifest.js.map +1 -0
- package/dist/lib/manifest/write-client-id.d.ts +33 -0
- package/dist/lib/manifest/write-client-id.js +94 -0
- package/dist/lib/manifest/write-client-id.js.map +1 -0
- package/dist/lib/messages.d.ts +15 -0
- package/dist/lib/messages.js +16 -0
- package/dist/lib/messages.js.map +1 -0
- package/dist/lib/orchestrator/child-runner.d.ts +140 -0
- package/dist/lib/orchestrator/child-runner.js +471 -0
- package/dist/lib/orchestrator/child-runner.js.map +1 -0
- package/dist/lib/preview/index.d.ts +11 -0
- package/dist/lib/preview/index.js +17 -0
- package/dist/lib/preview/index.js.map +1 -0
- package/dist/lib/preview/types.d.ts +16 -0
- package/dist/lib/preview/types.js +11 -0
- package/dist/lib/preview/types.js.map +1 -0
- package/dist/lib/ratio-command.d.ts +52 -0
- package/dist/lib/ratio-command.js +141 -0
- package/dist/lib/ratio-command.js.map +1 -0
- package/dist/lib/relay/index.d.ts +7 -0
- package/dist/lib/relay/index.js +362 -0
- package/dist/lib/relay/index.js.map +1 -0
- package/dist/lib/relay/relay-client.d.ts +91 -0
- package/dist/lib/relay/relay-client.js +362 -0
- package/dist/lib/relay/relay-client.js.map +1 -0
- package/dist/lib/relay/types.d.ts +71 -0
- package/dist/lib/relay/types.js +1 -0
- package/dist/lib/relay/types.js.map +1 -0
- package/dist/lib/render-error.d.ts +35 -0
- package/dist/lib/render-error.js +115 -0
- package/dist/lib/render-error.js.map +1 -0
- package/dist/lib/verbose-flag.d.ts +12 -0
- package/dist/lib/verbose-flag.js +17 -0
- package/dist/lib/verbose-flag.js.map +1 -0
- package/dist/render-CrHGqTPH.d.ts +115 -0
- package/dist/templates/.gitkeep +0 -0
- package/dist/templates/minimal/.env.example.tmpl +4 -0
- package/dist/templates/minimal/README.md.tmpl +26 -0
- package/dist/templates/minimal/gitignore +5 -0
- package/dist/templates/minimal/manifest.json +5 -0
- package/dist/templates/minimal/package.json.tmpl +25 -0
- package/dist/templates/minimal/ratio.config.jsonc.tmpl +17 -0
- package/dist/templates/minimal/src/index.ts.tmpl +26 -0
- package/dist/templates/minimal/src/install.ts.tmpl +37 -0
- package/dist/templates/minimal/src/webhooks.ts.tmpl +57 -0
- package/dist/templates/minimal/tsconfig.json +17 -0
- package/dist/templates/serverless/README.md.tmpl +28 -0
- package/dist/templates/serverless/gitignore +6 -0
- package/dist/templates/serverless/manifest.json +5 -0
- package/dist/templates/serverless/package.json.tmpl +22 -0
- package/dist/templates/serverless/ratio.config.jsonc.tmpl +17 -0
- package/dist/templates/serverless/src/index.ts.tmpl +20 -0
- package/dist/templates/serverless/src/install.ts.tmpl +20 -0
- package/dist/templates/serverless/src/webhooks.ts.tmpl +36 -0
- package/dist/templates/serverless/tsconfig.json +18 -0
- package/dist/templates/serverless/wrangler.toml.tmpl +9 -0
- package/dist/templates/with-admin-ui/.env.example.tmpl +4 -0
- package/dist/templates/with-admin-ui/README.md.tmpl +28 -0
- package/dist/templates/with-admin-ui/gitignore +6 -0
- package/dist/templates/with-admin-ui/manifest.json +5 -0
- package/dist/templates/with-admin-ui/package.json.tmpl +31 -0
- package/dist/templates/with-admin-ui/ratio.config.jsonc.tmpl +17 -0
- package/dist/templates/with-admin-ui/src/admin/app/globals.css +19 -0
- package/dist/templates/with-admin-ui/src/admin/app/layout.tsx.tmpl +13 -0
- package/dist/templates/with-admin-ui/src/admin/app/page.tsx.tmpl +8 -0
- package/dist/templates/with-admin-ui/src/admin/next-env.d.ts.tmpl +5 -0
- package/dist/templates/with-admin-ui/src/admin/next.config.mjs +3 -0
- package/dist/templates/with-admin-ui/src/admin/tsconfig.json +20 -0
- package/dist/templates/with-admin-ui/src/index.ts.tmpl +26 -0
- package/dist/templates/with-admin-ui/src/install.ts.tmpl +37 -0
- package/dist/templates/with-admin-ui/src/webhooks.ts.tmpl +57 -0
- package/dist/templates/with-admin-ui/tsconfig.json +17 -0
- package/package.json +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/app/link.ts","../../../src/lib/credentials/clock.ts","../../../src/lib/credentials/path.ts","../../../src/lib/errors.ts","../../../src/lib/credentials/refresher.ts","../../../src/lib/credentials/store.ts","../../../src/lib/credentials/schema.ts","../../../src/lib/manifest/write-client-id.ts","../../../src/lib/ratio-command.ts","../../../src/lib/render-error.ts","../../../src/lib/verbose-flag.ts"],"sourcesContent":["import { createInterface } from 'node:readline/promises';\nimport fs from 'node:fs';\nimport { Flags } from '@oclif/core';\nimport { z } from 'zod';\nimport { FileCredentialStore } from '../../lib/credentials/index.js';\nimport type { CredentialStore, DeveloperSession } from '../../lib/credentials/index.js';\nimport {\n NetworkError,\n NotLoggedInError,\n ValidationError,\n} from '../../lib/errors.js';\nimport {\n writeClientId,\n MANIFEST_FILE_NAME,\n} from '../../lib/manifest/write-client-id.js';\nimport { RatioCommand } from '../../lib/ratio-command.js';\nimport { jsonFlag, verboseFlag } from '../../lib/verbose-flag.js';\n\n/** Per-request budget for platform HTTP calls; slower responses time out. */\nexport const REQUEST_TIMEOUT_MS = 10_000;\n\n/** Compiled-in default server for app-link when nothing else resolves. */\nexport const DEFAULT_LINK_SERVER = 'https://api.ratio.app';\n\n/** Env var that overrides the server for this command. */\nconst SERVER_ENV_VAR = 'RATIO_SERVER';\n\n/**\n * Env var carrying a developer bearer for unattended usage. Skips the\n * interactive sign-in entirely. Strictly separate from RATIO_API_TOKEN\n * (which is the OAuth token used by other commands and is never read here).\n */\nconst DEVELOPER_TOKEN_ENV_VAR = 'RATIO_DEVELOPER_TOKEN';\n\n/** Developer sessions live 24 hours from issue; computed at persist time. */\nexport const DEVELOPER_SESSION_TTL_MS = 24 * 60 * 60 * 1000;\n\n/** Fallback description when the user provides none at create time. */\nexport const DEFAULT_APP_DESCRIPTION = 'A Ratio app scaffolded via ratio-cli.';\n\n/** Default category slug used at create time; override with --category. */\nexport const DEFAULT_CATEGORY_SLUG = 'general';\n\n/** Read-only extraction of the manifest's appName row (echo only). */\nconst MANIFEST_APP_NAME_ROW = /^\\s*\"appName\"\\s*:\\s*\"([^\"]*)\"/m;\n\n/** Read-only presence check for the manifest's clientId row. */\nconst MANIFEST_CLIENT_ID_ROW = /^(\\s*\"clientId\"\\s*:\\s*\")([^\"]*)(\"\\s*,?)/m;\n\n// ── Wire shapes ─────────────────────────────────────────────────────────────\n\nconst signinResponseSchema = z.object({\n 'accessToken': z.string().min(1),\n 'developer': z\n .object({\n 'id': z.string().min(1),\n 'email': z.string(),\n })\n .passthrough(),\n});\n\nexport type SigninResponse = z.infer<typeof signinResponseSchema>;\n\nconst applicationSummarySchema = z\n .object({\n 'id': z.string().min(1),\n 'name': z.string(),\n 'clientId': z.string().nullish(),\n })\n .passthrough();\n\nexport type ApplicationSummary = z.infer<typeof applicationSummarySchema>;\n\nconst listApplicationsResponseSchema = z\n .object({\n 'data': z\n .object({\n 'applications': z.array(applicationSummarySchema),\n })\n .passthrough(),\n })\n .passthrough();\n\nconst createApplicationResponseSchema = z\n .object({\n 'data': z\n .object({\n 'id': z.string().min(1),\n 'clientId': z.string().nullish(),\n })\n .passthrough(),\n })\n .passthrough();\n\nconst credentialsResponseSchema = z\n .object({\n 'data': z\n .object({\n 'clientId': z.string().min(1),\n })\n .passthrough(),\n })\n .passthrough();\n\n/** Compute the session expiry from the moment the token was obtained. */\nexport function computeSessionExpiresAt(obtainedAtIso: string): string {\n return new Date(\n new Date(obtainedAtIso).getTime() + DEVELOPER_SESSION_TTL_MS,\n ).toISOString();\n}\n\n// ── Prompting ───────────────────────────────────────────────────────────────\n\n/** Interactive input seam. Tests inject a scripted fake. */\nexport interface LinkPrompter {\n /** Ask a visible-echo question (email, name, selection). */\n text(question: string): Promise<string>;\n /** Ask a masked question (password). Input is never echoed. */\n masked(question: string): Promise<string>;\n}\n\n/**\n * Production prompter. Questions render on stderr so stdout stays reserved\n * for the final human line or the machine-readable JSON envelope. The masked\n * variant reads raw stdin and never echoes a character; the entered value is\n * never logged and never persisted.\n */\nexport function defaultLinkPrompter(): LinkPrompter {\n return {\n async text(question: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n try {\n return (await rl.question(question)).trim();\n } finally {\n rl.close();\n }\n },\n async masked(question: string): Promise<string> {\n return new Promise<string>((resolvePromise, rejectPromise) => {\n const stdin = process.stdin;\n process.stderr.write(question);\n stdin.setRawMode?.(true);\n stdin.resume();\n let value = '';\n const finish = (err?: Error): void => {\n stdin.off('data', onData);\n stdin.setRawMode?.(false);\n stdin.pause();\n process.stderr.write('\\n');\n if (err !== undefined) {\n rejectPromise(err);\n } else {\n resolvePromise(value);\n }\n };\n const onData = (chunk: Buffer): void => {\n for (const ch of chunk.toString('utf8')) {\n if (ch === '\\r' || ch === '\\n') {\n finish();\n return;\n }\n if (ch === '\u0003') {\n finish(new ValidationError('Sign-in cancelled.'));\n return;\n }\n if (ch === '' || ch === '\\b') {\n value = value.slice(0, -1);\n continue;\n }\n if (ch >= ' ') {\n value += ch;\n }\n }\n };\n stdin.on('data', onData);\n });\n },\n };\n}\n\n// ── HTTP plumbing ───────────────────────────────────────────────────────────\n\n/**\n * Internal marker for a 401 from an app-management call. Always converted\n * by the retry wrapper into either a fresh sign-in or a NotLoggedInError;\n * it never escapes the command.\n */\nclass SessionRejectedError extends Error {\n constructor() {\n super('session rejected');\n }\n}\n\ninterface HttpJsonResult {\n status: number;\n body: unknown;\n}\n\n/**\n * Perform one platform HTTP request, tracing \"<METHOD> <path> → <status>\n * (<ms>ms)\" when verbose. Network failures, timeouts, and unparseable\n * bodies are reported without ever echoing request or response payloads.\n */\nasync function httpJson(\n url: string,\n pathLabel: string,\n init: RequestInit,\n trace: (message: string) => void,\n): Promise<HttpJsonResult> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n const method = init.method ?? 'GET';\n const started = Date.now();\n let response: Response;\n try {\n response = await fetch(url, { ...init, signal: controller.signal });\n } catch {\n throw new NetworkError('Could not reach the Ratio platform.');\n } finally {\n clearTimeout(timer);\n }\n trace(`${method} ${pathLabel} → ${response.status} (${Date.now() - started}ms)`);\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n body = undefined;\n }\n return { status: response.status, body };\n}\n\n/** Extract a human-safe server message from an error body, if present. */\nfunction serverMessage(body: unknown): string | undefined {\n if (typeof body === 'object' && body !== null && 'message' in body) {\n const message = (body as { message: unknown }).message;\n if (typeof message === 'string' && message.length > 0) {\n return message;\n }\n }\n return undefined;\n}\n\n// ── Command ─────────────────────────────────────────────────────────────────\n\ninterface BearerState {\n token: string;\n source: 'env' | 'file' | 'signin';\n email?: string;\n}\n\nexport default class AppLink extends RatioCommand {\n static override description =\n 'Link the app in the current directory to a Ratio app registration ' +\n '(sign in, pick or create the app, and write its clientId into ' +\n 'ratio.config.jsonc).';\n\n static override examples = [\n '<%= config.bin %> app link',\n '<%= config.bin %> app link --email dev@example.com',\n '<%= config.bin %> app link --app-id 4f9d2c31-8a6b-4f2e-9c1d-3e5a7b9d0f12 --json',\n '<%= config.bin %> app link --client-id ratio_ci_1234',\n ];\n\n static override flags = {\n ...verboseFlag,\n ...jsonFlag,\n email: Flags.string({\n description: 'Developer email for sign-in (prompted when omitted).',\n }),\n 'client-id': Flags.string({\n description:\n 'Write this clientId directly into ratio.config.jsonc without ' +\n 'contacting the server.',\n }),\n 'app-id': Flags.string({\n description:\n 'Attach to an existing app by id; its clientId is fetched and written.',\n }),\n category: Flags.string({\n description: `Category slug used when creating a new app (default: ${DEFAULT_CATEGORY_SLUG}).`,\n }),\n };\n\n // ── Seams: overridable in tests; defaults hit the real process. ──────────\n public writeOut: (chunk: string) => void = (chunk) => {\n process.stdout.write(chunk);\n };\n\n public writeErr: (chunk: string) => void = (chunk) => {\n process.stderr.write(chunk);\n };\n\n public makeCredentialStore: () => CredentialStore = () =>\n new FileCredentialStore();\n\n public prompter: LinkPrompter = defaultLinkPrompter();\n\n public isInteractive: () => boolean = () => process.stdin.isTTY === true;\n\n public envReader: () => Readonly<Record<string, string | undefined>> = () =>\n process.env;\n\n public getCwd: () => string = () => process.cwd();\n\n public async run(): Promise<void> {\n const { flags } = await this.parse(AppLink);\n const jsonMode = flags.json === true;\n const verbose = flags.verbose === true;\n const trace = (message: string): void => {\n if (verbose) {\n this.writeErr(`[verbose] ${message}\\n`);\n }\n };\n const cwd = this.getCwd();\n\n // Step 1 — validate the manifest before touching auth or the network.\n const manifest = this.readManifest(cwd);\n\n // Direct-write path: trust the given clientId, no server contact.\n const directClientId = flags['client-id'];\n if (directClientId !== undefined && directClientId !== '') {\n await this.writeAndReport(cwd, directClientId, manifest.appName, jsonMode, trace);\n return;\n }\n\n const store = this.makeCredentialStore();\n const server = await this.resolveServer(store);\n\n // Step 2 — acquire the developer bearer (env → cached file → sign-in).\n const bearer = await this.acquireBearer(store, server, flags.email, trace);\n\n // Step 3 — resolve the target app's clientId.\n let clientId: string;\n let appName: string | undefined = manifest.appName;\n\n const appId = flags['app-id'];\n if (appId !== undefined && appId !== '') {\n clientId = await this.withAuthRetry(store, server, bearer, trace, (token) =>\n this.fetchAppClientId(server, token, appId, trace),\n );\n } else {\n const picked = await this.pickOrCreateApp(\n store,\n server,\n bearer,\n manifest.appName,\n flags.category,\n trace,\n );\n clientId = picked.clientId;\n appName = picked.appName ?? manifest.appName;\n }\n\n // Step 4 — write the manifest and report.\n await this.writeAndReport(cwd, clientId, appName, jsonMode, trace);\n }\n\n // ── Manifest ──────────────────────────────────────────────────────────────\n\n private readManifest(cwd: string): { appName?: string } {\n let content: string;\n try {\n content = fs.readFileSync(`${cwd}/${MANIFEST_FILE_NAME}`, 'utf8');\n } catch {\n throw new ValidationError(\n `${MANIFEST_FILE_NAME} not found in ${cwd}.`,\n `Run this command from the app's repo root; ${MANIFEST_FILE_NAME} not found in ${cwd}.`,\n );\n }\n if (!MANIFEST_CLIENT_ID_ROW.test(content)) {\n throw new ValidationError(\n `${MANIFEST_FILE_NAME} has no top-level \"clientId\" entry.`,\n 'Restore the \"clientId\" line from the app template, then re-run.',\n );\n }\n const appName = MANIFEST_APP_NAME_ROW.exec(content)?.[1];\n return appName !== undefined ? { appName } : {};\n }\n\n private async writeAndReport(\n cwd: string,\n clientId: string,\n appName: string | undefined,\n jsonMode: boolean,\n trace: (message: string) => void,\n ): Promise<void> {\n const result = await writeClientId(cwd, clientId);\n if (result.previousClientId !== undefined) {\n this.writeErr(\n `Warning: replacing existing clientId ${result.previousClientId} with ${clientId}\\n`,\n );\n }\n if (result.written) {\n const bytes = fs.statSync(result.path).size;\n trace(`write ${MANIFEST_FILE_NAME} (${bytes}b)`);\n }\n\n const displayName = result.appName ?? appName ?? clientId;\n\n if (jsonMode) {\n const data: Record<string, unknown> = { 'clientId': clientId };\n if (result.appName !== undefined) {\n data['appName'] = result.appName;\n } else if (appName !== undefined) {\n data['appName'] = appName;\n }\n data['written'] = result.written;\n data['path'] = result.path;\n this.writeOut(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n\n if (result.written) {\n this.writeOut(\n `Linked to app '${displayName}' (clientId: ${clientId}). Wrote to ${result.path}.\\n`,\n );\n } else {\n this.writeOut(\n `Already linked to '${displayName}' (clientId: ${clientId}). No changes.\\n`,\n );\n }\n }\n\n // ── Server + bearer acquisition ───────────────────────────────────────────\n\n private async resolveServer(store: CredentialStore): Promise<string> {\n try {\n const creds = await store.load();\n if (typeof creds.server === 'string' && creds.server.length > 0) {\n return creds.server;\n }\n } catch {\n // No credentials file yet — fall through to env / default.\n }\n const envServer = this.envReader()[SERVER_ENV_VAR];\n if (typeof envServer === 'string' && envServer.length > 0) {\n return envServer;\n }\n return DEFAULT_LINK_SERVER;\n }\n\n private async acquireBearer(\n store: CredentialStore,\n server: string,\n emailFlag: string | undefined,\n trace: (message: string) => void,\n ): Promise<BearerState> {\n const envToken = this.envReader()[DEVELOPER_TOKEN_ENV_VAR];\n if (typeof envToken === 'string' && envToken.length > 0) {\n return { token: envToken, source: 'env' };\n }\n\n try {\n const token = await store.getValidDeveloperToken();\n const state: BearerState = { token, source: 'file' };\n let cachedEmail: string | undefined;\n try {\n const creds = await store.load();\n cachedEmail = creds.developer?.email;\n } catch {\n // Email lookup is best-effort; retry UX will prompt when needed.\n }\n const email = emailFlag ?? cachedEmail;\n if (email !== undefined) {\n state.email = email;\n }\n return state;\n } catch (err) {\n if (!(err instanceof NotLoggedInError)) {\n throw err;\n }\n }\n\n return this.signinInteractive(store, server, emailFlag, trace);\n }\n\n private async signinInteractive(\n store: CredentialStore,\n server: string,\n knownEmail: string | undefined,\n trace: (message: string) => void,\n ): Promise<BearerState> {\n if (!this.isInteractive()) {\n throw new ValidationError(\n 'Developer sign-in needs an interactive terminal.',\n 'Non-interactive shell — set RATIO_DEVELOPER_TOKEN or run interactively.',\n );\n }\n\n const email =\n knownEmail !== undefined && knownEmail !== ''\n ? knownEmail\n : await this.prompter.text('Developer email: ');\n const password = await this.prompter.masked('Developer password: ');\n\n const session = await this.signin(server, email, password, trace);\n await store.saveDeveloperSession(session);\n return { token: session.jwt, source: 'signin', email };\n }\n\n private async signin(\n server: string,\n email: string,\n password: string,\n trace: (message: string) => void,\n ): Promise<DeveloperSession> {\n const base = server.replace(/\\/+$/, '');\n const { status, body } = await httpJson(\n `${base}/api/v1/developers/signin`,\n '/developers/signin',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ 'email': email, 'password': password }),\n },\n trace,\n );\n\n if (status === 401) {\n throw new NotLoggedInError(\n 'Invalid credentials or account not verified. Sign in via the developer portal to verify your email if you have not done so.',\n );\n }\n if (status === 429) {\n throw new NetworkError(\n 'Too many signin attempts. Wait 60 seconds or set RATIO_DEVELOPER_TOKEN for CI usage.',\n );\n }\n if (status < 200 || status >= 300) {\n throw new NetworkError(\n `The server had a problem processing your request (status: ${status}); try again.`,\n );\n }\n\n const parsed = signinResponseSchema.safeParse(body);\n if (!parsed.success) {\n throw new NetworkError('The server response was not the expected shape.');\n }\n\n const obtainedAt = new Date().toISOString();\n return {\n jwt: parsed.data.accessToken,\n developerId: parsed.data.developer.id,\n email: parsed.data.developer.email,\n expiresAt: computeSessionExpiresAt(obtainedAt),\n obtainedAt,\n };\n }\n\n /**\n * Run one app-management call; on a 401 re-prompt the password ONCE\n * (email already known when possible), retry ONCE, and fail typed on a\n * second 401. Unattended runs (env token / non-TTY) fail typed directly.\n */\n private async withAuthRetry<T>(\n store: CredentialStore,\n server: string,\n bearer: BearerState,\n trace: (message: string) => void,\n call: (token: string) => Promise<T>,\n ): Promise<T> {\n const expired = (): NotLoggedInError =>\n new NotLoggedInError(\n 'Your developer session expired.',\n 'Your developer session expired. Sign in again by re-running `ratio app link`.',\n );\n\n try {\n return await call(bearer.token);\n } catch (err) {\n if (!(err instanceof SessionRejectedError)) {\n throw err;\n }\n if (bearer.source === 'env' || !this.isInteractive()) {\n throw expired();\n }\n\n const email =\n bearer.email !== undefined && bearer.email !== ''\n ? bearer.email\n : await this.prompter.text('Developer email: ');\n const password = await this.prompter.masked('Developer password: ');\n const session = await this.signin(server, email, password, trace);\n await store.saveDeveloperSession(session);\n bearer.token = session.jwt;\n bearer.source = 'signin';\n bearer.email = email;\n\n try {\n return await call(bearer.token);\n } catch (err2) {\n if (err2 instanceof SessionRejectedError) {\n throw expired();\n }\n throw err2;\n }\n }\n }\n\n // ── App-management calls ──────────────────────────────────────────────────\n\n private async fetchAppClientId(\n server: string,\n token: string,\n appId: string,\n trace: (message: string) => void,\n ): Promise<string> {\n const base = server.replace(/\\/+$/, '');\n const { status, body } = await httpJson(\n `${base}/api/v1/applications/${appId}/credentials`,\n `/applications/${appId}/credentials`,\n { method: 'GET', headers: { Authorization: `Bearer ${token}` } },\n trace,\n );\n\n if (status === 401) {\n throw new SessionRejectedError();\n }\n if (status === 404) {\n throw new ValidationError('Selected app not found. It may have been deleted.');\n }\n if (status < 200 || status >= 300) {\n throw new NetworkError(\n `The server had a problem processing your request (status: ${status}); try again.`,\n );\n }\n\n const parsed = credentialsResponseSchema.safeParse(body);\n if (!parsed.success) {\n throw new NetworkError('The server response was not the expected shape.');\n }\n return parsed.data.data.clientId;\n }\n\n private async listApplications(\n server: string,\n token: string,\n trace: (message: string) => void,\n ): Promise<ApplicationSummary[]> {\n const base = server.replace(/\\/+$/, '');\n const { status, body } = await httpJson(\n `${base}/api/v1/applications`,\n '/applications',\n { method: 'GET', headers: { Authorization: `Bearer ${token}` } },\n trace,\n );\n\n if (status === 401) {\n throw new SessionRejectedError();\n }\n if (status < 200 || status >= 300) {\n throw new NetworkError(\n `The server had a problem processing your request (status: ${status}); try again.`,\n );\n }\n\n const parsed = listApplicationsResponseSchema.safeParse(body);\n if (!parsed.success) {\n throw new NetworkError('The server response was not the expected shape.');\n }\n return parsed.data.data.applications;\n }\n\n private async createApplication(\n server: string,\n token: string,\n payload: { name: string; description: string; categoryIds: string[] },\n trace: (message: string) => void,\n ): Promise<{ id: string; clientId?: string }> {\n const base = server.replace(/\\/+$/, '');\n const { status, body } = await httpJson(\n `${base}/api/v1/applications`,\n '/applications',\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n 'name': payload.name,\n 'description': payload.description,\n 'categoryIds': payload.categoryIds,\n }),\n },\n trace,\n );\n\n if (status === 401) {\n throw new SessionRejectedError();\n }\n if (status === 400) {\n throw new ValidationError(\n serverMessage(body) ?? 'The server rejected the request.',\n 'Category resolution failed — visit the portal to seed categories or pass --category <slug> with a known slug.',\n );\n }\n if (status < 200 || status >= 300) {\n throw new NetworkError(\n `The server had a problem processing your request (status: ${status}); try again.`,\n );\n }\n\n const parsed = createApplicationResponseSchema.safeParse(body);\n if (!parsed.success) {\n throw new NetworkError('The server response was not the expected shape.');\n }\n const clientId = parsed.data.data.clientId;\n return {\n id: parsed.data.data.id,\n ...(typeof clientId === 'string' && clientId.length > 0\n ? { clientId }\n : {}),\n };\n }\n\n // ── Interactive pick / create flow ────────────────────────────────────────\n\n private async pickOrCreateApp(\n store: CredentialStore,\n server: string,\n bearer: BearerState,\n manifestAppName: string | undefined,\n categoryFlag: string | undefined,\n trace: (message: string) => void,\n ): Promise<{ clientId: string; appName?: string }> {\n if (!this.isInteractive()) {\n throw new ValidationError(\n 'Choosing an app needs an interactive terminal.',\n 'Non-interactive shell — pass --app-id <id> or --client-id <x>.',\n );\n }\n\n const apps = await this.withAuthRetry(store, server, bearer, trace, (token) =>\n this.listApplications(server, token, trace),\n );\n\n this.writeErr('0. Create new app\\n');\n apps.forEach((app, index) => {\n const shortId = app.id.slice(0, 8);\n const shortClientId =\n typeof app.clientId === 'string' && app.clientId.length > 0\n ? app.clientId.slice(0, 8)\n : '-';\n this.writeErr(\n `${index + 1}. ${app.name} (id: ${shortId}, clientId: ${shortClientId})\\n`,\n );\n });\n\n const answer = await this.prompter.text('Select a number: ');\n const selection = Number.parseInt(answer, 10);\n if (\n Number.isNaN(selection) ||\n selection < 0 ||\n selection > apps.length ||\n String(selection) !== answer.trim()\n ) {\n throw new ValidationError(\n `Invalid selection: enter a number between 0 and ${apps.length}.`,\n );\n }\n\n if (selection === 0) {\n return this.createFlow(store, server, bearer, manifestAppName, categoryFlag, trace);\n }\n\n const chosen = apps[selection - 1];\n if (chosen === undefined) {\n throw new ValidationError(\n `Invalid selection: enter a number between 0 and ${apps.length}.`,\n );\n }\n const clientId = await this.withAuthRetry(store, server, bearer, trace, (token) =>\n this.fetchAppClientId(server, token, chosen.id, trace),\n );\n return { clientId, appName: chosen.name };\n }\n\n private async createFlow(\n store: CredentialStore,\n server: string,\n bearer: BearerState,\n manifestAppName: string | undefined,\n categoryFlag: string | undefined,\n trace: (message: string) => void,\n ): Promise<{ clientId: string; appName?: string }> {\n const nameDefault =\n manifestAppName !== undefined && manifestAppName !== ''\n ? manifestAppName\n : undefined;\n const nameAnswer = await this.prompter.text(\n nameDefault !== undefined ? `App name [${nameDefault}]: ` : 'App name: ',\n );\n const name = nameAnswer !== '' ? nameAnswer : (nameDefault ?? '');\n if (name === '') {\n throw new ValidationError('An app name is required.');\n }\n\n const descriptionAnswer = await this.prompter.text(\n `Description [${DEFAULT_APP_DESCRIPTION}]: `,\n );\n const description =\n descriptionAnswer !== '' ? descriptionAnswer : DEFAULT_APP_DESCRIPTION;\n\n const categoryIds = [\n categoryFlag !== undefined && categoryFlag !== ''\n ? categoryFlag\n : DEFAULT_CATEGORY_SLUG,\n ];\n\n const created = await this.withAuthRetry(store, server, bearer, trace, (token) =>\n this.createApplication(server, token, { name, description, categoryIds }, trace),\n );\n\n if (created.clientId !== undefined) {\n return { clientId: created.clientId, appName: name };\n }\n\n const clientId = await this.withAuthRetry(store, server, bearer, trace, (token) =>\n this.fetchAppClientId(server, token, created.id, trace),\n );\n return { clientId, appName: name };\n }\n}\n","/**\n * Injectable wall-clock seam for the credential store.\n *\n * This is the ONLY file in the credentials module allowed to sample the\n * real wall clock. Every other file receives a {@link Clock} instance so\n * tests can drive expiry-window logic deterministically with a fake.\n */\n\n/** Injectable time source. */\nexport interface Clock {\n /** Milliseconds since Unix epoch. */\n now(): number;\n}\n\n/** Production {@link Clock} backed by the system wall clock. */\nexport class SystemClock implements Clock {\n now(): number {\n return Date.now();\n }\n}\n","/**\n * Pure path resolution for the credentials file.\n *\n * POSIX: `$XDG_CONFIG_HOME/ratio/credentials` when the override is a\n * non-empty string, otherwise `~/.config/ratio/credentials`.\n * Windows: `%APPDATA%\\ratio\\credentials`, falling back to\n * `<home>\\AppData\\Roaming\\ratio\\credentials` when APPDATA is unset.\n *\n * `path.win32.join` / `path.posix.join` are used explicitly (never bare\n * `path.join`) so the result is deterministic when a test running on a\n * POSIX host fakes `platform === 'win32'`.\n */\n\nimport os from 'node:os';\nimport path from 'node:path';\n\n/** The subset of environment variables the path algorithm consults. */\nexport interface PathEnv {\n readonly XDG_CONFIG_HOME?: string;\n readonly APPDATA?: string;\n}\n\n/** Snapshot the two relevant variables from the real process env. */\nfunction snapshotPathEnv(): PathEnv {\n const snapshot: { XDG_CONFIG_HOME?: string; APPDATA?: string } = {};\n const xdgConfigHome = process.env['XDG_CONFIG_HOME'];\n if (xdgConfigHome !== undefined) {\n snapshot.XDG_CONFIG_HOME = xdgConfigHome;\n }\n const appData = process.env['APPDATA'];\n if (appData !== undefined) {\n snapshot.APPDATA = appData;\n }\n return snapshot;\n}\n\n/**\n * Pure function. Returns the on-disk path for the credentials file.\n * platform defaults to process.platform, homedir defaults to os.homedir(),\n * env defaults to a snapshot of {XDG_CONFIG_HOME, APPDATA} from process.env.\n * All three are injectable so unit tests fake Windows without a Windows host.\n */\nexport function defaultCredentialsPath(\n platform?: NodeJS.Platform,\n homedir?: string,\n env?: PathEnv,\n): string {\n const resolvedPlatform = platform ?? process.platform;\n const resolvedHome = homedir ?? os.homedir();\n const resolvedEnv = env ?? snapshotPathEnv();\n\n if (resolvedPlatform === 'win32') {\n const appData = resolvedEnv.APPDATA;\n const base =\n appData !== undefined && appData !== ''\n ? appData\n : path.win32.join(resolvedHome, 'AppData', 'Roaming');\n return path.win32.join(base, 'ratio', 'credentials');\n }\n\n const xdgConfigHome = resolvedEnv.XDG_CONFIG_HOME;\n const base =\n typeof xdgConfigHome === 'string' && xdgConfigHome !== ''\n ? xdgConfigHome\n : path.posix.join(resolvedHome, '.config');\n return path.posix.join(base, 'ratio', 'credentials');\n}\n","/**\n * Error hierarchy for the Ratio CLI. Every error thrown from a command's\n * run() is expected to be an instance of RatioCLIError so the global\n * render-error handler can map it to a deterministic stderr line and exit\n * code. Uncaught Error subclasses render as \"INTERNAL: <msg>\" with exit 1.\n *\n * No import from @oclif/core — this file is dependency-free so tests\n * can import it without oclif overhead.\n */\n\nexport class RatioCLIError extends Error {\n readonly code: string;\n readonly exitCode: number;\n readonly hint?: string;\n readonly docsUrl?: string;\n\n constructor(opts: {\n code: string;\n exitCode: number;\n message: string;\n hint?: string;\n docsUrl?: string;\n }) {\n super(opts.message);\n this.name = new.target.name;\n this.code = opts.code;\n this.exitCode = opts.exitCode;\n if (opts.hint !== undefined) this.hint = opts.hint;\n if (opts.docsUrl !== undefined) this.docsUrl = opts.docsUrl;\n }\n}\n\n/** Coming-soon stub UX. Renders on stdout with exit 0. */\nexport class ComingSoonError extends RatioCLIError {\n constructor(message: string) {\n super({ code: 'COMING_SOON', exitCode: 0, message });\n }\n}\n\n/** User has no credentials and command requires auth. */\nexport class NotLoggedInError extends RatioCLIError {\n constructor(\n message: string,\n hint: string = \"Run 'ratio auth login' to sign in.\",\n ) {\n super({ code: 'NOT_LOGGED_IN', exitCode: 3, message, hint });\n }\n}\n\nexport class TokenExpiredError extends RatioCLIError {\n constructor(\n message: string,\n hint: string = \"Run 'ratio auth login' to refresh your session.\",\n ) {\n super({ code: 'TOKEN_EXPIRED', exitCode: 3, message, hint });\n }\n}\n\nexport class ScopeDeniedError extends RatioCLIError {\n constructor(message: string, hint?: string) {\n const opts: { code: string; exitCode: number; message: string; hint?: string } = {\n code: 'SCOPE_DENIED',\n exitCode: 4,\n message,\n };\n if (hint !== undefined) opts.hint = hint;\n super(opts);\n }\n}\n\nexport class NetworkError extends RatioCLIError {\n constructor(message: string, hint: string = 'Check your network and retry.') {\n super({ code: 'NETWORK_ERROR', exitCode: 5, message, hint });\n }\n}\n\nexport class RelayConnectError extends RatioCLIError {\n constructor(\n message: string,\n hint: string = 'Verify the tunnel URL and that the auth-server is reachable.',\n ) {\n super({ code: 'RELAY_CONNECT', exitCode: 6, message, hint });\n }\n}\n\nexport class TemplateExistsError extends RatioCLIError {\n constructor(\n message: string,\n hint: string = 'Choose a different name or remove the existing directory.',\n ) {\n super({ code: 'TEMPLATE_EXISTS', exitCode: 7, message, hint });\n }\n}\n\nexport class ValidationError extends RatioCLIError {\n constructor(message: string, hint?: string) {\n const opts: { code: string; exitCode: number; message: string; hint?: string } = {\n code: 'VALIDATION_ERROR',\n exitCode: 8,\n message,\n };\n if (hint !== undefined) opts.hint = hint;\n super(opts);\n }\n}\n\nexport class RelayResumeGapError extends RatioCLIError {\n constructor(\n message: string,\n hint: string = 'Reconnect from the current cursor; older events are outside the buffer window.',\n ) {\n super({ code: 'RELAY_RESUME_GAP', exitCode: 9, message, hint });\n }\n}\n\nexport class OrchestratorSpawnError extends RatioCLIError {\n constructor(\n message: string,\n hint: string = \"Could not start your app's dev command — check that the command in ratio.config.jsonc runs from this directory.\",\n ) {\n super({ code: 'ORCHESTRATOR_SPAWN', exitCode: 10, message, hint });\n }\n}\n\nexport class OrchestratorDrainTimeoutError extends RatioCLIError {\n constructor(\n message: string,\n hint: string = 'Your dev command did not exit cleanly within 5 seconds — it was forcibly terminated.',\n ) {\n super({ code: 'ORCHESTRATOR_DRAIN', exitCode: 11, message, hint });\n }\n}\n\nexport function isRatioCLIError(err: unknown): err is RatioCLIError {\n return err instanceof RatioCLIError;\n}\n","/**\n * Injectable token-refresh seam for the credential store.\n *\n * The store never performs network I/O itself: when a stored token is\n * close to expiry the store calls the injected {@link TokenRefresher}\n * and persists whatever it returns. The concrete HTTP implementation is\n * wired in by the auth commands; this module only defines the seam plus\n * a throw-if-called placeholder used before sign-in support is wired.\n */\n\nimport { NotLoggedInError } from '../errors.js';\nimport type { TokenSet } from './types.js';\n\n/** Injectable seam that exchanges a refresh token for a fresh token set. */\nexport interface TokenRefresher {\n refresh(refreshToken: string): Promise<TokenSet>;\n}\n\n/**\n * v1 stub. Always rejects with a static-message NotLoggedInError; it\n * never inspects, echoes, or logs the token it was handed. The real\n * HTTP implementation is supplied by the auth commands.\n */\nexport class NotImplementedRefresher implements TokenRefresher {\n async refresh(refreshToken: string): Promise<TokenSet> {\n // Deliberately unused: this placeholder must never touch the token.\n void refreshToken;\n throw new NotLoggedInError(\n 'Token refresh is not available yet in this CLI version.',\n );\n }\n}\n","/**\n * File-backed credential store.\n *\n * Token resolution precedence (highest first):\n * 1. `RATIO_API_TOKEN` from the injected env reader — returned as-is\n * with ZERO filesystem access and ZERO refresher involvement.\n * 2. The requested profile (default: \"default\") from the credentials\n * file, silently refreshed via the injected TokenRefresher when it\n * is within 5 minutes of expiry.\n *\n * Write contract: atomic tmp + rename in the SAME directory, tmp created\n * with mode 0600 at open time (POSIX), parent directory created 0700,\n * belt-and-braces chmod 0600 after rename. On Windows no chmod / mode\n * checks are ever performed. Concurrent writers are last-writer-wins by\n * design — the atomic rename guarantees no half-written file, which is\n * sufficient for a single-developer CLI.\n *\n * No secret (access token, refresh token, raw file contents) is ever\n * included in an error message or a log line.\n */\n\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { dirname } from 'node:path';\nimport {\n NotLoggedInError,\n TokenExpiredError,\n ValidationError,\n} from '../errors.js';\nimport { SystemClock } from './clock.js';\nimport type { Clock } from './clock.js';\nimport { defaultCredentialsPath } from './path.js';\nimport { NotImplementedRefresher } from './refresher.js';\nimport type { TokenRefresher } from './refresher.js';\nimport { parseCredentials } from './schema.js';\nimport type { Credentials, DeveloperSession, Profile, TokenSet } from './types.js';\n\n/** Milliseconds before expiry at which a silent refresh is triggered. */\nconst REFRESH_WINDOW_MS = 300_000;\n\n/** Env var that short-circuits all file access when set to a non-empty string. */\nconst ENV_TOKEN_VAR = 'RATIO_API_TOKEN';\n\n/**\n * Env var that short-circuits developer-session file access when set to a\n * non-empty string. Strictly separate from RATIO_API_TOKEN: this one carries\n * the developer sign-in bearer used for app-management calls, never the\n * OAuth access token.\n */\nconst ENV_DEVELOPER_TOKEN_VAR = 'RATIO_DEVELOPER_TOKEN';\n\nexport interface CredentialStoreOptions {\n /** Defaults to defaultCredentialsPath(). */\n readonly filePath?: string;\n /** Defaults to new SystemClock(). */\n readonly clock?: Clock;\n /** Defaults to new NotImplementedRefresher(). */\n readonly refresher?: TokenRefresher;\n /** Defaults to () => process.env — injected for env-precedence tests. */\n readonly envReader?: () => Readonly<Record<string, string | undefined>>;\n /** Defaults to process.platform. */\n readonly platform?: NodeJS.Platform;\n}\n\nexport interface CredentialStore {\n /** Reads the full credentials file. Throws NotLoggedInError if missing. */\n load(): Promise<Credentials>;\n /** Atomic write with 0600 on POSIX. tmp + rename in same dir. */\n save(creds: Credentials): Promise<void>;\n /** Deletes the credentials file. Idempotent. */\n clear(): Promise<void>;\n /**\n * Env precedence: if envReader().RATIO_API_TOKEN is set, returns it\n * WITHOUT reading the file, WITHOUT touching the refresher, WITHOUT\n * any fs I/O. This is the auditable \"no disk access\" path.\n *\n * Otherwise: reads the file, validates, computes\n * msUntilExpiry = new Date(profile.expiresAt).getTime() - clock.now()\n * If msUntilExpiry < 300_000 (5 min), calls refresher.refresh(...),\n * persists the new TokenSet atomically, returns the new accessToken.\n * Otherwise returns profile.accessToken.\n */\n getValidAccessToken(profileName?: string): Promise<string>;\n /**\n * Env precedence: RATIO_DEVELOPER_TOKEN if set (no fs I/O).\n * Otherwise: load(); if creds.developer is absent or within 5 minutes of\n * expiry, throws NotLoggedInError ('Developer signin required.').\n * Otherwise returns creds.developer.jwt.\n * NO refresh attempt — the caller drives the re-prompt UX.\n */\n getValidDeveloperToken(): Promise<string>;\n /**\n * Merge a DeveloperSession into the loaded credentials (creating the file\n * if absent), atomic tmp+rename, 0600.\n */\n saveDeveloperSession(session: DeveloperSession): Promise<void>;\n}\n\n/** Narrow an unknown thrown value to a Node errno error with the given code. */\nfunction hasErrnoCode(err: unknown, code: string): boolean {\n return (\n err instanceof Error &&\n (err as NodeJS.ErrnoException).code === code\n );\n}\n\nexport class FileCredentialStore implements CredentialStore {\n private readonly filePath: string;\n private readonly clock: Clock;\n private readonly refresher: TokenRefresher;\n private readonly envReader: () => Readonly<\n Record<string, string | undefined>\n >;\n private readonly platform: NodeJS.Platform;\n\n constructor(opts?: CredentialStoreOptions) {\n this.filePath = opts?.filePath ?? defaultCredentialsPath();\n this.clock = opts?.clock ?? new SystemClock();\n this.refresher = opts?.refresher ?? new NotImplementedRefresher();\n this.envReader = opts?.envReader ?? (() => process.env);\n this.platform = opts?.platform ?? process.platform;\n }\n\n async load(): Promise<Credentials> {\n this.reapplyFileModeIfDrifted();\n\n let raw: string;\n try {\n raw = fs.readFileSync(this.filePath, 'utf8');\n } catch (err) {\n if (hasErrnoCode(err, 'ENOENT')) {\n throw new NotLoggedInError('You are not signed in.');\n }\n throw err;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // Never echo the raw contents — they may hold a truncated token.\n throw new ValidationError(\n 'credentials file is not valid JSON — run \"ratio auth login\" to reset',\n );\n }\n\n return parseCredentials(parsed);\n }\n\n async save(creds: Credentials): Promise<void> {\n const dir = dirname(this.filePath);\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n if (this.platform !== 'win32') {\n // mkdirSync does not re-mode a pre-existing directory; re-apply.\n fs.chmodSync(dir, 0o700);\n }\n\n const tmpPath = `${this.filePath}.${process.pid}.${crypto\n .randomBytes(8)\n .toString('hex')}.tmp`;\n try {\n // mode at open time closes the create-then-chmod race window.\n fs.writeFileSync(tmpPath, JSON.stringify(creds, null, 2), {\n mode: 0o600,\n });\n fs.renameSync(tmpPath, this.filePath);\n } catch (err) {\n try {\n fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup: the tmp file may never have been created.\n }\n throw err;\n }\n\n if (this.platform !== 'win32') {\n // Belt-and-braces: rename may preserve a prior destination mode.\n fs.chmodSync(this.filePath, 0o600);\n }\n }\n\n async clear(): Promise<void> {\n try {\n fs.unlinkSync(this.filePath);\n } catch (err) {\n if (hasErrnoCode(err, 'ENOENT')) {\n return; // Idempotent: already gone.\n }\n throw err;\n }\n }\n\n async getValidAccessToken(profileName?: string): Promise<string> {\n const envToken = this.envReader()[ENV_TOKEN_VAR];\n if (typeof envToken === 'string' && envToken.length > 0) {\n return envToken;\n }\n\n const creds = await this.load();\n const name = profileName ?? 'default';\n const profile = creds.profiles[name];\n if (profile === undefined) {\n throw new NotLoggedInError('You are not signed in.');\n }\n\n const msUntilExpiry =\n new Date(profile.expiresAt).getTime() - this.clock.now();\n if (msUntilExpiry < REFRESH_WINDOW_MS) {\n return this.refreshAndPersist(creds, name, profile);\n }\n\n return profile.accessToken;\n }\n\n async getValidDeveloperToken(): Promise<string> {\n const envToken = this.envReader()[ENV_DEVELOPER_TOKEN_VAR];\n if (typeof envToken === 'string' && envToken.length > 0) {\n return envToken;\n }\n\n let creds: Credentials;\n try {\n creds = await this.load();\n } catch (err) {\n if (err instanceof NotLoggedInError) {\n throw new NotLoggedInError('Developer signin required.');\n }\n throw err;\n }\n\n const session = creds.developer;\n if (session === undefined) {\n throw new NotLoggedInError('Developer signin required.');\n }\n\n const msUntilExpiry =\n new Date(session.expiresAt).getTime() - this.clock.now();\n if (msUntilExpiry < REFRESH_WINDOW_MS) {\n // No refresh endpoint exists for developer sessions — the caller\n // drives a fresh interactive sign-in instead.\n throw new NotLoggedInError('Developer signin required.');\n }\n\n return session.jwt;\n }\n\n async saveDeveloperSession(session: DeveloperSession): Promise<void> {\n let creds: Credentials;\n try {\n creds = await this.load();\n } catch (err) {\n if (err instanceof NotLoggedInError) {\n // Edge case: no prior credentials file. Create a fresh v1 shell so\n // the merged write below still validates on the next load().\n creds = { version: 1, server: '', clientId: '', profiles: {} };\n } else {\n throw err;\n }\n }\n\n const updated: Credentials = {\n ...creds,\n developer: session,\n };\n await this.save(updated);\n }\n\n /**\n * Exchange the profile's refresh token for a fresh token set, merge it\n * into the loaded credentials (preserving every other profile), persist\n * atomically, and return the new access token. A refresher failure is\n * wrapped in TokenExpiredError; the underlying message is deliberately\n * discarded so a secret can never leak through the error surface.\n */\n private async refreshAndPersist(\n creds: Credentials,\n name: string,\n profile: Profile,\n ): Promise<string> {\n let refreshed: TokenSet;\n try {\n refreshed = await this.refresher.refresh(profile.refreshToken);\n } catch {\n throw new TokenExpiredError(\n 'Your session has expired and could not be refreshed.',\n );\n }\n\n const updated: Credentials = {\n ...creds,\n profiles: {\n ...creds.profiles,\n [name]: {\n ...profile,\n accessToken: refreshed.accessToken,\n refreshToken: refreshed.refreshToken,\n expiresAt: refreshed.expiresAt,\n },\n },\n };\n await this.save(updated);\n return refreshed.accessToken;\n }\n\n /**\n * POSIX-only startup permission check: if the file exists with a mode\n * other than 0600, re-apply 0600 and emit a warning to stderr. Never\n * runs on Windows (stat modes there do not map to POSIX bits and chmod\n * must not be called). Missing file is NOT an error here — load()\n * reports it as NotLoggedInError.\n */\n private reapplyFileModeIfDrifted(): void {\n if (this.platform === 'win32') {\n return;\n }\n\n let stat: fs.Stats;\n try {\n stat = fs.statSync(this.filePath);\n } catch (err) {\n if (hasErrnoCode(err, 'ENOENT')) {\n return; // load() turns the missing file into NotLoggedInError.\n }\n throw err;\n }\n\n const mode = stat.mode & 0o777;\n if (mode !== 0o600) {\n fs.chmodSync(this.filePath, 0o600);\n process.stderr.write(\n `[warn] credentials file permissions were 0o${mode.toString(8)}; corrected to 0600.\\n`,\n );\n }\n }\n}\n","/**\n * Runtime validation for the on-disk credentials file.\n *\n * The parser is deliberately strict: unknown top-level keys, missing\n * fields, non-ISO timestamps, and any `version` other than `1` are all\n * rejected with a typed ValidationError. Error messages NEVER embed the\n * raw file contents — a malformed file may contain a truncated token.\n */\n\nimport { z } from 'zod';\nimport { ValidationError } from '../errors.js';\nimport type { Credentials } from './types.js';\n\nconst identitySchema = z\n .object({\n developerId: z.string(),\n email: z.string(),\n // Token-derived fields captured at login. Optional (additive) so files\n // written before these fields existed still validate as version 1.\n merchantId: z.string().optional(),\n scopes: z.array(z.string()).optional(),\n })\n .strict();\n\nconst profileSchema = z\n .object({\n accessToken: z.string(),\n refreshToken: z.string(),\n expiresAt: z.string().datetime({ offset: true }),\n identity: identitySchema,\n obtainedAt: z.string().datetime({ offset: true }),\n })\n .strict();\n\nconst developerSessionSchema = z\n .object({\n jwt: z.string(),\n developerId: z.string(),\n email: z.string(),\n expiresAt: z.string().datetime({ offset: true }),\n obtainedAt: z.string().datetime({ offset: true }),\n })\n .strict();\n\n/** Strict zod schema for the version-1 credentials file. */\nexport const credentialsSchema = z\n .object({\n version: z.literal(1),\n server: z.string(),\n clientId: z.string(),\n profiles: z.record(z.string(), profileSchema),\n // Optional cached developer session. Additive so files written before\n // this field existed still validate as version 1.\n developer: developerSessionSchema.optional(),\n })\n .strict();\n\n/**\n * Validate an already-JSON-parsed value against the version-1 shape.\n *\n * Throws ValidationError on any mismatch. A recognisable-but-unsupported\n * `version` gets a dedicated upgrade message; the version label is only\n * echoed when it is a number so arbitrary file contents can never leak\n * into an error message.\n */\nexport function parseCredentials(input: unknown): Credentials {\n if (typeof input === 'object' && input !== null && 'version' in input) {\n const version = (input as { version: unknown }).version;\n if (version !== 1) {\n const label = typeof version === 'number' ? String(version) : 'unknown';\n throw new ValidationError(\n `credentials file version ${label} is unsupported by this CLI — upgrade the CLI or sign in again`,\n );\n }\n }\n\n const result = credentialsSchema.safeParse(input);\n if (!result.success) {\n throw new ValidationError(\n 'credentials file schema mismatch — expected v1 shape',\n );\n }\n return result.data as Credentials;\n}\n","/**\n * Atomically rewrite the top-level \"clientId\" value in a ratio.config.jsonc\n * file. Preserves every comment, whitespace token, and unknown key.\n *\n * The file is treated as an opaque UTF-8 string: no JSON/JSONC parser is\n * involved, so comments survive verbatim. Exactly one targeted regex is\n * applied to the `\"clientId\": \"<value>\"` line; everything else is copied\n * byte-for-byte.\n *\n * Throws ValidationError if the file is not shaped like a canonical\n * ratio.config.jsonc (no top-level \"clientId\" key on its own line).\n */\n\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { ValidationError } from '../errors.js';\n\n/** Manifest file name expected in the app repo root. */\nexport const MANIFEST_FILE_NAME = 'ratio.config.jsonc';\n\n/** Targeted single-key rewrite regex for the clientId value. */\nconst CLIENT_ID_ROW = /^(\\s*\"clientId\"\\s*:\\s*\")([^\"]*)(\"\\s*,?)/m;\n\n/** Read-only extraction regex for the appName echo. Never used to write. */\nconst APP_NAME_ROW = /^\\s*\"appName\"\\s*:\\s*\"([^\"]*)\"/m;\n\nexport interface WriteClientIdResult {\n /** false when the file already had the same clientId. */\n written: boolean;\n /** Absolute path to the manifest file. */\n path: string;\n /** Parsed from the manifest's appName row, when present. */\n appName?: string;\n /** The value that was replaced, when it was non-empty and different. */\n previousClientId?: string;\n}\n\n/**\n * Locate `ratio.config.jsonc` under `cwd`, rewrite ONLY the top-level\n * clientId value, and persist via atomic tmp+rename with 0600 discipline\n * (mirrors the credential store's write contract). Same-value re-runs are\n * a no-op (`written: false`, file untouched).\n */\nexport async function writeClientId(\n cwd: string,\n clientId: string,\n): Promise<WriteClientIdResult> {\n const filePath = path.join(cwd, MANIFEST_FILE_NAME);\n\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf8');\n } catch {\n throw new ValidationError(\n `${MANIFEST_FILE_NAME} not found in ${cwd}.`,\n `Run this command from the app's repo root; ${MANIFEST_FILE_NAME} not found in ${cwd}.`,\n );\n }\n\n const appNameMatch = APP_NAME_ROW.exec(content);\n const appName = appNameMatch?.[1];\n\n const rowMatch = CLIENT_ID_ROW.exec(content);\n if (rowMatch === null) {\n throw new ValidationError(\n `${MANIFEST_FILE_NAME} has no top-level \"clientId\" entry.`,\n 'Restore the \"clientId\" line from the app template, then re-run.',\n );\n }\n\n const existing = rowMatch[2] ?? '';\n\n const base: WriteClientIdResult = {\n written: false,\n path: filePath,\n ...(appName !== undefined ? { appName } : {}),\n };\n\n if (existing === clientId) {\n return base;\n }\n\n // Function replacer: the new value is inserted verbatim, immune to `$`\n // pattern expansion in String.replace replacement strings.\n const updated = content.replace(\n CLIENT_ID_ROW,\n (_row, open: string, _old: string, close: string) =>\n `${open}${clientId}${close}`,\n );\n\n const tmpPath = `${filePath}.${process.pid}.${crypto\n .randomBytes(8)\n .toString('hex')}.tmp`;\n try {\n // mode at open time closes the create-then-chmod race window.\n fs.writeFileSync(tmpPath, updated, { mode: 0o600 });\n fs.renameSync(tmpPath, filePath);\n } catch (err) {\n try {\n fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup: the tmp file may never have been created.\n }\n throw err;\n }\n if (process.platform !== 'win32') {\n // Belt-and-braces: rename may preserve a prior destination mode.\n fs.chmodSync(filePath, 0o600);\n }\n\n return {\n ...base,\n written: true,\n ...(existing !== '' ? { previousClientId: existing } : {}),\n };\n}\n","import { Command, Errors } from '@oclif/core';\nimport { renderError } from './render-error.js';\n\n/**\n * Base class for all Ratio CLI commands.\n *\n * Overrides catch() to intercept every error thrown from run(), render it\n * per the Ratio CLI error-UX spec (typed hierarchy -> structured stderr/stdout,\n * --verbose for stack traces, --json for machine-readable envelope), and then\n * suppress oclif's own double-render by setting skipOclifErrorHandling on the\n * re-thrown error.\n *\n * Path B: base-class override — no oclif.hooks wiring needed.\n * oclif v4 does not expose a command_error hook; the reliable interception\n * point is Command.catch(), which is called by Command._run() before the\n * error bubbles up to oclif's global handle() function.\n *\n * Double-render prevention:\n * oclif's handle() checks err.skipOclifErrorHandling before printing.\n * After rendering we set skipOclifErrorHandling = true so oclif's\n * pretty-print is skipped. We also set err.oclif.exit = N so handle()\n * calls process.exit(N) with the correct code.\n *\n * Exit-code routing:\n * - exitCode === 0 (ComingSoonError): throw ExitError(0) so oclif calls\n * process.exit(0) cleanly. ExitError has code 'EEXIT' and is treated as\n * a clean exit — shouldPrint is false in handle().\n * - exitCode > 0: mark err.skipOclifErrorHandling = true and set\n * err.oclif = { exit: N }, then re-throw so handle() exits N.\n *\n * Verbose/json detection strategy:\n * We scan this.argv directly. this.argv is set by oclif's Command before\n * run() is called and always contains the raw argument list. This approach\n * works both for:\n * (a) post-parse errors (thrown after this.parse() succeeds) — argv has the\n * flags that were successfully recognised.\n * (b) pre-parse errors (thrown BY this.parse(), e.g. unknown flag) — argv\n * still contains --verbose / --json even though parse() threw.\n * Simple string scan is sufficient since --verbose and --json are boolean\n * flags with no value argument.\n */\nexport abstract class RatioCommand extends Command {\n private _getRenderFlags(): { verbose: boolean; json: boolean } {\n const argv = this.argv as string[];\n return {\n verbose: argv.includes('--verbose'),\n json: argv.includes('--json'),\n };\n }\n\n override async catch(\n err: Error & {\n exitCode?: number;\n oclif?: { exit?: number };\n skipOclifErrorHandling?: boolean;\n },\n ): Promise<never> {\n const { verbose, json } = this._getRenderFlags();\n\n const output = renderError({ err, verbose, json });\n\n // Write rendered output before any exit/throw\n if (output.stdout) process.stdout.write(output.stdout);\n if (output.stderr) process.stderr.write(output.stderr);\n\n if (output.exitCode === 0) {\n // ComingSoonError path — ExitError(0) causes oclif to call process.exit(0)\n // cleanly. ExitError has code 'EEXIT'; handle() treats it as a clean exit\n // (shouldPrint is false) so no additional output is written.\n throw new Errors.ExitError(0);\n }\n\n // For non-zero exits: suppress oclif's pretty-print by marking the error,\n // then route the correct exit code via oclif.exit so handle() calls process.exit(N).\n err.skipOclifErrorHandling = true;\n err.oclif = { exit: output.exitCode };\n throw err;\n }\n}\n","import { isRatioCLIError, RatioCLIError, ValidationError } from './errors.js';\n\nexport interface RenderInput {\n err: unknown;\n verbose: boolean;\n json: boolean;\n}\n\nexport interface RenderOutput {\n /** Text to write to stdout. Empty string means nothing to write. */\n stdout: string;\n /** Text to write to stderr. Empty string means nothing to write. */\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Detects whether an error originates from the oclif argument/flag parser.\n * oclif's CLIParseError subclasses (NonExistentFlagsError, RequiredArgsError,\n * etc.) all carry a `parse` property set by CLIParseErrorOptions. This is the\n * stable structural signal — more reliable than checking the class name string.\n */\nfunction isOclifParserError(err: unknown): err is Error & { parse: unknown } {\n return (\n err instanceof Error &&\n 'parse' in err &&\n err.parse !== null &&\n err.parse !== undefined\n );\n}\n\n/**\n * Builds the JSON error envelope. Fields with undefined values are omitted\n * so the consumer never sees `\"hint\": null` or `\"docsUrl\": null`.\n */\nfunction buildErrorEnvelope(\n code: string,\n message: string,\n hint?: string,\n docsUrl?: string,\n): string {\n const obj: Record<string, unknown> = { ok: false, code, message };\n if (hint !== undefined) obj['hint'] = hint;\n if (docsUrl !== undefined) obj['docsUrl'] = docsUrl;\n return JSON.stringify(obj) + '\\n';\n}\n\n/**\n * Pure rendering function — no side effects, no process.exit, no I/O.\n * The caller (RatioCommand.catch) is responsible for writing output and exiting.\n *\n * Rendering rules (all branches):\n *\n * | Error shape | --json | --verbose | stdout | stderr | exit |\n * |----------------------|--------|-----------|--------------------------------|----------------------------------|------|\n * | ComingSoonError | false | any | err.message + '\\n' | '' | 0 |\n * | ComingSoonError | true | any | '{\"ok\":true,\"data\":null}\\n' | '' | 0 |\n * | RatioCLIError >0 | false | false | '' | code: msg [+ hint] [+ docsUrl] | N |\n * | RatioCLIError >0 | false | true | '' | above + stack | N |\n * | RatioCLIError >0 | true | false | JSON envelope | '' | N |\n * | RatioCLIError >0 | true | true | JSON envelope | stack only | N |\n * | oclif ParserError | false | any | '' | VALIDATION_ERROR: msg [+stack] | 8 |\n * | oclif ParserError | true | any | JSON VALIDATION_ERROR envelope | stack if verbose | 8 |\n * | Plain Error / other | false | false | '' | INTERNAL: msg\\n | 1 |\n * | Plain Error / other | false | true | '' | INTERNAL: msg\\n + stack\\n | 1 |\n * | Plain Error / other | true | any | JSON INTERNAL envelope | stack if verbose | 1 |\n */\nexport function renderError(input: RenderInput): RenderOutput {\n const { err, verbose, json } = input;\n\n // ── Branch 1: our own typed hierarchy ───────────────────────────────────\n if (isRatioCLIError(err)) {\n const ratioErr = err as RatioCLIError;\n\n // exitCode === 0 → coming-soon: render on stdout, no stderr, exit 0\n if (ratioErr.exitCode === 0) {\n if (json) {\n return {\n stdout: JSON.stringify({ ok: true, data: null }) + '\\n',\n stderr: '',\n exitCode: 0,\n };\n }\n return {\n stdout: ratioErr.message + '\\n',\n stderr: '',\n exitCode: 0,\n };\n }\n\n // exitCode > 0 → error render\n let stderrLines = `${ratioErr.code}: ${ratioErr.message}\\n`;\n if (ratioErr.hint !== undefined) stderrLines += `${ratioErr.hint}\\n`;\n if (ratioErr.docsUrl !== undefined) stderrLines += `${ratioErr.docsUrl}\\n`;\n\n if (json) {\n return {\n stdout: buildErrorEnvelope(ratioErr.code, ratioErr.message, ratioErr.hint, ratioErr.docsUrl),\n stderr: verbose && ratioErr.stack ? ratioErr.stack + '\\n' : '',\n exitCode: ratioErr.exitCode,\n };\n }\n\n if (verbose && ratioErr.stack) {\n stderrLines += ratioErr.stack + '\\n';\n }\n\n return { stdout: '', stderr: stderrLines, exitCode: ratioErr.exitCode };\n }\n\n // ── Branch 2: oclif parser error (unknown flag, missing required arg, etc.) ──\n if (isOclifParserError(err)) {\n const parserErr = err as Error & { parse: unknown };\n const msg = parserErr.message || 'Unknown parse error';\n\n // Strip oclif's \"Unexpected argument: --foo\" or similar prefix noise where\n // possible, but keep the core message as-is since it's already human-readable.\n const coercedErr = new ValidationError(msg);\n\n let stderrText = `${coercedErr.code}: ${msg}\\n`;\n\n if (json) {\n return {\n stdout: buildErrorEnvelope(coercedErr.code, msg),\n stderr: verbose && parserErr.stack ? parserErr.stack + '\\n' : '',\n exitCode: 8,\n };\n }\n\n if (verbose && parserErr.stack) {\n stderrText += parserErr.stack + '\\n';\n }\n\n return { stdout: '', stderr: stderrText, exitCode: 8 };\n }\n\n // ── Branch 3: plain Error or non-Error throw ─────────────────────────────\n const message = err instanceof Error ? err.message : String(err);\n const stack = err instanceof Error ? err.stack : undefined;\n const stderrPrefix = `INTERNAL: ${message}\\n`;\n\n if (json) {\n return {\n stdout: buildErrorEnvelope('INTERNAL', message),\n stderr: verbose && stack ? stack + '\\n' : '',\n exitCode: 1,\n };\n }\n\n let stderrText = stderrPrefix;\n if (verbose && stack) {\n stderrText += stack + '\\n';\n }\n\n return { stdout: '', stderr: stderrText, exitCode: 1 };\n}\n","import { Flags } from '@oclif/core';\n\n/** Global verbose flag — every command imports and spreads this. */\nexport const verboseFlag = {\n verbose: Flags.boolean({\n description:\n 'Show diagnostic detail: HTTP status/timing lines for auth requests (stderr) and stack traces for internal errors.',\n }),\n};\n\n/** JSON envelope flag — read-style commands (whoami, dev trigger --list) import. */\nexport const jsonFlag = {\n json: Flags.boolean({\n description: 'Emit machine-readable JSON on stdout.',\n }),\n};\n"],"mappings":"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","names":["fs","Flags","z","base","crypto","fs","path","path","fs","crypto","stderrText","z","Flags","fs"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as _oclif_core_interfaces from '@oclif/core/interfaces';
|
|
2
|
+
import { RatioCommand } from '../../lib/ratio-command.js';
|
|
3
|
+
import { FileCredentialStore } from '../../lib/credentials/store.js';
|
|
4
|
+
import { VerboseTracer } from '../../lib/auth/verbose-trace.js';
|
|
5
|
+
import { BrowserOpener } from '../../lib/auth/browser.js';
|
|
6
|
+
import { StdinPrompt } from '../../lib/auth/manual-prompt.js';
|
|
7
|
+
import '@oclif/core';
|
|
8
|
+
import '../../lib/credentials/clock.js';
|
|
9
|
+
import '../../lib/credentials/refresher.js';
|
|
10
|
+
import '../../lib/credentials/types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Minimal spinner contract — satisfied by oclif's `ux.action`.
|
|
14
|
+
* Injectable so tests can assert start/stop without a real TTY.
|
|
15
|
+
* oclif's ux.action writes to STDERR and auto-degrades to plain
|
|
16
|
+
* lines on non-TTY / CI, so stdout (incl. --json consumers of other
|
|
17
|
+
* commands) is never corrupted.
|
|
18
|
+
*/
|
|
19
|
+
interface SpinnerLike {
|
|
20
|
+
start(msg: string): void;
|
|
21
|
+
stop(msg?: string): void;
|
|
22
|
+
}
|
|
23
|
+
declare class AuthLogin extends RatioCommand {
|
|
24
|
+
static description: string;
|
|
25
|
+
static examples: string[];
|
|
26
|
+
static args: {};
|
|
27
|
+
static flags: {
|
|
28
|
+
manual: _oclif_core_interfaces.BooleanFlag<boolean>;
|
|
29
|
+
profile: _oclif_core_interfaces.OptionFlag<string, _oclif_core_interfaces.CustomOptions>;
|
|
30
|
+
verbose: _oclif_core_interfaces.BooleanFlag<boolean>;
|
|
31
|
+
};
|
|
32
|
+
browserOpener: BrowserOpener;
|
|
33
|
+
stdinPrompt: StdinPrompt;
|
|
34
|
+
spinner: SpinnerLike;
|
|
35
|
+
/** Verbose diagnostic tracer — no-op unless --verbose; set in run(). */
|
|
36
|
+
trace: VerboseTracer;
|
|
37
|
+
run(): Promise<void>;
|
|
38
|
+
_runBrowserFlow(serverUrl: string, codeVerifier: string, codeChallenge: string, state: string, store: FileCredentialStore, profileName: string): Promise<void>;
|
|
39
|
+
_runManualFlow(serverUrl: string, codeVerifier: string, codeChallenge: string, state: string, store: FileCredentialStore, profileName: string): Promise<void>;
|
|
40
|
+
private _persistCredentials;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { type SpinnerLike, AuthLogin as default };
|