olakai-cli 0.6.1 → 0.6.3
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/dist/{chunk-PGRX347G.js → chunk-OHPX4STO.js} +9 -2
- package/dist/chunk-OHPX4STO.js.map +1 -0
- package/dist/index.js +85 -18
- package/dist/index.js.map +1 -1
- package/dist/{status-NK4PX2NS.js → status-CAHO5FHI.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-PGRX347G.js.map +0 -1
- /package/dist/{status-NK4PX2NS.js.map → status-CAHO5FHI.js.map} +0 -0
|
@@ -624,7 +624,14 @@ async function printClaudeCodeStatus(opts) {
|
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
|
-
} catch {
|
|
627
|
+
} catch (err) {
|
|
628
|
+
console.warn(
|
|
629
|
+
"\u26A0 Activity check unavailable \u2014 token may be revoked or network down."
|
|
630
|
+
);
|
|
631
|
+
if (process.env.OLAKAI_MONITOR_DEBUG === "1") {
|
|
632
|
+
const cause = err instanceof Error && err.cause && typeof err.cause === "object" ? err.cause.code ?? err.cause.message : err instanceof Error ? err.message : String(err);
|
|
633
|
+
console.warn(` cause: ${cause ?? "unknown"}`);
|
|
634
|
+
}
|
|
628
635
|
}
|
|
629
636
|
}
|
|
630
637
|
|
|
@@ -668,4 +675,4 @@ export {
|
|
|
668
675
|
getClaudeCodeStatus,
|
|
669
676
|
printClaudeCodeStatus
|
|
670
677
|
};
|
|
671
|
-
//# sourceMappingURL=chunk-
|
|
678
|
+
//# sourceMappingURL=chunk-OHPX4STO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/monitor/plugins/claude-code/status.ts","../src/lib/profiles.ts","../src/lib/config.ts","../src/lib/auth.ts","../src/monitor/plugins/claude-code/settings.ts","../src/monitor/plugins/claude-code/config.ts","../src/monitor/paths.ts","../src/monitor/migrations.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { StatusReport } from \"../../plugin.js\";\nimport { getValidToken } from \"../../../lib/auth.js\";\nimport { getBaseUrl } from \"../../../lib/config.js\";\nimport {\n OLAKAI_HOOK_MARKER,\n getSettingsPath,\n readJsonFile,\n type ClaudeSettings,\n} from \"./settings.js\";\nimport {\n getClaudeCodeConfigPath,\n loadClaudeCodeConfig,\n} from \"./config.js\";\n\nexport async function getClaudeCodeStatus(opts?: {\n projectRoot?: string;\n}): Promise<StatusReport> {\n const projectRoot = opts?.projectRoot ?? process.cwd();\n const configPath = getClaudeCodeConfigPath(projectRoot);\n const config = loadClaudeCodeConfig(projectRoot);\n\n const settings = readJsonFile<ClaudeSettings>(getSettingsPath(projectRoot));\n const hooksConfigured = settings?.hooks\n ? Object.values(settings.hooks).some((entries) =>\n entries.some((e) =>\n e.hooks.some((h) => h.command.includes(OLAKAI_HOOK_MARKER)),\n ),\n )\n : false;\n\n if (!config) {\n return {\n toolId: \"claude-code\",\n configured: false,\n hooksConfigured,\n configPath,\n notes: hooksConfigured\n ? [\"Hooks present in settings.json but no monitor config — re-run init.\"]\n : [],\n };\n }\n\n return {\n toolId: \"claude-code\",\n configured: true,\n hooksConfigured,\n agentId: config.agentId,\n agentName: config.agentName,\n source: config.source,\n apiKeyMasked: config.apiKey.slice(0, 12) + \"...\",\n monitoringEndpoint: config.monitoringEndpoint,\n configuredAt: config.createdAt,\n configPath,\n };\n}\n\n/**\n * Print a human-readable status summary for the Claude Code plugin\n * (non-JSON path). Also fetches recent activity if a token is\n * available — same behavior as pre-Stage-2 `monitor status`.\n */\nexport async function printClaudeCodeStatus(opts?: {\n projectRoot?: string;\n}): Promise<void> {\n const projectRoot = opts?.projectRoot ?? process.cwd();\n const status = await getClaudeCodeStatus({ projectRoot });\n\n if (!status.configured) {\n console.log(\"Monitoring is not configured for this workspace.\");\n console.log(\n \"Run 'olakai monitor init --tool claude-code' to set up monitoring.\",\n );\n process.exit(1);\n }\n\n const configRel = status.configPath\n ? path.relative(projectRoot, status.configPath)\n : \"(unknown)\";\n\n console.log(\"Olakai Monitor Status (Claude Code)\");\n console.log(\"===================================\");\n console.log(`Agent: ${status.agentName}`);\n console.log(`Agent ID: ${status.agentId}`);\n console.log(`API Key: ${status.apiKeyMasked}`);\n console.log(`Endpoint: ${status.monitoringEndpoint}`);\n console.log(`Source: ${status.source}`);\n console.log(`Configured: ${status.configuredAt}`);\n console.log(`Config file: ${configRel}`);\n console.log(\n `Hooks: ${status.hooksConfigured ? \"Active\" : \"Missing (re-run 'olakai monitor init --tool claude-code')\"}`,\n );\n\n try {\n const token = getValidToken();\n if (token && status.agentId) {\n const params = new URLSearchParams({\n agentId: status.agentId,\n limit: \"5\",\n });\n const response = await fetch(\n `${getBaseUrl()}/api/activity/prompts?${params}`,\n {\n headers: { Authorization: `Bearer ${token}` },\n },\n );\n if (response.ok) {\n const data = (await response.json()) as {\n prompts: Array<{ id: string; createdAt: string }>;\n };\n if (data.prompts && data.prompts.length > 0) {\n console.log(\"\");\n console.log(\"Recent Activity:\");\n for (const p of data.prompts) {\n console.log(` ${p.createdAt} ${p.id.slice(0, 12)}...`);\n }\n } else {\n console.log(\"\");\n console.log(\"No activity recorded yet.\");\n }\n }\n }\n } catch (err) {\n // Activity check is optional — don't fail the whole status command.\n // But never silently — a revoked token or DNS failure is exactly\n // what the user is trying to diagnose by running `status`. Surface\n // one actionable line on stderr, and a cause-detail line under\n // OLAKAI_MONITOR_DEBUG=1 for follow-up debugging.\n console.warn(\n \"\\u26a0 Activity check unavailable — token may be revoked or network down.\",\n );\n if (process.env.OLAKAI_MONITOR_DEBUG === \"1\") {\n const cause =\n err instanceof Error && err.cause && typeof err.cause === \"object\"\n ? ((err.cause as { code?: unknown }).code as string | undefined) ??\n (err.cause as { message?: unknown }).message\n : err instanceof Error\n ? err.message\n : String(err);\n console.warn(` cause: ${cause ?? \"unknown\"}`);\n }\n }\n}\n","/**\n * AWS-style profile manager for the Olakai CLI.\n *\n * Two configuration surfaces:\n *\n * 1. Global profile registry — `~/.config/olakai/profiles.json`\n * Stores one or more named profiles, each carrying a host + token\n * + (optionally) user/account identifiers. Owner-readable only\n * (file `0600`, parent dir `0700`).\n *\n * 2. Per-workspace pointer — `.olakai/config.json` in the workspace\n * root (the CWD or any ancestor). Either selects a profile by\n * name (`{ \"profile\": \"saas-prod\" }`) or pins an ad-hoc host\n * (`{ \"host\": \"https://...\" }`).\n *\n * Profile resolution order (highest precedence first):\n *\n * 1. `--profile` CLI flag (via `setProfileOverride()`)\n * 2. `OLAKAI_PROFILE` env var\n * 3. Workspace config `profile` field\n * 4. `profiles.json:default`\n *\n * Host resolution order:\n *\n * 1. `--host` flag / `OLAKAI_HOST` env (handled in `config.ts`)\n * 2. Workspace config `host` field (ad-hoc override)\n * 3. Resolved profile's `host`\n * 4. Named environment fallback (handled in `config.ts`)\n *\n * Migration: on first profile-manager read, if a legacy\n * `~/.config/olakai/credentials.json` exists and `profiles.json` does\n * NOT, the legacy credentials are imported as a single profile named\n * `\"default\"` and marked as the default. The legacy file is left in\n * place for one release as a non-destructive safety net; see\n * `migrateLegacyCredentialsIfNeeded()` below.\n */\n\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { Environment } from \"./config.js\";\n\nexport const PROFILES_FILE_VERSION = 1;\n\nexport const HOSTS: Record<Environment, string> = {\n production: \"https://app.olakai.ai\",\n staging: \"https://staging.app.olakai.ai\",\n local: \"http://localhost:3000\",\n};\n\n/**\n * One profile record. `host` is the only required runtime field; the\n * rest are filled in after login. Identifiers (`userId`, `accountId`)\n * are cached locally so `whoami` and similar commands don't have to\n * make a network call to know which account a profile belongs to.\n */\nexport interface Profile {\n host: string;\n token?: string;\n expiresAt?: number; // Unix timestamp in seconds\n userId?: string;\n accountId?: string;\n email?: string;\n}\n\nexport interface ProfilesFile {\n version: number;\n default?: string;\n profiles: Record<string, Profile>;\n}\n\nexport interface WorkspaceConfig {\n profile?: string;\n host?: string;\n}\n\n/**\n * Resolved view of the profile actually in effect for the current\n * command invocation. Exposed to consumers (whoami, login, the API\n * client) so they can read the active host/token without re-walking\n * the resolution chain.\n */\nexport interface ResolvedProfile {\n /** Name of the profile that won resolution. */\n name: string;\n /** The profile record itself (host/token/etc). */\n profile: Profile;\n /**\n * Effective host. May differ from `profile.host` if the workspace\n * config or `--host` overrode it; in that case the profile's token\n * is still used but the host comes from the override.\n */\n host: string;\n /** True when the host was overridden by workspace config (`host`). */\n hostFromWorkspaceConfig: boolean;\n /** Where the profile name itself came from. */\n source: \"flag\" | \"env\" | \"workspace\" | \"default\";\n}\n\n// ---- module state -----------------------------------------------------------\n\nlet profileOverride: string | undefined;\n\n/** Set from the `--profile` global flag in the CLI preAction hook. */\nexport function setProfileOverride(name: string | undefined): void {\n profileOverride = name && name.length > 0 ? name : undefined;\n}\n\nexport function getProfileOverride(): string | undefined {\n return profileOverride;\n}\n\n// ---- paths ------------------------------------------------------------------\n\nfunction getConfigDir(home: string = os.homedir()): string {\n return path.join(home, \".config\", \"olakai\");\n}\n\nexport function getProfilesPath(home: string = os.homedir()): string {\n return path.join(getConfigDir(home), \"profiles.json\");\n}\n\nexport function getLegacyCredentialsPath(home: string = os.homedir()): string {\n return path.join(getConfigDir(home), \"credentials.json\");\n}\n\nfunction ensureConfigDir(home: string = os.homedir()): void {\n const dir = getConfigDir(home);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n } else {\n // Best-effort tighten — ignore failures (e.g. on Windows).\n try {\n fs.chmodSync(dir, 0o700);\n } catch {\n // ignore\n }\n }\n}\n\n// ---- profiles.json IO -------------------------------------------------------\n\nfunction emptyProfilesFile(): ProfilesFile {\n return { version: PROFILES_FILE_VERSION, profiles: {} };\n}\n\nfunction readProfilesFileRaw(home: string = os.homedir()): ProfilesFile | null {\n const file = getProfilesPath(home);\n if (!fs.existsSync(file)) return null;\n try {\n const content = fs.readFileSync(file, \"utf-8\");\n const parsed = JSON.parse(content) as Partial<ProfilesFile>;\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof parsed.profiles !== \"object\" ||\n parsed.profiles === null\n ) {\n return null;\n }\n return {\n version:\n typeof parsed.version === \"number\"\n ? parsed.version\n : PROFILES_FILE_VERSION,\n default: typeof parsed.default === \"string\" ? parsed.default : undefined,\n profiles: parsed.profiles as Record<string, Profile>,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Read the profiles registry, applying lazy migration from a legacy\n * `credentials.json` on first read. Always returns a valid object;\n * returns the empty registry when no on-disk state exists.\n */\nexport function readProfilesFile(home: string = os.homedir()): ProfilesFile {\n migrateLegacyCredentialsIfNeeded(home);\n return readProfilesFileRaw(home) ?? emptyProfilesFile();\n}\n\n/**\n * Atomically write the profiles registry to disk with `0600` perms.\n * Parent directory is created with `0700` perms if missing.\n */\nexport function writeProfilesFile(\n data: ProfilesFile,\n home: string = os.homedir(),\n): void {\n ensureConfigDir(home);\n const file = getProfilesPath(home);\n // Per-invocation tmp filename to avoid corruption when two CLI\n // processes race on the same registry (e.g. concurrent `olakai\n // login` flows). PID + random suffix is overkill in practice but\n // costs ~nothing.\n const tmp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 0o600 });\n fs.renameSync(tmp, file);\n // `renameSync` preserves the tmp file's mode but be defensive on\n // platforms where it might not — re-apply 0600 explicitly.\n try {\n fs.chmodSync(file, 0o600);\n } catch {\n // ignore\n }\n}\n\n// ---- legacy migration -------------------------------------------------------\n\ninterface LegacyCredentials {\n token: string;\n expiresAt: number;\n environment: Environment;\n}\n\n/**\n * If `profiles.json` is missing and a legacy `credentials.json` exists,\n * import the legacy credentials as a profile named `\"default\"` and set\n * it as the default. Idempotent — a no-op if `profiles.json` already\n * exists.\n *\n * The legacy `credentials.json` is intentionally LEFT IN PLACE for one\n * release. Once we ship this in olakai-cli ≥ N+1, we should add a\n * follow-up that deletes the legacy file post-migration. (Tracking in\n * OLA-213.)\n */\nexport function migrateLegacyCredentialsIfNeeded(\n home: string = os.homedir(),\n): void {\n const profilesPath = getProfilesPath(home);\n if (fs.existsSync(profilesPath)) return;\n\n const legacyPath = getLegacyCredentialsPath(home);\n if (!fs.existsSync(legacyPath)) return;\n\n let legacy: LegacyCredentials;\n try {\n const raw = fs.readFileSync(legacyPath, \"utf-8\");\n legacy = JSON.parse(raw) as LegacyCredentials;\n } catch {\n // Unparseable legacy file — skip migration silently rather than\n // crashing every command. User can run `olakai login` to start\n // fresh.\n return;\n }\n\n if (\n typeof legacy?.token !== \"string\" ||\n typeof legacy?.expiresAt !== \"number\" ||\n typeof legacy?.environment !== \"string\"\n ) {\n return;\n }\n\n const host = HOSTS[legacy.environment] ?? HOSTS.production;\n\n const migrated: ProfilesFile = {\n version: PROFILES_FILE_VERSION,\n default: \"default\",\n profiles: {\n default: {\n host,\n token: legacy.token,\n expiresAt: legacy.expiresAt,\n },\n },\n };\n\n try {\n writeProfilesFile(migrated, home);\n } catch {\n // Migration is best-effort — failure to write should not break\n // the calling command. Subsequent commands will retry.\n }\n}\n\n// ---- workspace config -------------------------------------------------------\n\nconst WORKSPACE_CONFIG_FILENAME = \"config.json\";\nconst WORKSPACE_CONFIG_DIR = \".olakai\";\n\n/**\n * Walk up from `startDir` looking for a `.olakai/config.json` file.\n * Stops at filesystem root or at the user's home directory (whichever\n * comes first) so we don't accidentally pick up a config from above\n * `$HOME`.\n */\nexport function findWorkspaceConfigPath(\n startDir: string = process.cwd(),\n home: string = os.homedir(),\n): string | null {\n let current = path.resolve(startDir);\n const root = path.parse(current).root;\n\n // Allow finding a config in HOME itself, but don't escape above HOME\n // by default. If `startDir` is outside HOME (e.g. a system path),\n // walk all the way to root.\n const stopAtHome = current.startsWith(home);\n\n // We bound the loop on `current` reaching root to guarantee termination.\n while (true) {\n const candidate = path.join(current, WORKSPACE_CONFIG_DIR, WORKSPACE_CONFIG_FILENAME);\n if (fs.existsSync(candidate)) return candidate;\n\n if (current === root) return null;\n if (stopAtHome && current === home) return null;\n\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nexport function readWorkspaceConfig(\n startDir: string = process.cwd(),\n home: string = os.homedir(),\n): WorkspaceConfig | null {\n const file = findWorkspaceConfigPath(startDir, home);\n if (!file) return null;\n try {\n const raw = fs.readFileSync(file, \"utf-8\");\n const parsed = JSON.parse(raw) as Partial<WorkspaceConfig>;\n return {\n profile:\n typeof parsed?.profile === \"string\" && parsed.profile.length > 0\n ? parsed.profile\n : undefined,\n host:\n typeof parsed?.host === \"string\" && parsed.host.length > 0\n ? parsed.host\n : undefined,\n };\n } catch {\n return null;\n }\n}\n\n// ---- resolution -------------------------------------------------------------\n\nfunction normalizeHost(host: string): string {\n const withScheme = /^https?:\\/\\//i.test(host) ? host : `https://${host}`;\n return withScheme.replace(/\\/+$/, \"\");\n}\n\n/**\n * Resolve the profile name to use for this invocation, given the\n * precedence rules. Returns `null` only when no profile is configured\n * at all (registry empty AND no env/flag/workspace pointer).\n */\nexport function resolveProfileName(\n startDir: string = process.cwd(),\n home: string = os.homedir(),\n): { name: string; source: ResolvedProfile[\"source\"] } | null {\n // 1. --profile flag\n if (profileOverride) {\n return { name: profileOverride, source: \"flag\" };\n }\n // 2. OLAKAI_PROFILE env\n const envProfile = process.env.OLAKAI_PROFILE?.trim();\n if (envProfile) {\n return { name: envProfile, source: \"env\" };\n }\n // 3. Workspace config\n const workspace = readWorkspaceConfig(startDir, home);\n if (workspace?.profile) {\n return { name: workspace.profile, source: \"workspace\" };\n }\n // 4. profiles.default\n const file = readProfilesFile(home);\n if (file.default) {\n return { name: file.default, source: \"default\" };\n }\n return null;\n}\n\n/**\n * Resolve the active profile + host for the current invocation, or\n * null if no profile is configured. The returned object's `host` is\n * already normalized (trailing slash stripped, scheme prepended).\n *\n * Throws when the user EXPLICITLY requested a profile (via `--profile`,\n * `OLAKAI_PROFILE`, or workspace config) that doesn't exist in the\n * registry. Callers in flows that legitimately create profiles on the\n * fly (e.g. `olakai login --profile new-name`) should use\n * `tryResolveActiveProfile` instead.\n */\nexport function resolveActiveProfile(\n startDir: string = process.cwd(),\n home: string = os.homedir(),\n): ResolvedProfile | null {\n const resolved = resolveProfileName(startDir, home);\n if (!resolved) return null;\n\n const file = readProfilesFile(home);\n const profile = file.profiles[resolved.name];\n if (!profile) {\n // Name was explicitly requested but the profile doesn't exist.\n // Surface this with a clear error so the caller can react.\n throw new Error(\n `Profile \"${resolved.name}\" not found. Run 'olakai profiles list' to see available profiles.`,\n );\n }\n\n const workspace = readWorkspaceConfig(startDir, home);\n let host = profile.host;\n let hostFromWorkspaceConfig = false;\n if (workspace?.host) {\n host = workspace.host;\n hostFromWorkspaceConfig = true;\n }\n\n return {\n name: resolved.name,\n profile,\n host: normalizeHost(host),\n hostFromWorkspaceConfig,\n source: resolved.source,\n };\n}\n\n/**\n * Lenient sibling of `resolveActiveProfile`: returns `null` BOTH when\n * no profile is configured AND when the requested profile doesn't yet\n * exist. Use this in flows that may legitimately need to operate\n * before the profile is created — notably `olakai login --profile new`\n * and the internal `getBaseUrl()` resolver (which must work during\n * first-login).\n */\nexport function tryResolveActiveProfile(\n startDir: string = process.cwd(),\n home: string = os.homedir(),\n): ResolvedProfile | null {\n try {\n return resolveActiveProfile(startDir, home);\n } catch {\n return null;\n }\n}\n\n// ---- mutators ---------------------------------------------------------------\n\n/**\n * Update (or insert) a profile and persist. If `setDefault` is true OR\n * the registry has no default yet, the profile is marked as default.\n */\nexport function upsertProfile(\n name: string,\n profile: Profile,\n options: { setDefault?: boolean } = {},\n home: string = os.homedir(),\n): ProfilesFile {\n const file = readProfilesFile(home);\n file.profiles[name] = profile;\n if (options.setDefault || !file.default) {\n file.default = name;\n }\n writeProfilesFile(file, home);\n return file;\n}\n\n/**\n * Patch fields of an existing profile (or create it with the patch\n * applied). Preserves any existing fields not present in `patch`.\n */\nexport function patchProfile(\n name: string,\n patch: Partial<Profile>,\n home: string = os.homedir(),\n): Profile {\n const file = readProfilesFile(home);\n const existing = file.profiles[name];\n const merged: Profile = {\n host: patch.host ?? existing?.host ?? HOSTS.production,\n token: patch.token ?? existing?.token,\n expiresAt: patch.expiresAt ?? existing?.expiresAt,\n userId: patch.userId ?? existing?.userId,\n accountId: patch.accountId ?? existing?.accountId,\n email: patch.email ?? existing?.email,\n };\n file.profiles[name] = merged;\n if (!file.default) {\n file.default = name;\n }\n writeProfilesFile(file, home);\n return merged;\n}\n\n/**\n * Remove `name` from the registry. Throws if the profile is the\n * current default — callers should switch the default first.\n */\nexport function removeProfile(\n name: string,\n home: string = os.homedir(),\n): void {\n const file = readProfilesFile(home);\n if (!file.profiles[name]) {\n throw new Error(`Profile \"${name}\" does not exist.`);\n }\n if (file.default === name) {\n throw new Error(\n `Cannot remove profile \"${name}\" because it is the active default. ` +\n `Switch the default first with 'olakai profiles use <other>'.`,\n );\n }\n delete file.profiles[name];\n writeProfilesFile(file, home);\n}\n\n/**\n * Set `name` as the registry's default profile. Throws if `name`\n * doesn't exist.\n */\nexport function setDefaultProfile(\n name: string,\n home: string = os.homedir(),\n): void {\n const file = readProfilesFile(home);\n if (!file.profiles[name]) {\n throw new Error(\n `Profile \"${name}\" does not exist. Run 'olakai login --profile ${name}' first.`,\n );\n }\n file.default = name;\n writeProfilesFile(file, home);\n}\n\n/**\n * Clear stored token fields on a profile (but keep the profile itself\n * around so the host + identifiers survive a logout).\n */\nexport function clearProfileToken(\n name: string,\n home: string = os.homedir(),\n): void {\n const file = readProfilesFile(home);\n const profile = file.profiles[name];\n if (!profile) return;\n delete profile.token;\n delete profile.expiresAt;\n writeProfilesFile(file, home);\n}\n","import { tryResolveActiveProfile } from \"./profiles.js\";\n\nexport type Environment = \"production\" | \"staging\" | \"local\";\n\nconst HOSTS: Record<Environment, string> = {\n production: \"https://app.olakai.ai\",\n staging: \"https://staging.app.olakai.ai\",\n local: \"http://localhost:3000\",\n};\n\n// CLI client identifier\nexport const CLIENT_ID = \"olakai-cli\";\n\nlet currentEnvironment: Environment = \"production\";\nlet hostOverride: string | undefined;\n\n/**\n * Set the current environment\n */\nexport function setEnvironment(env: Environment): void {\n currentEnvironment = env;\n}\n\n/**\n * Set an explicit host override (e.g. for on-prem deployments).\n * Accepts a hostname only (\"olakai.acme.com\") or a full URL.\n * Pass `undefined` to clear the override.\n */\nexport function setHostOverride(host: string | undefined): void {\n hostOverride = host;\n}\n\n/**\n * Get the current environment from:\n * 1. Environment variable OLAKAI_ENV\n * 2. Programmatically set value\n * 3. Default to \"production\"\n */\nexport function getEnvironment(): Environment {\n const envVar = process.env.OLAKAI_ENV as Environment | undefined;\n if (envVar && isValidEnvironment(envVar)) {\n return envVar;\n }\n return currentEnvironment;\n}\n\n/**\n * Normalize a user-supplied host value into a base URL.\n * Accepts either a full URL (\"https://olakai.acme.com\") or a bare hostname\n * (\"olakai.acme.com\"); for bare hostnames `https://` is prepended.\n * Trailing slashes are stripped so callers can safely append paths.\n */\nfunction normalizeHost(host: string): string {\n const withScheme = /^https?:\\/\\//i.test(host) ? host : `https://${host}`;\n return withScheme.replace(/\\/+$/, \"\");\n}\n\n/**\n * Get the API base URL.\n *\n * Resolution precedence:\n * 1. Programmatic host override (`setHostOverride()`, set from `--host` flag)\n * 2. `OLAKAI_HOST` env var (on-prem deployments)\n * 3. Active profile's host (workspace config `host` > profile `host`)\n * 4. Named environment via `OLAKAI_ENV` / `--env` (production, staging, local)\n *\n * The profile lookup is loaded lazily to avoid a hard dependency cycle\n * between this module (used by `profiles.ts`) and `profiles.ts` itself.\n */\nexport function getBaseUrl(): string {\n const override = hostOverride?.trim();\n if (override) {\n return normalizeHost(override);\n }\n const envHost = process.env.OLAKAI_HOST?.trim();\n if (envHost) {\n return normalizeHost(envHost);\n }\n\n // Profile-aware host (workspace config overrides profile.host, but\n // both are below --host / OLAKAI_HOST). We use the LENIENT resolver\n // here because `getBaseUrl()` runs during first-login flows where\n // the requested profile name might not exist yet (e.g.\n // `olakai login --profile brand-new`). Dedicated `olakai profiles\n // ...` commands surface the not-found error directly. A null return\n // here just means \"fall through to the named-env default\".\n const active = tryResolveActiveProfile();\n if (active) {\n return active.host;\n }\n\n return HOSTS[getEnvironment()];\n}\n\n/**\n * Check if a string is a valid environment\n */\nexport function isValidEnvironment(env: string): env is Environment {\n return env === \"production\" || env === \"staging\" || env === \"local\";\n}\n\n/**\n * Get list of valid environments for CLI help\n */\nexport function getValidEnvironments(): Environment[] {\n return [\"production\", \"staging\", \"local\"];\n}\n","/**\n * Token storage shim that delegates to the profile manager.\n *\n * Historically the CLI stored a single token at\n * `~/.config/olakai/credentials.json`. As of OLA-213 we manage one or\n * more named profiles in `~/.config/olakai/profiles.json` (each with\n * its own token). This module preserves the original\n * `loadToken / isTokenValid / saveToken / clearToken / getValidToken`\n * API so the dozens of callers across `src/commands/` and\n * `src/lib/api.ts` can keep working without per-call refactors.\n *\n * All operations resolve against the *active* profile (the one chosen\n * by `--profile` / `OLAKAI_PROFILE` / workspace config / registry\n * default). Saving a token mutates the active profile's stored token;\n * clearing it removes only the token fields, leaving the host +\n * cached identifiers in place.\n */\n\nimport { getBaseUrl, getEnvironment, type Environment } from \"./config.js\";\nimport {\n HOSTS,\n clearProfileToken,\n readProfilesFile,\n resolveActiveProfile,\n resolveProfileName,\n writeProfilesFile,\n} from \"./profiles.js\";\n\nexport interface StoredCredentials {\n token: string;\n expiresAt: number; // Unix timestamp in seconds\n /**\n * Legacy field kept for callers that still want a coarse-grained\n * environment label. Populated from the active profile's host —\n * falls back to the active CLI environment when the profile's host\n * doesn't match a known named env.\n */\n environment: Environment;\n}\n\nfunction hostToEnvironment(host: string): Environment {\n for (const env of [\"production\", \"staging\", \"local\"] as const) {\n if (HOSTS[env] === host) return env;\n }\n return getEnvironment();\n}\n\n/**\n * Save an access token to disk under the active profile.\n *\n * If `--profile`/env/workspace selected a profile that doesn't exist\n * yet, it is created on the spot (typical first-login flow). If\n * nothing was selected, the token is written to a profile named\n * `\"default\"` and that becomes the registry default — preserving the\n * pre-profile-manager UX where `olakai login` Just Worked.\n */\nexport function saveToken(token: string, expiresIn: number): void {\n const expiresAt = Math.floor(Date.now() / 1000) + expiresIn;\n\n // Determine target profile name + host. `resolveProfileName` doesn't\n // require the profile to already exist (vs `resolveActiveProfile`\n // which throws).\n const resolved = resolveProfileName();\n const name = resolved?.name ?? \"default\";\n const host = getBaseUrl();\n\n const file = readProfilesFile();\n const existing = file.profiles[name];\n file.profiles[name] = {\n host,\n token,\n expiresAt,\n userId: existing?.userId,\n accountId: existing?.accountId,\n email: existing?.email,\n };\n // Set as the default profile when:\n // - There is no default yet, OR\n // - The caller didn't explicitly request a profile (so the new\n // login should land on the registry's default).\n if (!file.default || !resolved) {\n file.default = name;\n }\n writeProfilesFile(file);\n}\n\n/**\n * Load the credentials for the active profile, in the legacy shape so\n * existing callers (esp. `whoami` and `api.ts`) keep compiling. Returns\n * null if no profile is configured or the active profile has no token.\n */\nexport function loadToken(): StoredCredentials | null {\n let active;\n try {\n active = resolveActiveProfile();\n } catch {\n // Requested profile doesn't exist — treat as \"not logged in\" for\n // the legacy callers. The dedicated profile commands surface the\n // error directly.\n return null;\n }\n if (!active?.profile.token || !active.profile.expiresAt) return null;\n return {\n token: active.profile.token,\n expiresAt: active.profile.expiresAt,\n environment: hostToEnvironment(active.host),\n };\n}\n\n/**\n * Clear the token on the active profile (but keep the profile record\n * itself so the host + identifiers survive a logout).\n */\nexport function clearToken(): void {\n let resolved;\n try {\n resolved = resolveActiveProfile();\n } catch {\n return;\n }\n if (!resolved) return;\n clearProfileToken(resolved.name);\n}\n\n/**\n * Check whether the active profile holds a non-expired token. A 60s\n * skew is applied so callers don't ship a request with a token that's\n * about to expire mid-flight.\n */\nexport function isTokenValid(): boolean {\n const credentials = loadToken();\n if (!credentials) return false;\n\n // 60 second skew to avoid the API rejecting a token that's about to\n // expire.\n const now = Math.floor(Date.now() / 1000);\n if (credentials.expiresAt <= now + 60) return false;\n\n return true;\n}\n\n/** Convenience: return the active token if it's valid, else null. */\nexport function getValidToken(): string | null {\n if (!isTokenValid()) return null;\n return loadToken()?.token ?? null;\n}\n","/**\n * Claude Code `settings.json` hook-block helpers.\n *\n * Pure functions extracted out of the original `monitor.ts` so they\n * remain unit-testable without touching the filesystem. The dispatcher\n * is owned by `install.ts` / `uninstall.ts`.\n */\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nexport const CLAUDE_DIR = \".claude\";\nexport const SETTINGS_FILE = \"settings.json\";\n\n/**\n * Substring used to identify Olakai-installed hook commands inside an\n * existing Claude `settings.json`. Both stop and subagent-stop entries\n * carry this marker as a prefix of the command string.\n */\nexport const OLAKAI_HOOK_MARKER = \"olakai monitor hook\";\n\nexport interface HookCommand {\n type: string;\n command: string;\n}\n\nexport interface HookMatcherEntry {\n matcher: string;\n hooks: HookCommand[];\n}\n\nexport interface ClaudeSettings {\n hooks?: Record<string, HookMatcherEntry[]>;\n [key: string]: unknown;\n}\n\nexport const HOOK_DEFINITIONS: Record<string, HookMatcherEntry[]> = {\n Stop: [\n {\n matcher: \"\",\n hooks: [\n {\n type: \"command\",\n command: \"olakai monitor hook stop\",\n },\n ],\n },\n ],\n SubagentStop: [\n {\n matcher: \"\",\n hooks: [\n {\n type: \"command\",\n command: \"olakai monitor hook subagent-stop\",\n },\n ],\n },\n ],\n};\n\n/**\n * Layer the Olakai default hook definitions onto an existing hooks\n * block. See the original docstring in `monitor.ts` for the merge\n * rules — preserved verbatim during the Stage 2 refactor.\n */\nexport function mergeHooksSettings(\n existing: Record<string, HookMatcherEntry[]> | undefined,\n definitions: Record<string, HookMatcherEntry[]> = HOOK_DEFINITIONS,\n): Record<string, HookMatcherEntry[]> {\n const merged: Record<string, HookMatcherEntry[]> = {\n ...(existing ?? {}),\n };\n\n for (const [event, defaultEntries] of Object.entries(definitions)) {\n const existingEntries = merged[event] ?? [];\n const hasOlakaiHook = existingEntries.some((e) =>\n e.hooks.some((h) => h.command.includes(OLAKAI_HOOK_MARKER)),\n );\n\n if (hasOlakaiHook) {\n merged[event] = existingEntries;\n } else {\n merged[event] = [...existingEntries, ...defaultEntries];\n }\n }\n\n return merged;\n}\n\nexport function getClaudeDir(projectRoot: string): string {\n return path.join(projectRoot, CLAUDE_DIR);\n}\n\nexport function getSettingsPath(projectRoot: string): string {\n return path.join(getClaudeDir(projectRoot), SETTINGS_FILE);\n}\n\nexport function readJsonFile<T>(filePath: string): T | null {\n try {\n if (!fs.existsSync(filePath)) return null;\n const content = fs.readFileSync(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nexport function writeJsonFile(filePath: string, data: unknown): void {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + \"\\n\", \"utf-8\");\n}\n","/**\n * Per-tool monitor config storage for Claude Code. Lives at\n * `.olakai/monitor-claude-code.json` (post-Stage-2). Pre-Stage-2 the\n * file lived at `.claude/olakai-monitor.json`; the auto-migration\n * shim in `monitor/migrations.ts` upgrades old workspaces transparently.\n */\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n getMonitorConfigPath as resolveMonitorConfigPath,\n getOlakaiDir,\n} from \"../../paths.js\";\nimport { migrateLegacyClaudeConfigIfNeeded } from \"../../migrations.js\";\n\nexport interface MonitorConfig {\n agentId: string;\n apiKey: string;\n agentName: string;\n source: string;\n createdAt: string;\n monitoringEndpoint: string;\n}\n\nexport function getClaudeCodeConfigPath(projectRoot: string): string {\n return resolveMonitorConfigPath(projectRoot, \"claude-code\");\n}\n\n/**\n * Load the Claude Code monitor config. Migrates legacy\n * `.claude/olakai-monitor.json` to the new path on first read when\n * needed. `notify` is plumbed through so the hook path can pass a\n * no-op (silent), while init/status/disable use stderr.\n */\nexport function loadClaudeCodeConfig(\n projectRoot: string,\n notify?: (msg: string) => void,\n): MonitorConfig | null {\n migrateLegacyClaudeConfigIfNeeded(projectRoot, notify);\n const filePath = getClaudeCodeConfigPath(projectRoot);\n try {\n if (!fs.existsSync(filePath)) return null;\n const raw = fs.readFileSync(filePath, \"utf-8\");\n return JSON.parse(raw) as MonitorConfig;\n } catch {\n return null;\n }\n}\n\nexport function writeClaudeCodeConfig(\n projectRoot: string,\n config: MonitorConfig,\n): void {\n const filePath = getClaudeCodeConfigPath(projectRoot);\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n // chmod failures are non-fatal — Windows / unusual filesystems\n }\n}\n\nexport function deleteClaudeCodeConfig(projectRoot: string): boolean {\n const filePath = getClaudeCodeConfigPath(projectRoot);\n if (!fs.existsSync(filePath)) return false;\n try {\n fs.unlinkSync(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Path to the directory that the new config lives in. Exposed for\n * messages like \"config saved to <dir>\".\n */\nexport function getOlakaiConfigDir(projectRoot: string): string {\n return getOlakaiDir(projectRoot);\n}\n","/**\n * Per-tool config-path resolver. Centralized so plugins, the\n * auto-migration shim, and the hook dispatcher all agree on where each\n * tool's config lives.\n *\n * Layout:\n * .olakai/\n * monitor-claude-code.json\n * monitor-codex.json\n * monitor-cursor.json\n *\n * Pre-Stage-2 layout (Claude Code only):\n * .claude/\n * olakai-monitor.json <-- migrated to the new path on first read\n */\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ToolId } from \"./plugin.js\";\n\nexport const OLAKAI_DIR = \".olakai\";\nexport const LEGACY_CLAUDE_DIR = \".claude\";\nexport const LEGACY_CLAUDE_CONFIG_FILE = \"olakai-monitor.json\";\n\nexport function getOlakaiDir(projectRoot: string): string {\n return path.join(projectRoot, OLAKAI_DIR);\n}\n\nexport function getMonitorConfigFileName(toolId: ToolId): string {\n return `monitor-${toolId}.json`;\n}\n\nexport function getMonitorConfigPath(\n projectRoot: string,\n toolId: ToolId,\n): string {\n return path.join(getOlakaiDir(projectRoot), getMonitorConfigFileName(toolId));\n}\n\nexport function getLegacyClaudeMonitorConfigPath(projectRoot: string): string {\n return path.join(\n projectRoot,\n LEGACY_CLAUDE_DIR,\n LEGACY_CLAUDE_CONFIG_FILE,\n );\n}\n\n/**\n * Walk up from `startDir` looking for ANY indicator that this tree was\n * configured by `olakai monitor init`. Used by the hook dispatcher to\n * locate the configured workspace independently of the calling\n * process's CWD (Claude Code, Codex, and Cursor all reserve the right\n * to spawn hooks from arbitrary directories — see INV-002).\n *\n * Returns the closest ancestor directory containing either the new\n * `.olakai/monitor-<tool>.json` file (for any tool) OR the legacy\n * `.claude/olakai-monitor.json` file. Returns null when nothing is\n * found — callers MUST treat that as silent-exit.\n *\n * Exported for unit tests.\n */\nexport function findConfiguredWorkspace(\n startDir: string,\n toolIds: readonly ToolId[],\n): string | null {\n let dir = startDir;\n while (true) {\n if (hasConfiguredMonitorIn(dir, toolIds)) {\n return dir;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction hasConfiguredMonitorIn(\n dir: string,\n toolIds: readonly ToolId[],\n): boolean {\n for (const toolId of toolIds) {\n if (fs.existsSync(getMonitorConfigPath(dir, toolId))) {\n return true;\n }\n }\n if (fs.existsSync(getLegacyClaudeMonitorConfigPath(dir))) {\n return true;\n }\n return false;\n}\n","/**\n * Auto-migration: pre-Stage-2 layout\n * .claude/olakai-monitor.json\n * to the new per-tool layout\n * .olakai/monitor-claude-code.json\n *\n * Trigger: any read path that asks for the Claude Code monitor config.\n * If the new path is missing AND the legacy path exists, we copy the\n * legacy file's contents to the new location (preserving fields and\n * 0600 permissions) and emit a one-line notice via `notify`.\n *\n * The legacy file is intentionally NOT deleted — users may have it in\n * automation, and leaving it in place gives a clear rollback path.\n */\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport {\n getLegacyClaudeMonitorConfigPath,\n getMonitorConfigPath,\n getOlakaiDir,\n LEGACY_CLAUDE_CONFIG_FILE,\n LEGACY_CLAUDE_DIR,\n OLAKAI_DIR,\n} from \"./paths.js\";\n\nexport type MigrationNotifier = (message: string) => void;\n\nconst defaultNotifier: MigrationNotifier = (msg) => {\n console.error(msg);\n};\n\n/**\n * If a legacy `.claude/olakai-monitor.json` exists at `projectRoot`\n * and the new `.olakai/monitor-claude-code.json` does not, copy the\n * legacy content to the new path. Returns true when a migration\n * happened, false otherwise. Never throws.\n *\n * The hook code path is silent-only — pass a no-op `notify` there so\n * we don't spam stderr (which could break Claude Code). Init/status/\n * disable use the default notifier.\n */\nexport function migrateLegacyClaudeConfigIfNeeded(\n projectRoot: string,\n notify: MigrationNotifier = defaultNotifier,\n): boolean {\n try {\n const legacyPath = getLegacyClaudeMonitorConfigPath(projectRoot);\n const newPath = getMonitorConfigPath(projectRoot, \"claude-code\");\n if (fs.existsSync(newPath)) return false;\n if (!fs.existsSync(legacyPath)) return false;\n\n const raw = fs.readFileSync(legacyPath, \"utf-8\");\n // Validate the legacy file is parseable JSON before writing — a\n // corrupt legacy file shouldn't seed a corrupt new file.\n JSON.parse(raw);\n\n const newDir = getOlakaiDir(projectRoot);\n if (!fs.existsSync(newDir)) {\n fs.mkdirSync(newDir, { recursive: true });\n }\n fs.writeFileSync(newPath, raw, \"utf-8\");\n try {\n fs.chmodSync(newPath, 0o600);\n } catch {\n // chmod can fail on Windows / weird filesystems — non-fatal\n }\n\n try {\n notify(\n `[olakai] Migrated ${path.join(LEGACY_CLAUDE_DIR, LEGACY_CLAUDE_CONFIG_FILE)} -> ${path.join(OLAKAI_DIR, \"monitor-claude-code.json\")} (legacy file kept).`,\n );\n } catch {\n // Notifier failures must not break the caller\n }\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["path","HOSTS","normalizeHost","fs","path","fs","path","fs","path","fs","path","path"]}
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
setProfileOverride,
|
|
38
38
|
writeClaudeCodeConfig,
|
|
39
39
|
writeJsonFile
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-OHPX4STO.js";
|
|
41
41
|
|
|
42
42
|
// src/index.ts
|
|
43
43
|
import { createRequire } from "module";
|
|
@@ -997,12 +997,7 @@ async function postHandshakeJson(url, body, options = {}) {
|
|
|
997
997
|
body: JSON.stringify(body)
|
|
998
998
|
});
|
|
999
999
|
} catch (err) {
|
|
1000
|
-
return
|
|
1001
|
-
kind: "error",
|
|
1002
|
-
code: "network_error",
|
|
1003
|
-
message: err instanceof Error ? err.message : "Network request failed",
|
|
1004
|
-
status: 0
|
|
1005
|
-
};
|
|
1000
|
+
return buildNetworkError(err);
|
|
1006
1001
|
}
|
|
1007
1002
|
if (response.ok) {
|
|
1008
1003
|
let data;
|
|
@@ -1024,7 +1019,13 @@ async function postHandshakeJson(url, body, options = {}) {
|
|
|
1024
1019
|
} catch {
|
|
1025
1020
|
}
|
|
1026
1021
|
const code = mapErrorCode(response.status, errBody);
|
|
1027
|
-
|
|
1022
|
+
let fallbackPath = "";
|
|
1023
|
+
try {
|
|
1024
|
+
fallbackPath = new URL(url).pathname;
|
|
1025
|
+
} catch {
|
|
1026
|
+
fallbackPath = url;
|
|
1027
|
+
}
|
|
1028
|
+
const message = errBody.message || errBody.error || `Request to ${fallbackPath} failed with status ${response.status}`;
|
|
1028
1029
|
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
1029
1030
|
const envelope = {
|
|
1030
1031
|
kind: "error",
|
|
@@ -1076,6 +1077,28 @@ function mapErrorCode(status, body) {
|
|
|
1076
1077
|
return "unknown_error";
|
|
1077
1078
|
}
|
|
1078
1079
|
}
|
|
1080
|
+
function buildNetworkError(err) {
|
|
1081
|
+
const base = err instanceof Error ? err.message : "Network request failed";
|
|
1082
|
+
let causeCode;
|
|
1083
|
+
let causeMessage;
|
|
1084
|
+
if (err instanceof Error && err.cause && typeof err.cause === "object") {
|
|
1085
|
+
const c = err.cause;
|
|
1086
|
+
if (typeof c.code === "string" && c.code.length > 0) {
|
|
1087
|
+
causeCode = c.code;
|
|
1088
|
+
}
|
|
1089
|
+
if (typeof c.message === "string" && c.message.length > 0) {
|
|
1090
|
+
causeMessage = c.message;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
const message = causeMessage && causeMessage !== base ? `${base}: ${causeMessage}` : base;
|
|
1094
|
+
return {
|
|
1095
|
+
kind: "error",
|
|
1096
|
+
code: "network_error",
|
|
1097
|
+
message,
|
|
1098
|
+
status: 0,
|
|
1099
|
+
...causeCode ? { causeCode } : {}
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1079
1102
|
function parseRetryAfter(header) {
|
|
1080
1103
|
if (!header) return void 0;
|
|
1081
1104
|
const asNumber2 = Number(header);
|
|
@@ -1130,6 +1153,7 @@ function getCodexSessionsDir() {
|
|
|
1130
1153
|
|
|
1131
1154
|
// src/monitor/plugins/claude-code/index.ts
|
|
1132
1155
|
import * as fs4 from "fs";
|
|
1156
|
+
import { spawnSync } from "child_process";
|
|
1133
1157
|
|
|
1134
1158
|
// src/monitor/plugin.ts
|
|
1135
1159
|
var TOOL_IDS = [
|
|
@@ -1813,17 +1837,33 @@ var claudeCodePlugin = {
|
|
|
1813
1837
|
if (fs4.existsSync(getLegacyClaudeMonitorConfigPath(projectRoot))) {
|
|
1814
1838
|
return true;
|
|
1815
1839
|
}
|
|
1816
|
-
return false;
|
|
1817
1840
|
} catch {
|
|
1818
|
-
return false;
|
|
1819
1841
|
}
|
|
1842
|
+
return detectClaudeBinaryOnPath();
|
|
1820
1843
|
}
|
|
1821
1844
|
};
|
|
1845
|
+
function detectClaudeBinaryOnPath() {
|
|
1846
|
+
try {
|
|
1847
|
+
const probe = spawnSync(
|
|
1848
|
+
process.platform === "win32" ? "where" : "which",
|
|
1849
|
+
["claude"],
|
|
1850
|
+
{
|
|
1851
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1852
|
+
timeout: 1e3
|
|
1853
|
+
}
|
|
1854
|
+
);
|
|
1855
|
+
if (probe.status === 0 && probe.stdout && probe.stdout.toString().trim()) {
|
|
1856
|
+
return true;
|
|
1857
|
+
}
|
|
1858
|
+
} catch {
|
|
1859
|
+
}
|
|
1860
|
+
return false;
|
|
1861
|
+
}
|
|
1822
1862
|
registerPlugin(claudeCodePlugin);
|
|
1823
1863
|
|
|
1824
1864
|
// src/monitor/plugins/codex/index.ts
|
|
1825
1865
|
import * as fs9 from "fs";
|
|
1826
|
-
import { spawnSync } from "child_process";
|
|
1866
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
1827
1867
|
|
|
1828
1868
|
// src/monitor/plugins/codex/install.ts
|
|
1829
1869
|
import * as fs6 from "fs";
|
|
@@ -2526,7 +2566,7 @@ var codexPlugin = {
|
|
|
2526
2566
|
};
|
|
2527
2567
|
function detectCodexBinaryOnPath() {
|
|
2528
2568
|
try {
|
|
2529
|
-
const probe =
|
|
2569
|
+
const probe = spawnSync2(
|
|
2530
2570
|
process.platform === "win32" ? "where" : "which",
|
|
2531
2571
|
["codex"],
|
|
2532
2572
|
{
|
|
@@ -3692,7 +3732,17 @@ async function resolveHost(profileName, options, interactive) {
|
|
|
3692
3732
|
const file = readProfilesFile();
|
|
3693
3733
|
const existing = file.profiles[profileName];
|
|
3694
3734
|
if (existing?.host) {
|
|
3695
|
-
|
|
3735
|
+
if (!interactive || isTokenValid()) {
|
|
3736
|
+
return existing.host;
|
|
3737
|
+
}
|
|
3738
|
+
const keep = await promptUser(
|
|
3739
|
+
`
|
|
3740
|
+
Connect to ${existing.host}? [Y/n] (or pick a different workspace): `
|
|
3741
|
+
);
|
|
3742
|
+
if (keep.trim().toLowerCase() !== "n") {
|
|
3743
|
+
return existing.host;
|
|
3744
|
+
}
|
|
3745
|
+
console.log("");
|
|
3696
3746
|
}
|
|
3697
3747
|
if (!interactive) {
|
|
3698
3748
|
throw new InitAbortedError(
|
|
@@ -3780,7 +3830,11 @@ We sent a 6-digit code to ${ctx.emailObfuscated} (expires in ${expiryMin} min).`
|
|
|
3780
3830
|
console.log("Codes are 6 digits. Try again.");
|
|
3781
3831
|
continue;
|
|
3782
3832
|
}
|
|
3783
|
-
|
|
3833
|
+
let result = await postHandshakeVerify({ email, code: trimmed });
|
|
3834
|
+
if (result.kind === "error" && result.code === "network_error") {
|
|
3835
|
+
console.log("(Network error verifying code \u2014 retrying once...)");
|
|
3836
|
+
result = await postHandshakeVerify({ email, code: trimmed });
|
|
3837
|
+
}
|
|
3784
3838
|
if (result.kind === "ok") {
|
|
3785
3839
|
const consentToken = result.data.consentToken;
|
|
3786
3840
|
await runExchange(consentToken, profileName, email);
|
|
@@ -3819,7 +3873,9 @@ We sent a 6-digit code to ${ctx.emailObfuscated} (expires in ${expiryMin} min).`
|
|
|
3819
3873
|
);
|
|
3820
3874
|
throw new InitAbortedError("", 1);
|
|
3821
3875
|
case "network_error":
|
|
3822
|
-
console.error(
|
|
3876
|
+
console.error(
|
|
3877
|
+
`Network error${result.causeCode ? ` (${result.causeCode})` : ""}: ${result.message}`
|
|
3878
|
+
);
|
|
3823
3879
|
throw new InitAbortedError("", 1);
|
|
3824
3880
|
default:
|
|
3825
3881
|
console.error(
|
|
@@ -3830,7 +3886,11 @@ We sent a 6-digit code to ${ctx.emailObfuscated} (expires in ${expiryMin} min).`
|
|
|
3830
3886
|
}
|
|
3831
3887
|
}
|
|
3832
3888
|
async function runExchange(consentToken, profileName, email) {
|
|
3833
|
-
|
|
3889
|
+
let result = await postHandshakeExchange({ consentToken });
|
|
3890
|
+
if (result.kind === "error" && result.code === "network_error") {
|
|
3891
|
+
console.log("(Network error exchanging consent \u2014 retrying once...)");
|
|
3892
|
+
result = await postHandshakeExchange({ consentToken });
|
|
3893
|
+
}
|
|
3834
3894
|
if (result.kind === "error") {
|
|
3835
3895
|
switch (result.code) {
|
|
3836
3896
|
case "invalid_consent":
|
|
@@ -3853,6 +3913,11 @@ async function runExchange(consentToken, profileName, email) {
|
|
|
3853
3913
|
"Olakai is temporarily unavailable. Try again in a minute."
|
|
3854
3914
|
);
|
|
3855
3915
|
break;
|
|
3916
|
+
case "network_error":
|
|
3917
|
+
console.error(
|
|
3918
|
+
`Network error exchanging consent${result.causeCode ? ` (${result.causeCode})` : ""}: ${result.message}`
|
|
3919
|
+
);
|
|
3920
|
+
break;
|
|
3856
3921
|
default:
|
|
3857
3922
|
console.error(`Exchange failed (${result.code}): ${result.message}`);
|
|
3858
3923
|
}
|
|
@@ -3887,7 +3952,9 @@ function handleHandshakeError(err) {
|
|
|
3887
3952
|
console.error("Olakai is temporarily unavailable. Try again in a minute.");
|
|
3888
3953
|
break;
|
|
3889
3954
|
case "network_error":
|
|
3890
|
-
console.error(
|
|
3955
|
+
console.error(
|
|
3956
|
+
`Network error contacting Olakai${err.causeCode ? ` (${err.causeCode})` : ""}: ${err.message}`
|
|
3957
|
+
);
|
|
3891
3958
|
break;
|
|
3892
3959
|
default:
|
|
3893
3960
|
console.error(`Handshake failed (${err.code}): ${err.message}`);
|
|
@@ -5293,7 +5360,7 @@ async function statusCommand(options) {
|
|
|
5293
5360
|
return;
|
|
5294
5361
|
}
|
|
5295
5362
|
if (plugin.id === "claude-code") {
|
|
5296
|
-
const { printClaudeCodeStatus } = await import("./status-
|
|
5363
|
+
const { printClaudeCodeStatus } = await import("./status-CAHO5FHI.js");
|
|
5297
5364
|
await printClaudeCodeStatus({ projectRoot: process.cwd() });
|
|
5298
5365
|
return;
|
|
5299
5366
|
}
|