olakai-cli 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -87,7 +87,12 @@ olakai activity kpis --agent-id <id> | --workflow-id <id> [--since <date>] [--un
87
87
 
88
88
  ## Environment
89
89
 
90
- Set environment with `-e` flag or `OLAKAI_ENV` variable:
90
+ The CLI supports two ways to choose the backend it talks to: a named
91
+ environment (Olakai-hosted) or an explicit on-prem host.
92
+
93
+ ### Named environments (Olakai-hosted)
94
+
95
+ Set with `-e` flag or `OLAKAI_ENV` variable:
91
96
 
92
97
  - `production` (default): https://app.olakai.ai
93
98
  - `staging`: https://staging.app.olakai.ai
@@ -100,6 +105,24 @@ olakai -e local login
100
105
  OLAKAI_ENV=local olakai login
101
106
  ```
102
107
 
108
+ ### On-prem deployments
109
+
110
+ For self-hosted Olakai, point the CLI at your on-prem host with the
111
+ `--host` flag or `OLAKAI_HOST` env var:
112
+
113
+ ```bash
114
+ olakai --host olakai.acme.com login
115
+ # or
116
+ OLAKAI_HOST=olakai.acme.com olakai login
117
+ ```
118
+
119
+ `--host` accepts a bare hostname (`olakai.acme.com`) or a full URL
120
+ (`https://olakai.acme.com`). The host setting overrides `--env` and
121
+ `OLAKAI_ENV`.
122
+
123
+ **Resolution precedence:** `--host` flag → `OLAKAI_HOST` env var →
124
+ `--env` / `OLAKAI_ENV` (default `production`).
125
+
103
126
  ## Development
104
127
 
105
128
  ```bash
@@ -14,9 +14,13 @@ var HOSTS = {
14
14
  };
15
15
  var CLIENT_ID = "olakai-cli";
16
16
  var currentEnvironment = "production";
17
+ var hostOverride;
17
18
  function setEnvironment(env) {
18
19
  currentEnvironment = env;
19
20
  }
21
+ function setHostOverride(host) {
22
+ hostOverride = host;
23
+ }
20
24
  function getEnvironment() {
21
25
  const envVar = process.env.OLAKAI_ENV;
22
26
  if (envVar && isValidEnvironment(envVar)) {
@@ -24,7 +28,19 @@ function getEnvironment() {
24
28
  }
25
29
  return currentEnvironment;
26
30
  }
31
+ function normalizeHost(host) {
32
+ const withScheme = /^https?:\/\//i.test(host) ? host : `https://${host}`;
33
+ return withScheme.replace(/\/+$/, "");
34
+ }
27
35
  function getBaseUrl() {
36
+ const override = hostOverride?.trim();
37
+ if (override) {
38
+ return normalizeHost(override);
39
+ }
40
+ const envHost = process.env.OLAKAI_HOST?.trim();
41
+ if (envHost) {
42
+ return normalizeHost(envHost);
43
+ }
28
44
  return HOSTS[getEnvironment()];
29
45
  }
30
46
  function isValidEnvironment(env) {
@@ -382,6 +398,7 @@ async function printClaudeCodeStatus(opts) {
382
398
  export {
383
399
  CLIENT_ID,
384
400
  setEnvironment,
401
+ setHostOverride,
385
402
  getEnvironment,
386
403
  getBaseUrl,
387
404
  isValidEnvironment,
@@ -409,4 +426,4 @@ export {
409
426
  getClaudeCodeStatus,
410
427
  printClaudeCodeStatus
411
428
  };
412
- //# sourceMappingURL=chunk-HI5R5CP2.js.map
429
+ //# sourceMappingURL=chunk-NJ3O5MBL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/monitor/plugins/claude-code/status.ts","../src/lib/auth.ts","../src/lib/config.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 {\n // Activity check is optional — don't fail\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { type Environment, getEnvironment } from \"./config.js\";\n\ninterface StoredCredentials {\n token: string;\n expiresAt: number; // Unix timestamp in seconds\n environment: Environment;\n}\n\n/**\n * Get the credentials file path\n */\nfunction getCredentialsPath(): string {\n const configDir = path.join(os.homedir(), \".config\", \"olakai\");\n return path.join(configDir, \"credentials.json\");\n}\n\n/**\n * Ensure the config directory exists\n */\nfunction ensureConfigDir(): void {\n const configDir = path.dirname(getCredentialsPath());\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });\n }\n}\n\n/**\n * Save an access token to disk\n */\nexport function saveToken(token: string, expiresIn: number): void {\n ensureConfigDir();\n\n const credentials: StoredCredentials = {\n token,\n expiresAt: Math.floor(Date.now() / 1000) + expiresIn,\n environment: getEnvironment(),\n };\n\n const credentialsPath = getCredentialsPath();\n fs.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {\n mode: 0o600, // Read/write for owner only\n });\n}\n\n/**\n * Load the stored token\n */\nexport function loadToken(): StoredCredentials | null {\n const credentialsPath = getCredentialsPath();\n\n if (!fs.existsSync(credentialsPath)) {\n return null;\n }\n\n try {\n const content = fs.readFileSync(credentialsPath, \"utf-8\");\n const credentials = JSON.parse(content) as StoredCredentials;\n return credentials;\n } catch {\n return null;\n }\n}\n\n/**\n * Clear stored credentials\n */\nexport function clearToken(): void {\n const credentialsPath = getCredentialsPath();\n\n if (fs.existsSync(credentialsPath)) {\n fs.unlinkSync(credentialsPath);\n }\n}\n\n/**\n * Check if the stored token is valid (exists and not expired)\n */\nexport function isTokenValid(): boolean {\n const credentials = loadToken();\n\n if (!credentials) {\n return false;\n }\n\n // Check if expired (with 60 second buffer)\n const now = Math.floor(Date.now() / 1000);\n if (credentials.expiresAt <= now + 60) {\n return false;\n }\n\n // Check if environment matches\n if (credentials.environment !== getEnvironment()) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Get the current valid token or null\n */\nexport function getValidToken(): string | null {\n if (!isTokenValid()) {\n return null;\n }\n\n const credentials = loadToken();\n return credentials?.token ?? null;\n}\n","export 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. Named environment via `OLAKAI_ENV` / `--env` (production, staging, local)\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 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 * 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","fs","path","fs","path","fs","path","fs","path","path"]}
package/dist/index.js CHANGED
@@ -25,9 +25,10 @@ import {
25
25
  readJsonFile,
26
26
  saveToken,
27
27
  setEnvironment,
28
+ setHostOverride,
28
29
  writeClaudeCodeConfig,
29
30
  writeJsonFile
30
- } from "./chunk-HI5R5CP2.js";
31
+ } from "./chunk-NJ3O5MBL.js";
31
32
 
32
33
  // src/index.ts
33
34
  import { createRequire } from "module";
@@ -4584,7 +4585,7 @@ async function statusCommand(options) {
4584
4585
  return;
4585
4586
  }
4586
4587
  if (plugin.id === "claude-code") {
4587
- const { printClaudeCodeStatus } = await import("./status-PPSSB7FV.js");
4588
+ const { printClaudeCodeStatus } = await import("./status-SIQPCNCZ.js");
4588
4589
  await printClaudeCodeStatus({ projectRoot: process.cwd() });
4589
4590
  return;
4590
4591
  }
@@ -4745,6 +4746,9 @@ program.name("olakai").description("Olakai CLI tool").version(packageJson.versio
4745
4746
  "-e, --env <environment>",
4746
4747
  `Environment to use (${getValidEnvironments().join(", ")})`,
4747
4748
  "production"
4749
+ ).option(
4750
+ "--host <host>",
4751
+ "On-prem Olakai host (e.g. olakai.acme.com). Overrides --env and OLAKAI_HOST."
4748
4752
  ).hook("preAction", (thisCommand) => {
4749
4753
  const options = thisCommand.opts();
4750
4754
  if (options.env) {
@@ -4756,6 +4760,9 @@ program.name("olakai").description("Olakai CLI tool").version(packageJson.versio
4756
4760
  }
4757
4761
  setEnvironment(options.env);
4758
4762
  }
4763
+ if (typeof options.host === "string" && options.host.length > 0) {
4764
+ setHostOverride(options.host);
4765
+ }
4759
4766
  });
4760
4767
  program.command("login").description("Log in to Olakai using browser authentication").action(loginCommand);
4761
4768
  program.command("logout").description("Log out from Olakai").action(logoutCommand);