@latitude-data/openclaw-telemetry 0.0.5 → 0.0.7
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 +106 -23
- package/dist/plugin.js +63 -45
- package/dist/plugin.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -12
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -603
- package/dist/cli.js.map +0 -1
package/dist/cli.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/openclaw-cli.ts","../src/settings-file.ts","../src/setup.ts","../src/cli.ts"],"sourcesContent":["import { type SpawnSyncReturns, spawnSync } from \"node:child_process\"\n\n/**\n * Lowest OpenClaw version we support. The reporter verified hook-dispatch\n * gating works correctly here; older versions either reject\n * `hooks.allowConversationAccess` outright (≤ 2026.4.21) or have unverified\n * gating behaviour (2026.4.22 – 2026.4.24). Refusing to install on older\n * versions is intentional — we'd rather fail loudly than ship a\n * config the gateway will quarantine or hooks the dispatcher will block.\n */\nexport const MIN_OPENCLAW_VERSION = \"2026.4.25\"\n\nconst DEFAULT_TIMEOUT_MS = 10_000\n\ntype RunResult =\n | { ok: true; stdout: string; stderr: string; code: 0 }\n | { ok: false; reason: \"enoent\"; stdout: \"\"; stderr: \"\"; code: null }\n | { ok: false; reason: \"timeout\"; stdout: string; stderr: string; code: null }\n | { ok: false; reason: \"exit\"; stdout: string; stderr: string; code: number }\n\n/**\n * Spawn `openclaw <args>` synchronously. Reports failure modes structurally so\n * callers can decide how to degrade (missing binary vs. timed out vs. exited\n * non-zero). Never throws; ENOENT becomes `{ reason: \"enoent\" }`.\n */\nexport function runOpenclaw(args: string[], opts: { timeoutMs?: number; stdin?: string } = {}): RunResult {\n const result: SpawnSyncReturns<string> = spawnSync(\"openclaw\", args, {\n encoding: \"utf-8\",\n timeout: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n input: opts.stdin,\n // Inherit env so the user's PATH and any LATITUDE_* / OPENCLAW_* vars\n // reach the child process. We don't need a TTY — install is non-interactive.\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n })\n\n // spawnSync surfaces I/O errors through `result.error` rather than a thrown\n // exception when called this way. Order matters: a timeout shows up as\n // `error.code === \"ETIMEDOUT\"` AND/OR `signal === \"SIGTERM\"|\"SIGKILL\"`\n // depending on platform — classify it before the generic `if (err)`\n // catch-all so callers reliably get `reason: \"timeout\"` (and the\n // timeout-specific error messages in setup.ts aren't dead code).\n const err = result.error as (NodeJS.ErrnoException & { code?: string }) | undefined\n if (err?.code === \"ENOENT\") {\n return { ok: false, reason: \"enoent\", stdout: \"\", stderr: \"\", code: null }\n }\n if (err?.code === \"ETIMEDOUT\" || result.signal === \"SIGTERM\" || result.signal === \"SIGKILL\") {\n return {\n ok: false,\n reason: \"timeout\",\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n code: null,\n }\n }\n if (err) {\n // Treat any other spawn error as an unparseable exit failure.\n return {\n ok: false,\n reason: \"exit\",\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? String(err),\n code: typeof result.status === \"number\" ? result.status : 1,\n }\n }\n\n if (result.status === 0) {\n return { ok: true, stdout: result.stdout ?? \"\", stderr: result.stderr ?? \"\", code: 0 }\n }\n\n return {\n ok: false,\n reason: \"exit\",\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n code: typeof result.status === \"number\" ? result.status : 1,\n }\n}\n\ntype VersionLookup =\n | { ok: true; version: string; raw: string }\n | { ok: false; error: \"missing\" | \"unparseable\"; raw?: string | undefined }\n\n/**\n * Run `openclaw --version` and parse out the version string.\n *\n * Banner format (per OpenClaw `src/cli/banner.ts` `formatCliBannerLine`):\n * `🦞 OpenClaw <version> (<commit-sha>)`\n *\n * The banner is normally suppressed when `--version` is the flag, but the\n * version itself still goes to stdout. We accept either layout (with or\n * without the lobster + commit sha) so we don't break if OpenClaw later\n * prints just the bare version string.\n */\nexport function getOpenclawVersion(): VersionLookup {\n const result = runOpenclaw([\"--version\"], { timeoutMs: 5_000 })\n if (!result.ok) {\n if (result.reason === \"enoent\") return { ok: false, error: \"missing\" }\n return { ok: false, error: \"unparseable\", raw: result.stdout || result.stderr }\n }\n\n const raw = result.stdout.trim()\n // Match a CalVer triple anywhere in the output. Be liberal — banner\n // decorations (emoji, \"OpenClaw\" prefix, trailing \"(sha)\") all just\n // surround a single `YYYY.M.PATCH` somewhere.\n const match = raw.match(/(\\d{4}\\.\\d+\\.\\d+)/)\n if (!match) return { ok: false, error: \"unparseable\", raw }\n return { ok: true, version: match[1] as string, raw }\n}\n\n/**\n * Compare two CalVer strings (`YYYY.M.PATCH`). Returns -1 if `a` < `b`,\n * 0 if equal, 1 if `a` > `b`. Strings with extra components or non-numeric\n * pieces fall back to a per-component string comparison so unexpected\n * formats don't crash the installer.\n */\nexport function compareCalver(a: string, b: string): -1 | 0 | 1 {\n const ap = a.split(\".\")\n const bp = b.split(\".\")\n const len = Math.max(ap.length, bp.length)\n for (let i = 0; i < len; i++) {\n const ai = ap[i] ?? \"0\"\n const bi = bp[i] ?? \"0\"\n const an = Number(ai)\n const bn = Number(bi)\n if (Number.isFinite(an) && Number.isFinite(bn)) {\n if (an < bn) return -1\n if (an > bn) return 1\n continue\n }\n if (ai < bi) return -1\n if (ai > bi) return 1\n }\n return 0\n}\n","import { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nconst CONFIG_DIR = join(homedir(), \".openclaw\")\nexport const SETTINGS_PATH = join(CONFIG_DIR, \"openclaw.json\")\nexport const SETTINGS_BACKUP_PATH = join(CONFIG_DIR, \"openclaw.json.latitude-bak\")\n\n/** Plugin id used both as the npm package name and as the OpenClaw plugin id. */\nexport const PLUGIN_ID = \"@latitude-data/openclaw-telemetry\"\n\n/**\n * The shape OpenClaw's strict zod schema accepts for a single\n * `plugins.entries[id]` block. We keep this minimal — only the fields we\n * actually write — so we don't drop anything when round-tripping an entry\n * with fields we don't know about.\n */\ninterface OpenClawPluginEntry {\n enabled?: boolean\n /**\n * Strict zod object on OpenClaw 2026.4.25+: only `allowPromptInjection` and\n * `allowConversationAccess` are accepted. We populate the latter — it's the\n * dispatch gate the OpenClaw runtime checks before forwarding LLM/tool/agent\n * events to non-bundled plugins. Without it, the gateway logs\n * `[plugins] typed hook \"...\" blocked because non-bundled plugins must set\n * plugins.entries.<id>.hooks.allowConversationAccess=true` and the plugin's\n * handlers never fire.\n */\n hooks?: {\n allowPromptInjection?: boolean\n allowConversationAccess?: boolean\n [key: string]: unknown\n }\n /**\n * Free-form bucket OpenClaw passes to the plugin at activation\n * (`api.pluginConfig`). Our credentials and feature flags live here.\n */\n config?: Record<string, unknown>\n // Pass through anything else that may already be there — `subagent`, etc.\n [key: string]: unknown\n}\n\nexport interface OpenClawSettings {\n plugins?: {\n enabled?: boolean\n entries?: Record<string, OpenClawPluginEntry>\n /**\n * Operator-managed allowlist of plugin ids that may auto-load. OpenClaw\n * warns at every gateway start when a non-bundled plugin loads without\n * provenance via `plugins.allow` or an install record.\n */\n allow?: string[]\n load?: { paths?: string[] }\n [key: string]: unknown\n }\n [key: string]: unknown\n}\n\nexport interface LatitudePluginConfig {\n apiKey: string\n project: string\n baseUrl?: string | undefined\n allowConversationAccess?: boolean | undefined\n debug?: boolean | undefined\n}\n\nexport function readSettings(): OpenClawSettings {\n if (!existsSync(SETTINGS_PATH)) return {}\n try {\n const raw = readFileSync(SETTINGS_PATH, \"utf-8\")\n const parsed = JSON.parse(raw) as OpenClawSettings\n return parsed && typeof parsed === \"object\" ? parsed : {}\n } catch {\n return {}\n }\n}\n\nexport function writeSettings(settings: OpenClawSettings): void {\n writeFileSync(SETTINGS_PATH, `${JSON.stringify(settings, null, 2)}\\n`, \"utf-8\")\n}\n\nexport function backupSettings(): void {\n if (existsSync(SETTINGS_PATH)) copyFileSync(SETTINGS_PATH, SETTINGS_BACKUP_PATH)\n}\n\ninterface SetPluginEntryPatch {\n /** New API key. Always overwrites — comes from the install prompt. */\n apiKey: string\n /** New project slug. Always overwrites — comes from the install prompt. */\n project: string\n /**\n * New `baseUrl`. `undefined` clears any existing override (used when\n * installing back to production). Anything else overwrites.\n */\n baseUrl: string | undefined\n /**\n * `true`/`false` overwrites both `config.allowConversationAccess` AND\n * `hooks.allowConversationAccess` (always coupled — see `setPluginEntry`).\n * `undefined` preserves the existing value, or defaults to `true` for a\n * fresh install.\n */\n allowConversationAccess?: boolean | undefined\n /**\n * `true`/`false` overwrites; `undefined` preserves the existing value (the\n * installer doesn't pass `debug` — it's a hand-edit affordance).\n */\n debug?: boolean | undefined\n /**\n * `true`/`false` overwrites; `undefined` preserves existing, defaulting to\n * `true` for a fresh install. This keeps a paused plugin (`enabled: false`\n * hand-edited in openclaw.json) paused across re-installs.\n */\n enabled?: boolean | undefined\n}\n\n/**\n * Set the `plugins.entries[id]` block for our plugin.\n *\n * Two places in the entry get written:\n *\n * - `.config` (free-form `record(string, unknown)`): credentials, baseUrl,\n * and our copy of `allowConversationAccess`. This is what the plugin\n * runtime reads via `api.pluginConfig`.\n * - `.hooks.allowConversationAccess`: controls whether OpenClaw's hook\n * dispatcher actually forwards `llm_input` / `llm_output` / tool /\n * `agent_end` events to our handlers. Without it set to `true` on\n * OpenClaw 2026.4.25+, every typed hook is blocked at the dispatcher\n * and the plugin's handlers never fire.\n *\n * The two flags mean different things — `hooks.*` is the dispatch gate,\n * `config.*` is the payload-content gate — but for THIS plugin we always\n * couple them: dispatch off + payload on is useless (no payloads to gate),\n * and dispatch on + payload off is a legitimate \"structural-only telemetry\"\n * mode (timing, tokens, ids, agent name; no message bodies). Always writing\n * both from the same source keeps the operator's mental model simple.\n *\n * Re-install idempotency: only `apiKey` / `project` / `baseUrl` always\n * overwrite (these come from install prompts). `enabled`, `debug`, and\n * `allowConversationAccess` are preserved when not provided in the patch.\n */\nexport function setPluginEntry(settings: OpenClawSettings, patch: SetPluginEntryPatch): void {\n const plugins = settings.plugins ?? {}\n const entries = plugins.entries ?? {}\n const existing = entries[PLUGIN_ID] ?? {}\n const existingConfig = (existing.config ?? {}) as Record<string, unknown>\n const existingHooks = existing.hooks ?? {}\n\n const nextConfig: Record<string, unknown> = {\n ...existingConfig,\n apiKey: patch.apiKey,\n project: patch.project,\n }\n if (patch.baseUrl !== undefined) {\n nextConfig.baseUrl = patch.baseUrl\n } else {\n delete nextConfig.baseUrl\n }\n if (patch.allowConversationAccess !== undefined) {\n nextConfig.allowConversationAccess = patch.allowConversationAccess\n }\n if (patch.debug !== undefined) {\n nextConfig.debug = patch.debug\n }\n\n // Resolve the effective allowConversationAccess for the hooks block.\n // We mirror whatever ended up in nextConfig.allowConversationAccess (just\n // computed above) so the two flags always agree. If neither the patch nor\n // the existing config sets it, fall back to the existing hooks value, then\n // to `true` (matches the README's first-install promise).\n const effectiveAccess =\n typeof nextConfig.allowConversationAccess === \"boolean\"\n ? nextConfig.allowConversationAccess\n : typeof existingHooks.allowConversationAccess === \"boolean\"\n ? existingHooks.allowConversationAccess\n : true\n\n const nextHooks: OpenClawPluginEntry[\"hooks\"] = {\n ...existingHooks,\n allowConversationAccess: effectiveAccess,\n }\n\n // Preserve a hand-edited `enabled: false` across re-installs. Fresh install\n // (no existing entry, no explicit patch) defaults to true.\n const nextEnabled = patch.enabled ?? existing.enabled ?? true\n\n entries[PLUGIN_ID] = {\n ...existing,\n enabled: nextEnabled,\n hooks: nextHooks,\n config: nextConfig,\n }\n plugins.entries = entries\n settings.plugins = plugins\n}\n\n/** Remove the plugin entry entirely. Used by uninstall as defense-in-depth. */\nexport function removePluginEntry(settings: OpenClawSettings): boolean {\n const plugins = settings.plugins\n if (!plugins?.entries) return false\n if (!(PLUGIN_ID in plugins.entries)) return false\n delete plugins.entries[PLUGIN_ID]\n return true\n}\n\n/**\n * Add the plugin id to `plugins.allow`. Idempotent — returns `true` only when\n * the array changed. OpenClaw warns at every gateway start when a non-bundled\n * plugin auto-loads without provenance via `plugins.allow` or an install\n * record. We get one warning cleared by going through `openclaw plugins\n * install` (provenance) and the other by adding ourselves to allow.\n *\n * Defensive against hand-edited non-array values: if `plugins.allow` is\n * present but not an array (e.g. someone wrote a string), we replace it\n * with a single-element array rather than spreading the bad value.\n */\nexport function addToPluginsAllow(settings: OpenClawSettings): boolean {\n const plugins = settings.plugins ?? {}\n const existing = plugins.allow\n const allow = Array.isArray(existing) ? existing : []\n if (allow.includes(PLUGIN_ID)) return false\n plugins.allow = [...allow, PLUGIN_ID]\n settings.plugins = plugins\n return true\n}\n\n/**\n * Inverse of `addToPluginsAllow`. Defense-in-depth — `openclaw plugins\n * uninstall` already strips the entry, but the install path can be skipped\n * (e.g. the user removed the plugin manually) and we want re-install/uninstall\n * round-trips to be tidy regardless.\n */\nexport function removeFromPluginsAllow(settings: OpenClawSettings): boolean {\n const allow = settings.plugins?.allow\n if (!Array.isArray(allow) || !allow.includes(PLUGIN_ID)) return false\n if (settings.plugins) {\n settings.plugins.allow = allow.filter((id) => id !== PLUGIN_ID)\n }\n return true\n}\n\nexport function hasLatitudePlugin(settings: OpenClawSettings): boolean {\n return Boolean(settings.plugins?.entries && PLUGIN_ID in settings.plugins.entries)\n}\n\n/**\n * Strip leftover keys from older installers that the strict zod schema\n * rejects on current OpenClaw versions.\n *\n * 0.0.1 wrote `LATITUDE_*` keys directly under `settings.env`. OpenClaw's\n * root schema is strict; the `env` block accepts only `{shellEnv, vars}`,\n * so those keys cause the gateway to quarantine the config as\n * `clobbered.<ts>` and roll back. We sweep them on every install.\n *\n * Note: 0.0.1 also wrote `hooks.allowConversationAccess` (when that key was\n * not yet in the schema). We deliberately do NOT strip it anymore — on\n * OpenClaw 2026.4.25+ the key IS in the schema and IS load-bearing for\n * dispatch. `setPluginEntry` overwrites it on every install with the right\n * value, so any 0.0.1 leftover is reconciled there.\n */\nexport function migrateLegacyEntries(settings: OpenClawSettings): { changed: boolean } {\n let changed = false\n\n const env = settings.env\n if (env && typeof env === \"object\" && !Array.isArray(env)) {\n const envObj = env as Record<string, unknown>\n for (const key of [\"LATITUDE_API_KEY\", \"LATITUDE_PROJECT\", \"LATITUDE_BASE_URL\"]) {\n if (key in envObj) {\n delete envObj[key]\n changed = true\n }\n }\n // Drop the env object only if it's now empty; don't touch a real\n // OpenClaw env block (`{shellEnv, vars}`) that already had those keys.\n if (Object.keys(envObj).length === 0) {\n delete settings.env\n }\n }\n\n return { changed }\n}\n","import { existsSync, mkdirSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { cancel, confirm, intro, isCancel, log, note, outro, password, spinner, text } from \"@clack/prompts\"\nimport pc from \"picocolors\"\nimport { compareCalver, getOpenclawVersion, MIN_OPENCLAW_VERSION, runOpenclaw } from \"./openclaw-cli.ts\"\nimport {\n addToPluginsAllow,\n backupSettings,\n hasLatitudePlugin,\n type LatitudePluginConfig,\n migrateLegacyEntries,\n PLUGIN_ID,\n readSettings,\n removeFromPluginsAllow,\n removePluginEntry,\n SETTINGS_BACKUP_PATH,\n SETTINGS_PATH,\n setPluginEntry,\n writeSettings,\n} from \"./settings-file.ts\"\n\nconst DOCS_URL = \"https://docs.latitude.so/openclaw-telemetry\"\n\ninterface EnvironmentConfig {\n name: \"production\" | \"staging\" | \"dev\"\n label: string\n app: string\n ingest: string\n}\n\nconst PRODUCTION_ENV: EnvironmentConfig = {\n name: \"production\",\n label: \"production\",\n app: \"https://console.latitude.so\",\n ingest: \"https://ingest.latitude.so\",\n}\nconst STAGING_ENV: EnvironmentConfig = {\n name: \"staging\",\n label: \"staging\",\n app: \"https://staging.latitude.so\",\n ingest: \"https://staging-ingest.latitude.so\",\n}\nconst DEV_ENV: EnvironmentConfig = {\n name: \"dev\",\n label: \"local dev\",\n app: \"http://localhost:3000\",\n ingest: \"http://localhost:3002\",\n}\n\nfunction urlsFor(env: EnvironmentConfig): {\n apiKeys: string\n projects: string\n projectView: (slug: string) => string\n} {\n return {\n apiKeys: `${env.app}/settings/api-keys`,\n projects: env.app,\n projectView: (slug: string) => `${env.app}/projects/${slug}`,\n }\n}\n\ninterface InstallFlags {\n apiKey?: string | undefined\n project?: string | undefined\n environment?: EnvironmentConfig | undefined\n /**\n * Tristate: `true` = capture (`--allow-conversation`), `false` = scrub\n * (`--no-content`), `undefined` = preserve existing or first-install\n * default. Tristate keeps re-install idempotent for hand-edited values.\n */\n allowConversationAccess?: boolean | undefined\n /** When true, skip adding the plugin id to `plugins.allow`. */\n noTrust?: boolean\n noPrompt?: boolean\n yes?: boolean\n}\n\nexport function parseFlags(argv: string[]): {\n subcommand: string | undefined\n flags: Record<string, string | boolean>\n} {\n const [subcommand, ...rest] = argv\n const flags: Record<string, string | boolean> = {}\n for (const arg of rest) {\n if (!arg.startsWith(\"--\")) continue\n const eq = arg.indexOf(\"=\")\n if (eq >= 0) {\n flags[arg.slice(2, eq)] = arg.slice(eq + 1)\n } else {\n flags[arg.slice(2)] = true\n }\n }\n return { subcommand, flags }\n}\n\nexport function normalizeInstallFlags(flags: Record<string, string | boolean>): InstallFlags {\n let environment: EnvironmentConfig | undefined\n if (flags.staging === true) environment = STAGING_ENV\n if (flags.dev === true) {\n if (environment) throw new Error(\"--staging and --dev are mutually exclusive\")\n environment = DEV_ENV\n }\n // Tristate: leave undefined unless the user explicitly asked one way or the\n // other. Re-install then preserves whatever's already in openclaw.json.\n let allowConversationAccess: boolean | undefined\n if (flags[\"no-content\"] === true || flags[\"no-conversation\"] === true) allowConversationAccess = false\n if (flags[\"allow-conversation\"] === true) allowConversationAccess = true\n\n return {\n apiKey: typeof flags[\"api-key\"] === \"string\" ? flags[\"api-key\"] : undefined,\n project: typeof flags.project === \"string\" ? flags.project : undefined,\n environment,\n allowConversationAccess,\n noTrust: flags[\"no-trust\"] === true,\n noPrompt: flags[\"no-prompt\"] === true || flags.yes === true,\n yes: flags.yes === true,\n }\n}\n\n// ─── Install ────────────────────────────────────────────────────────────────\n\nexport async function runInstall(flags: InstallFlags = {}): Promise<void> {\n const canPrompt = !flags.noPrompt && process.stdin.isTTY === true\n if (!canPrompt) return runFlagDrivenInstall(flags)\n await runInteractiveInstall(flags)\n}\n\nasync function runInteractiveInstall(flags: InstallFlags): Promise<void> {\n intro(pc.bgCyan(pc.black(\" Latitude · OpenClaw telemetry \")))\n\n // Bail before any prompts if the host OpenClaw is too old. We'd rather\n // tell the user to upgrade than walk them through a config we can't make\n // work on their version.\n ensureOpenclawIsCompatible()\n\n const existing = readSettings()\n const existingConfig =\n (existing.plugins?.entries?.[PLUGIN_ID]?.config as LatitudePluginConfig | undefined) ?? undefined\n const envConfig = flags.environment ?? PRODUCTION_ENV\n const urls = urlsFor(envConfig)\n\n const aboutLines = [\n \"Captures every OpenClaw agent run and ships it to Latitude as\",\n \"OpenTelemetry traces — full system prompt, tool I/O, messages,\",\n \"token usage, and agent name on every span.\",\n \"\",\n `${pc.dim(\"Docs\")} ${pc.cyan(DOCS_URL)}`,\n ]\n if (envConfig.name !== \"production\") {\n aboutLines.push(\"\", pc.yellow(`Using ${envConfig.label} environment (${envConfig.ingest})`))\n }\n note(aboutLines.join(\"\\n\"), \"About\")\n\n log.info(`Get an API key at ${pc.cyan(urls.apiKeys)}`)\n log.info(`Create a project at ${pc.cyan(urls.projects)}`)\n\n const apiKey = await promptApiKey(existingConfig?.apiKey, flags.apiKey)\n const project = await promptProject(existingConfig?.project, flags.project)\n\n await applyChanges({\n apiKey,\n project,\n envConfig,\n allowConversationAccess: flags.allowConversationAccess,\n noTrust: flags.noTrust === true,\n })\n\n note(\n [\n \"Restart the OpenClaw gateway for the plugin to load:\",\n pc.dim(\" openclaw gateway restart\"),\n \"\",\n `View your traces at ${pc.cyan(urls.projectView(project))}`,\n ].join(\"\\n\"),\n \"Next step\",\n )\n outro(pc.green(\"✓ Installed\"))\n}\n\nasync function runFlagDrivenInstall(flags: InstallFlags): Promise<void> {\n ensureOpenclawIsCompatible()\n const apiKey = flags.apiKey\n const project = flags.project\n if (!apiKey || !project) {\n throw new Error(\"Non-interactive install requires --api-key=... and --project=... (or run in a TTY).\")\n }\n const envConfig = flags.environment ?? PRODUCTION_ENV\n await applyChanges({\n apiKey,\n project,\n envConfig,\n allowConversationAccess: flags.allowConversationAccess,\n noTrust: flags.noTrust === true,\n })\n process.stdout.write(`Installed Latitude plugin in ${SETTINGS_PATH}\\n`)\n}\n\nasync function promptApiKey(_existing: string | undefined, flag: string | undefined): Promise<string> {\n if (flag) return flag\n const result = await password({\n message: \"Latitude API key\",\n mask: \"•\",\n validate: (v) => (v && v.length > 0 ? undefined : \"Required\"),\n })\n if (isCancel(result)) return onCancel()\n return result\n}\n\nasync function promptProject(existing: string | undefined, flag: string | undefined): Promise<string> {\n if (flag) return flag\n const result = await text({\n message: \"Latitude project slug\",\n placeholder: existing ?? \"my-openclaw-project\",\n ...(existing ? { initialValue: existing } : {}),\n validate: (v) => (v && v.length > 0 ? undefined : \"Required\"),\n })\n if (isCancel(result)) return onCancel()\n return result\n}\n\nfunction onCancel(): never {\n cancel(\"Cancelled — nothing was changed\")\n process.exit(1)\n}\n\ninterface ApplyParams {\n apiKey: string\n project: string\n envConfig: EnvironmentConfig\n /** Tristate — see `InstallFlags.allowConversationAccess`. */\n allowConversationAccess: boolean | undefined\n noTrust: boolean\n}\n\nasync function applyChanges({\n apiKey,\n project,\n envConfig,\n allowConversationAccess,\n noTrust,\n}: ApplyParams): Promise<void> {\n // 1. Take the backup BEFORE openclaw plugins install touches openclaw.json\n // so it represents the user's true pre-install state. `openclaw plugins\n // install` creates plugins.entries[id], so backing up after would lose\n // the original \"no entry\" state and make recovery harder if any later\n // step fails.\n ensureSettingsDir()\n backupSettings()\n\n // 2. Hand placement off to OpenClaw. `openclaw plugins install <path>`\n // copies our package into ~/.openclaw/extensions/<encoded-id>/, writes\n // the install record into ~/.openclaw/plugins/installs.json, and\n // creates a (disabled, configless) plugins.entries[id] in\n // openclaw.json. We layer config + hooks + allow on top in step 3.\n // --force lets us overwrite an existing install (e.g. when re-running\n // on top of a previous version).\n const packageRoot = resolvePackageRoot()\n const installSpinner = spinner()\n installSpinner.start(`Installing plugin via openclaw plugins install ${packageRoot}`)\n const installResult = runOpenclaw([\"plugins\", \"install\", packageRoot, \"--force\"], { timeoutMs: 60_000 })\n if (!installResult.ok) {\n installSpinner.stop(\"openclaw plugins install failed\")\n if (installResult.reason === \"enoent\") {\n throw new Error(\"`openclaw` not found on PATH. Install OpenClaw first (https://openclaw.ai/install) and re-run.\")\n }\n if (installResult.reason === \"timeout\") {\n throw new Error(\"openclaw plugins install timed out after 60s. Try running it manually to see what's stuck.\")\n }\n const detail = installResult.stderr.trim() || installResult.stdout.trim() || `exit code ${installResult.code}`\n throw new Error(`openclaw plugins install failed: ${detail}`)\n }\n installSpinner.stop(\"Plugin registered with OpenClaw\")\n\n // 3. Layer our config, hooks, and (optionally) plugins.allow on top of the\n // entry OpenClaw just created. We don't touch placement — that's\n // OpenClaw's job — only the policy fields.\n const settingsSpinner = spinner()\n settingsSpinner.start(\"Updating openclaw.json\")\n const settings = readSettings()\n // Sweep `LATITUDE_*` keys our 0.0.1 leaked under settings.env. Idempotent\n // when the keys aren't there.\n migrateLegacyEntries(settings)\n\n // Pass the install-flag tristate through verbatim — `setPluginEntry`\n // resolves the final value by consulting (in order): the patch, the\n // existing config block, the existing hooks block, then defaulting to\n // `true`. Going through that resolver matters because a 0.0.1 install\n // may have stored the flag only under hooks; if we pre-collapsed it\n // here using existing config alone, we'd lose that signal and silently\n // overwrite the user's intent.\n setPluginEntry(settings, {\n apiKey,\n project,\n baseUrl: envConfig.name === \"production\" ? undefined : envConfig.ingest,\n allowConversationAccess,\n // `debug` is intentionally not passed — `setPluginEntry` preserves\n // hand-edited values; fresh installs leave the key absent (runtime\n // default is `false`).\n })\n\n // Plugins.allow handling. Running `npx install` is itself the trust\n // signal — auto-add unless the user explicitly opted out via --no-trust.\n // Without this, OpenClaw prints a \"plugins.allow is empty\" warning at\n // every gateway start.\n if (!noTrust) {\n addToPluginsAllow(settings)\n }\n\n writeSettings(settings)\n settingsSpinner.stop(`Updated ${SETTINGS_PATH}`)\n if (existsSync(SETTINGS_BACKUP_PATH)) log.info(`Backup saved at ${pc.dim(SETTINGS_BACKUP_PATH)}`)\n if (noTrust) {\n log.warning(\n `--no-trust set; OpenClaw will warn at every gateway start that ${PLUGIN_ID} is untrusted. Add it to plugins.allow yourself when you're ready.`,\n )\n }\n}\n\nfunction ensureSettingsDir(): void {\n const dir = dirname(SETTINGS_PATH)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n}\n\n/**\n * Verify `openclaw` is on PATH AND its version is >= MIN_OPENCLAW_VERSION.\n * Aborts with a clear upgrade message otherwise. Called before any user\n * prompts so we don't waste their time collecting credentials we can't\n * use.\n */\nfunction ensureOpenclawIsCompatible(): void {\n const v = getOpenclawVersion()\n if (!v.ok) {\n if (v.error === \"missing\") {\n cancel(\"OpenClaw CLI not found on PATH. Install or update via `npm install -g openclaw@latest` and re-run.\")\n process.exit(1)\n }\n cancel(\n `Couldn't parse OpenClaw version output${v.raw ? ` (got: ${pc.dim(v.raw)})` : \"\"}. Run \\`openclaw --version\\` and report the output.`,\n )\n process.exit(1)\n }\n if (compareCalver(v.version, MIN_OPENCLAW_VERSION) < 0) {\n cancel(\n `OpenClaw ${v.version} is older than the minimum supported version (${MIN_OPENCLAW_VERSION}). Run \\`npm install -g openclaw@latest\\` and re-run install.`,\n )\n process.exit(1)\n }\n log.info(`OpenClaw ${pc.dim(v.version)} (>= ${MIN_OPENCLAW_VERSION})`)\n}\n\n/**\n * Resolve the absolute path to our package's root (the directory that\n * contains `package.json` + `openclaw.plugin.json` + `dist/`). The compiled\n * CLI lives at `<package-root>/dist/cli.js`, so `import.meta.url`'s parent\n * directory's parent is our root.\n *\n * `openclaw plugins install <path>` copies files synchronously, so we\n * don't have to keep this directory alive past the spawn return.\n */\nfunction resolvePackageRoot(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n return resolve(here, \"..\")\n}\n\n// ─── Uninstall ──────────────────────────────────────────────────────────────\n\ninterface UninstallFlags {\n noPrompt?: boolean\n}\n\nexport async function runUninstall(flags: UninstallFlags = {}): Promise<void> {\n intro(pc.bgYellow(pc.black(\" Latitude · OpenClaw telemetry — uninstall \")))\n\n const settings = readSettings()\n const hasEntry = hasLatitudePlugin(settings)\n\n // Without an entry, there's nothing for `openclaw plugins uninstall` to\n // remove either — short-circuit cleanly. If the user wants to uninstall\n // even when our entry is gone (e.g. we crashed mid-install), they can\n // run `openclaw plugins uninstall` themselves.\n if (!hasEntry) {\n note(\"No Latitude plugin entry found — nothing to remove.\", \"Status\")\n outro(pc.dim(\"Nothing changed\"))\n return\n }\n\n const plan = [\n `Run \\`openclaw plugins uninstall ${PLUGIN_ID} --force\\` (removes files, install record, and plugin entry)`,\n `Sweep any leftover LATITUDE_* keys from settings.env`,\n `Backup of openclaw.json saved at ${SETTINGS_BACKUP_PATH}`,\n ]\n note(plan.join(\"\\n\"), \"Plan\")\n\n if (!flags.noPrompt && process.stdin.isTTY === true) {\n const ok = await confirm({ message: \"Proceed?\", initialValue: true })\n if (isCancel(ok) || ok !== true) return onCancel()\n }\n\n // 1. Take the backup BEFORE openclaw plugins uninstall touches\n // openclaw.json so it represents the user's true pre-uninstall state\n // (entry + config + plugins.allow). Backing up after would capture\n // the already-stripped state, defeating the point of the backup.\n backupSettings()\n\n // 2. Hand uninstall to OpenClaw. It removes files, the install record,\n // plugins.entries[id], plugins.allow, plugins.deny, plugins.load.paths\n // — see src/plugins/uninstall.ts.\n const s = spinner()\n s.start(\"Reverting via openclaw plugins uninstall\")\n const uninstallResult = runOpenclaw([\"plugins\", \"uninstall\", PLUGIN_ID, \"--force\"], { timeoutMs: 60_000 })\n if (!uninstallResult.ok) {\n s.stop(\"openclaw plugins uninstall failed\")\n if (uninstallResult.reason === \"enoent\") {\n log.warning(\n \"`openclaw` not found on PATH. Falling back to local cleanup — files at ~/.openclaw/extensions/ may remain.\",\n )\n } else {\n const detail =\n uninstallResult.stderr.trim() || uninstallResult.stdout.trim() || `exit code ${uninstallResult.code}`\n log.warning(`openclaw plugins uninstall reported: ${detail}. Continuing with local cleanup.`)\n }\n } else {\n s.stop(\"Plugin removed by OpenClaw\")\n }\n\n // 3. Defensive cleanup — `openclaw plugins uninstall` already strips the\n // entry and plugins.allow on success, but if it failed above (enoent\n // / non-zero) we still want our state out of openclaw.json. These\n // are all idempotent.\n const cleanupSpinner = spinner()\n cleanupSpinner.start(\"Reverting openclaw.json\")\n const post = readSettings()\n removePluginEntry(post)\n removeFromPluginsAllow(post)\n migrateLegacyEntries(post)\n writeSettings(post)\n cleanupSpinner.stop(\"Done\")\n outro(pc.green(\"✓ Uninstalled\"))\n}\n","import { readFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { normalizeInstallFlags, parseFlags, runInstall, runUninstall } from \"./setup.ts\"\n\nconst USAGE = `usage: latitude-openclaw <command> [options]\n\ncommands:\n install Install the plugin (interactive when stdin is a TTY)\n uninstall Remove the plugin entry and files\n --version, -v Print the package version\n --help, -h Print this message\n\ninstall options:\n --api-key=<key> Pass the API key non-interactively\n --project=<slug> Pass the project slug non-interactively\n --staging Target https://staging.latitude.so / staging-ingest\n --dev Target http://localhost:3000 / 3002\n --no-content Skip raw prompt/response/tool I/O capture\n --allow-conversation Force conversation capture on (overrides existing config)\n --yes / --no-prompt Skip all prompts (required for non-TTY / CI)\n`\n\nfunction readVersion(): string {\n // dist/cli.js → ../package.json\n const here = dirname(fileURLToPath(import.meta.url))\n const pkgPath = join(here, \"..\", \"package.json\")\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as { version?: string }\n return pkg.version ?? \"unknown\"\n } catch {\n return \"unknown\"\n }\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2)\n // Top-level --version / --help are handled before parseFlags so users can\n // type them as the first arg without hitting the \"unknown subcommand\" path.\n if (argv[0] === \"--version\" || argv[0] === \"-v\") {\n process.stdout.write(`${readVersion()}\\n`)\n return\n }\n if (argv[0] === \"--help\" || argv[0] === \"-h\") {\n process.stdout.write(USAGE)\n return\n }\n\n const { subcommand, flags } = parseFlags(argv)\n if (subcommand === \"install\" || subcommand === undefined) {\n await runInstall(normalizeInstallFlags(flags))\n return\n }\n if (subcommand === \"uninstall\") {\n await runUninstall({ noPrompt: flags[\"no-prompt\"] === true || flags.yes === true })\n return\n }\n process.stderr.write(`unknown subcommand: ${subcommand}\\n`)\n process.stderr.write(USAGE)\n process.exit(1)\n}\n\nmain().catch((err) => {\n process.stderr.write(`${String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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"}
|