@legioncodeinc/honeycomb 0.1.8 → 0.1.10

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/bundle/cli.js CHANGED
@@ -5,39 +5,57 @@ var __export = (target, all) => {
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
+ // dist/src/cli/index.js
9
+ import { realpathSync } from "node:fs";
10
+ import { pathToFileURL } from "node:url";
11
+
8
12
  // dist/src/commands/contracts.js
13
+ var VERB_GROUPS = Object.freeze([
14
+ { key: "memory", label: "Memory & recall" },
15
+ { key: "knowledge", label: "Knowledge & skills" },
16
+ { key: "agents", label: "Agents, routing & config" },
17
+ { key: "account", label: "Account & workspaces" },
18
+ { key: "system", label: "Setup & system" }
19
+ ]);
9
20
  var VERB_TABLE = Object.freeze([
10
- { verb: "setup", cls: "local", summary: "detect assistants, wire hooks, bring up the daemon" },
11
- { verb: "install", cls: "local", summary: "bring up the daemon (health-gated) + open the dashboard (PRD-050a)" },
12
- { verb: "status", cls: "local", summary: "daemon connectivity + login + D1\u2013D5 environment health" },
13
- { verb: "daemon", cls: "local", summary: "start | stop | status the loopback daemon (3850)" },
14
- { verb: "dashboard", cls: "local", summary: "launch the daemon-served dashboard (020b)" },
15
- { verb: "telemetry", cls: "local", summary: "show exactly what adoption telemetry has been / would be sent (--show, PRD-050e)" },
16
- { verb: "pollinate", cls: "storage", summary: "trigger a pollinating consolidation pass on the daemon (009/026)" },
17
- { verb: "maintenance", cls: "storage", summary: "run version-history compaction over version-bumped tables (030)" },
18
- { verb: "remember", cls: "storage", summary: "write a memory through the daemon (--type fact|convention|preference|decision|gotcha|reference)" },
19
- { verb: "recall", cls: "storage", summary: "recall memories through the daemon" },
20
- { verb: "memory", cls: "storage", summary: "lifecycle: conflicts (list/resolve), stale-refs (list), inspect <id> --lifecycle (058d)" },
21
- { verb: "agent", cls: "storage", summary: "run an agent turn through the daemon" },
22
- { verb: "ontology", cls: "storage", summary: "inspect/propose ontology changes through the daemon" },
23
- { verb: "secret", cls: "storage", summary: "manage named secrets through the daemon" },
24
- { verb: "settings", cls: "storage", summary: "get/set/list vault settings + provider\u2192model selector through the daemon" },
25
- { verb: "asset", cls: "storage", summary: "register/promote/demote/style skills+agents through the tier\xD7style lattice (033)" },
26
- { verb: "skill", cls: "storage", summary: "skillify scope/pull/unpull/force/promote through the daemon (promote = cross-project, 049c)" },
27
- { verb: "skillify", cls: "storage", summary: "pull team skills from the daemon (016c)" },
28
- { verb: "hook", cls: "local", summary: "inspect/wire harness hooks" },
29
- { verb: "route", cls: "storage", summary: "manage inference routes through the daemon" },
30
- { verb: "sources", cls: "storage", summary: "connect/index/purge sources through the daemon" },
31
- { verb: "graph", cls: "storage", summary: "build/query the codebase graph through the daemon" },
32
- { verb: "goal", cls: "storage", summary: "manage goals/KPIs through the daemon" },
33
- { verb: "whoami", cls: "auth", summary: "show the authenticated user, org, and workspace (GET /me)" },
34
- { verb: "org", cls: "auth", summary: "list/switch org (passthrough to the auth dispatcher)" },
35
- { verb: "workspace", cls: "auth", summary: "list/switch/use workspace (passthrough to the auth dispatcher)" },
36
- { verb: "workspaces", cls: "auth", summary: "list workspaces in the active org (alias of `workspace list`)" },
37
- { verb: "project", cls: "auth", summary: "list/bind/use projects + show the resolved per-folder scope (049d)" },
38
- { verb: "sessions", cls: "storage", summary: "list/prune captured sessions through the daemon" },
39
- { verb: "uninstall", cls: "local", summary: "reverse only Honeycomb's changes" },
40
- { verb: "update", cls: "local", summary: "self-update the CLI, daemon, and bundles" }
21
+ // Memory & recall the product's core write/read/lifecycle surface.
22
+ { verb: "remember", cls: "storage", group: "memory", summary: "write a memory through the daemon (--type fact|convention|preference|decision|gotcha|reference)" },
23
+ { verb: "recall", cls: "storage", group: "memory", summary: "recall memories through the daemon" },
24
+ { verb: "memory", cls: "storage", group: "memory", summary: "lifecycle: conflicts (list/resolve), stale-refs (list), inspect <id> --lifecycle (058d)" },
25
+ { verb: "sessions", cls: "storage", group: "memory", summary: "list/prune captured sessions through the daemon" },
26
+ { verb: "pollinate", cls: "storage", group: "memory", summary: "trigger a pollinating consolidation pass on the daemon (009/026)" },
27
+ { verb: "maintenance", cls: "storage", group: "memory", summary: "run version-history compaction over version-bumped tables (030)" },
28
+ // Knowledge & skills — skills, assets, ontology, the codebase graph, and goals.
29
+ { verb: "skill", cls: "storage", group: "knowledge", summary: "skillify scope/pull/unpull/force/promote through the daemon (promote = cross-project, 049c)" },
30
+ { verb: "skillify", cls: "storage", group: "knowledge", summary: "pull team skills from the daemon (016c)" },
31
+ { verb: "asset", cls: "storage", group: "knowledge", summary: "register/promote/demote/style skills+agents through the tier\xD7style lattice (033)" },
32
+ { verb: "ontology", cls: "storage", group: "knowledge", summary: "inspect/propose ontology changes through the daemon" },
33
+ { verb: "graph", cls: "storage", group: "knowledge", summary: "build/query the codebase graph through the daemon" },
34
+ { verb: "sources", cls: "storage", group: "knowledge", summary: "connect/index/purge sources through the daemon" },
35
+ { verb: "goal", cls: "storage", group: "knowledge", summary: "manage goals/KPIs through the daemon" },
36
+ // Agents, routing & config agent turns, inference routes, secrets, and vault settings.
37
+ { verb: "agent", cls: "storage", group: "agents", summary: "run an agent turn through the daemon" },
38
+ { verb: "route", cls: "storage", group: "agents", summary: "manage inference routes through the daemon" },
39
+ { verb: "secret", cls: "storage", group: "agents", summary: "manage named secrets through the daemon" },
40
+ { verb: "settings", cls: "storage", group: "agents", summary: "get/set/list vault settings + provider\u2192model selector through the daemon" },
41
+ // Account & workspaces — auth, identity, and the org/workspace/project scope surface.
42
+ { verb: "login", cls: "auth", group: "account", summary: "authenticate via device flow, or --token <key> for headless (023)" },
43
+ { verb: "logout", cls: "auth", group: "account", summary: "remove the shared credentials and sign out (023)" },
44
+ { verb: "whoami", cls: "auth", group: "account", summary: "show the authenticated user, org, and workspace (GET /me)" },
45
+ { verb: "org", cls: "auth", group: "account", summary: "list/switch org (passthrough to the auth dispatcher)" },
46
+ { verb: "workspace", cls: "auth", group: "account", summary: "list/switch/use workspace (passthrough to the auth dispatcher)" },
47
+ { verb: "workspaces", cls: "auth", group: "account", summary: "list workspaces in the active org (alias of `workspace list`)" },
48
+ { verb: "project", cls: "auth", group: "account", summary: "list/bind/use projects + show the resolved per-folder scope (049d)" },
49
+ // Setup & system install/onboard, daemon lifecycle, dashboard, hooks, telemetry, update.
50
+ { verb: "setup", cls: "local", group: "system", summary: "detect assistants, wire hooks, bring up the daemon" },
51
+ { verb: "install", cls: "local", group: "system", summary: "bring up the daemon (health-gated) + open the dashboard (PRD-050a)" },
52
+ { verb: "status", cls: "local", group: "system", summary: "daemon connectivity + login + D1\u2013D5 environment health" },
53
+ { verb: "daemon", cls: "local", group: "system", summary: "start | stop | status the loopback daemon (3850)" },
54
+ { verb: "dashboard", cls: "local", group: "system", summary: "launch the daemon-served dashboard (020b)" },
55
+ { verb: "hook", cls: "local", group: "system", summary: "inspect/wire harness hooks" },
56
+ { verb: "telemetry", cls: "local", group: "system", summary: "show exactly what adoption telemetry has been / would be sent (--show, PRD-050e)" },
57
+ { verb: "update", cls: "local", group: "system", summary: "self-update the CLI, daemon, and bundles" },
58
+ { verb: "uninstall", cls: "local", group: "system", summary: "reverse only Honeycomb's changes" }
41
59
  ]);
42
60
  function lookupVerb(verb) {
43
61
  return VERB_TABLE.find((v) => v.verb === verb);
@@ -514,6 +532,15 @@ var PROVIDER_CATALOG = Object.freeze([
514
532
  // Suggestions only — OpenRouter accepts a free-form `vendor/model` id (passthrough).
515
533
  models: Object.freeze(["anthropic/claude-sonnet-4.6", "openai/gpt-4o"]),
516
534
  openEnded: true
535
+ }),
536
+ Object.freeze({
537
+ // PRD-063a: Portkey is a GATEWAY, not a model vendor — its `portkey.config` id is
538
+ // free-form (a config or virtual-key id copied from the Portkey dashboard), so it is
539
+ // `openEnded: true` like OpenRouter and carries NO curated model list of its own.
540
+ id: "portkey",
541
+ label: "Portkey",
542
+ models: Object.freeze([]),
543
+ openEnded: true
517
544
  })
518
545
  ]);
519
546
  function providerEntry(provider) {
@@ -16488,10 +16515,10 @@ function flagValue3(argv, flag) {
16488
16515
  return v !== void 0 && !v.startsWith("--") ? v : void 0;
16489
16516
  }
16490
16517
  function parseSkillId(raw) {
16491
- const sep3 = raw.lastIndexOf("--");
16492
- if (sep3 <= 0)
16518
+ const sep4 = raw.lastIndexOf("--");
16519
+ if (sep4 <= 0)
16493
16520
  return { name: raw, author: "" };
16494
- return { name: raw.slice(0, sep3), author: raw.slice(sep3 + 2) };
16521
+ return { name: raw.slice(0, sep4), author: raw.slice(sep4 + 2) };
16495
16522
  }
16496
16523
  function buildSkillRequest(argv) {
16497
16524
  const sub = subcommandOf(argv);
@@ -17235,7 +17262,7 @@ function buildAllowedProperties(input) {
17235
17262
  }
17236
17263
  var systemTelemetryClock = () => (/* @__PURE__ */ new Date()).toISOString();
17237
17264
  var DEFAULT_EMIT_TIMEOUT_MS = 2e3;
17238
- var HONEYCOMB_VERSION = true ? "0.1.8" : "0.0.0-dev";
17265
+ var HONEYCOMB_VERSION = true ? "0.1.10" : "0.0.0-dev";
17239
17266
  async function emitTelemetry(event, opts, deps = {}) {
17240
17267
  const env = deps.env ?? process.env;
17241
17268
  const key = deps.posthogKey ?? POSTHOG_KEY;
@@ -17378,7 +17405,7 @@ function renderGlassBoxText(view) {
17378
17405
  // dist/src/shared/constants.js
17379
17406
  var DAEMON_PORT = 3850;
17380
17407
  var DAEMON_HOST = "127.0.0.1";
17381
- var HONEYCOMB_VERSION2 = true ? "0.1.8" : "0.0.0-dev";
17408
+ var HONEYCOMB_VERSION2 = true ? "0.1.10" : "0.0.0-dev";
17382
17409
  var PRODUCT_SLUG = "honeycomb";
17383
17410
 
17384
17411
  // dist/src/commands/install.js
@@ -17461,6 +17488,19 @@ function openDashboardWithFallback(opener, out) {
17461
17488
  const opened = opener(loopback);
17462
17489
  out(opened ? `\u2192 opening dashboard at ${loopback}\u2026` : `\u2192 dashboard is ready at ${loopback} (open it in your browser).`);
17463
17490
  }
17491
+ async function reportDaemonSupervision(deps, out) {
17492
+ if (deps.lifecycle === void 0)
17493
+ return;
17494
+ try {
17495
+ const status3 = await deps.lifecycle.status();
17496
+ if (status3.serviceManager !== void 0) {
17497
+ out(`\u2713 daemon registered as an OS service (${status3.serviceManager}): it restarts on crash and starts on boot.`);
17498
+ } else {
17499
+ out("note: daemon running as a detached process (OS service registration unavailable on this host).");
17500
+ }
17501
+ } catch {
17502
+ }
17503
+ }
17464
17504
  async function runInstallCommand(argv, deps) {
17465
17505
  const out = deps.out ?? ((line) => console.log(line));
17466
17506
  const opener = deps.openDashboard ?? openLocalDashboardUrl;
@@ -17477,6 +17517,7 @@ async function runInstallCommand(argv, deps) {
17477
17517
  return { exitCode: 1 };
17478
17518
  }
17479
17519
  out(`\u2713 daemon up on ${DAEMON_HOST}:${DAEMON_PORT}.`);
17520
+ await reportDaemonSupervision(deps, out);
17480
17521
  const wrote = writeInstalledMarker(ref, deps.dir, out);
17481
17522
  if (wrote)
17482
17523
  out(`\u2713 onboarding marked installed (ref: ${ref}).`);
@@ -17535,12 +17576,26 @@ function parseInvocation(argv) {
17535
17576
  const tail = verb === "" ? argv.slice(i) : argv.slice(i + 1);
17536
17577
  return { verb, argv: tail, flags };
17537
17578
  }
17579
+ var HONEYCOMB_BANNER = [
17580
+ " __ __ __",
17581
+ " / \\__/ \\__/ \\ H O N E Y C O M B",
17582
+ " \\__/ \\__/ \\__/",
17583
+ " / \\__/ \\__/ \\ shared agent memory for your coding tools",
17584
+ " \\__/ \\__/ \\__/"
17585
+ ].join("\n");
17538
17586
  function usageText() {
17539
- const lines = [`${PRODUCT_SLUG} v${HONEYCOMB_VERSION2}`, "", "usage: honeycomb <command> [options]", "", "commands:"];
17540
- for (const spec of VERB_TABLE) {
17541
- lines.push(` ${spec.verb.padEnd(11)} ${spec.summary}`);
17587
+ const lines = [HONEYCOMB_BANNER, "", `${PRODUCT_SLUG} v${HONEYCOMB_VERSION2}`, "", "usage: honeycomb <command> [options]", ""];
17588
+ const pad = VERB_TABLE.reduce((w, s2) => Math.max(w, s2.verb.length), 0) + 2;
17589
+ for (const { key, label } of VERB_GROUPS) {
17590
+ const rows = VERB_TABLE.filter((s2) => s2.group === key);
17591
+ if (rows.length === 0)
17592
+ continue;
17593
+ lines.push(`${label}:`);
17594
+ for (const spec of rows)
17595
+ lines.push(` ${spec.verb.padEnd(pad)}${spec.summary}`);
17596
+ lines.push("");
17542
17597
  }
17543
- lines.push("", "global flags: --help --version --json --dry-run");
17598
+ lines.push("global flags: --help --version --json --dry-run");
17544
17599
  return lines.join("\n");
17545
17600
  }
17546
17601
  function lifecycleOf(deps) {
@@ -17663,13 +17718,14 @@ function createDispatcher() {
17663
17718
 
17664
17719
  // dist/src/cli/runtime.js
17665
17720
  import { spawn } from "node:child_process";
17666
- import { homedir as homedir12 } from "node:os";
17667
- import { dirname as dirname8, join as join12, resolve as resolve7 } from "node:path";
17721
+ import { mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync2 } from "node:fs";
17722
+ import { homedir as homedir13 } from "node:os";
17723
+ import { dirname as dirname8, join as join13, resolve as resolve8 } from "node:path";
17668
17724
  import { fileURLToPath as fileURLToPath3 } from "node:url";
17669
17725
 
17670
17726
  // dist/src/daemon/runtime/auth/deeplake-issuer.js
17671
17727
  import { execFileSync as execFileSync4 } from "node:child_process";
17672
- var realSleeper = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
17728
+ var realSleeper = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
17673
17729
  var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
17674
17730
  var DEEPLAKE_ORG_HEADER = "X-Activeloop-Org-Id";
17675
17731
  var DEEPLAKE_CLIENT_VALUE = "honeycomb";
@@ -20119,6 +20175,273 @@ async function launchDashboard(options = {}) {
20119
20175
  return renderDashboard(source);
20120
20176
  }
20121
20177
 
20178
+ // dist/src/cli/daemon-service.js
20179
+ import { createRequire } from "node:module";
20180
+ import { homedir as homedir12 } from "node:os";
20181
+ import { join as join12, normalize, resolve as resolve7, sep as sep3 } from "node:path";
20182
+ var SERVICE_LABEL = "ai.honeycomb.daemon";
20183
+ var SERVICE_TASK_NAME = "HoneycombDaemon";
20184
+ var SERVICE_MODE_ENV = "HONEYCOMB_DAEMON_SERVICE";
20185
+ function detectServiceManager(env = process.env, platform2 = process.platform) {
20186
+ if ((env[SERVICE_MODE_ENV] ?? "").trim().toLowerCase() === "spawn")
20187
+ return null;
20188
+ if (platform2 === "darwin")
20189
+ return "launchd";
20190
+ if (platform2 === "win32")
20191
+ return "schtasks";
20192
+ if (platform2 === "linux") {
20193
+ const xdg = (env.XDG_RUNTIME_DIR ?? "").trim();
20194
+ if (xdg.length > 0)
20195
+ return "systemd-user";
20196
+ return null;
20197
+ }
20198
+ return null;
20199
+ }
20200
+ function defaultServiceRunner() {
20201
+ const require2 = createRequire(import.meta.url);
20202
+ const cp = require2("node:child_process");
20203
+ const fs = require2("node:fs");
20204
+ return {
20205
+ run(cmd, args) {
20206
+ const out = cp.execFileSync(cmd, [...args], {
20207
+ encoding: "utf8",
20208
+ stdio: ["ignore", "pipe", "pipe"],
20209
+ timeout: 15e3,
20210
+ windowsHide: true
20211
+ });
20212
+ return typeof out === "string" ? out.trim() : "";
20213
+ },
20214
+ writeFile(path, contents) {
20215
+ fs.mkdirSync(join12(path, ".."), { recursive: true });
20216
+ fs.writeFileSync(path, contents, { encoding: "utf8" });
20217
+ },
20218
+ removeFile(path) {
20219
+ try {
20220
+ fs.rmSync(path, { force: true });
20221
+ } catch {
20222
+ }
20223
+ },
20224
+ fileExists(path) {
20225
+ try {
20226
+ return fs.existsSync(path);
20227
+ } catch {
20228
+ return false;
20229
+ }
20230
+ }
20231
+ };
20232
+ }
20233
+ function containedUnitPath(home, segments) {
20234
+ const resolvedHome = normalize(resolve7(home));
20235
+ const candidate = normalize(resolve7(resolvedHome, ...segments));
20236
+ const homeWithSep = resolvedHome.endsWith(sep3) ? resolvedHome : resolvedHome + sep3;
20237
+ if (candidate !== resolvedHome && !candidate.startsWith(homeWithSep)) {
20238
+ throw new Error("resolved service unit path escapes the home dir");
20239
+ }
20240
+ return candidate;
20241
+ }
20242
+ function launchdPlistPath(home) {
20243
+ return containedUnitPath(home, ["Library", "LaunchAgents", `${SERVICE_LABEL}.plist`]);
20244
+ }
20245
+ function systemdUnitPath(home) {
20246
+ return containedUnitPath(home, [".config", "systemd", "user", `${SERVICE_LABEL}.service`]);
20247
+ }
20248
+ function xmlEscape(value) {
20249
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
20250
+ }
20251
+ function renderLaunchdPlist(spec) {
20252
+ const argv = [spec.nodePath, ...spec.nodeFlags, spec.entry];
20253
+ const argvXml = argv.map((a) => ` <string>${xmlEscape(a)}</string>`).join("\n");
20254
+ return `<?xml version="1.0" encoding="UTF-8"?>
20255
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
20256
+ <plist version="1.0">
20257
+ <dict>
20258
+ <key>Label</key>
20259
+ <string>${SERVICE_LABEL}</string>
20260
+ <key>ProgramArguments</key>
20261
+ <array>
20262
+ ${argvXml}
20263
+ </array>
20264
+ <key>WorkingDirectory</key>
20265
+ <string>${xmlEscape(spec.workspace)}</string>
20266
+ <key>EnvironmentVariables</key>
20267
+ <dict>
20268
+ <key>HONEYCOMB_WORKSPACE</key>
20269
+ <string>${xmlEscape(spec.workspace)}</string>
20270
+ </dict>
20271
+ <key>RunAtLoad</key>
20272
+ <true/>
20273
+ <key>KeepAlive</key>
20274
+ <true/>
20275
+ <key>ProcessType</key>
20276
+ <string>Background</string>
20277
+ </dict>
20278
+ </plist>
20279
+ `;
20280
+ }
20281
+ function renderSystemdUnit(spec) {
20282
+ const execStart = [spec.nodePath, ...spec.nodeFlags, spec.entry].map((a) => `"${a}"`).join(" ");
20283
+ return `[Unit]
20284
+ Description=Honeycomb primary daemon (127.0.0.1:3850)
20285
+ After=network.target
20286
+
20287
+ [Service]
20288
+ Type=simple
20289
+ ExecStart=${execStart}
20290
+ WorkingDirectory=${spec.workspace}
20291
+ Environment=HONEYCOMB_WORKSPACE=${spec.workspace}
20292
+ Restart=always
20293
+ RestartSec=5
20294
+
20295
+ [Install]
20296
+ WantedBy=default.target
20297
+ `;
20298
+ }
20299
+ function assertCmdSafe(value) {
20300
+ if (/[&|<>^"%\r\n]/.test(value)) {
20301
+ throw new Error("unsafe character in service path; refusing to build schtasks /TR command");
20302
+ }
20303
+ }
20304
+ function buildSchtasksCreateArgs(spec) {
20305
+ assertCmdSafe(spec.workspace);
20306
+ assertCmdSafe(spec.nodePath);
20307
+ assertCmdSafe(spec.entry);
20308
+ const nodeFlags = spec.nodeFlags.length > 0 ? `${spec.nodeFlags.join(" ")} ` : "";
20309
+ const tr = `cmd /c "cd /d "${spec.workspace}" && set HONEYCOMB_WORKSPACE=${spec.workspace} && "${spec.nodePath}" ${nodeFlags}"${spec.entry}""`;
20310
+ return [
20311
+ "/Create",
20312
+ "/TN",
20313
+ SERVICE_TASK_NAME,
20314
+ "/TR",
20315
+ tr,
20316
+ "/SC",
20317
+ "ONLOGON",
20318
+ "/RL",
20319
+ "LIMITED",
20320
+ "/F"
20321
+ ];
20322
+ }
20323
+ function specHome(spec) {
20324
+ return spec.home ?? homedir12();
20325
+ }
20326
+ function createDaemonServiceController(manager, runner = defaultServiceRunner()) {
20327
+ if (manager === "launchd")
20328
+ return launchdController(runner);
20329
+ if (manager === "systemd-user")
20330
+ return systemdController(runner);
20331
+ return schtasksController(runner);
20332
+ }
20333
+ function launchdController(runner) {
20334
+ function domain2() {
20335
+ return `gui/${process.getuid?.() ?? 501}`;
20336
+ }
20337
+ return {
20338
+ manager: "launchd",
20339
+ register(spec) {
20340
+ const path = launchdPlistPath(specHome(spec));
20341
+ runner.writeFile(path, renderLaunchdPlist(spec));
20342
+ runner.run("launchctl", ["bootstrap", domain2(), path]);
20343
+ return { ok: true, manager: "launchd" };
20344
+ },
20345
+ unregister(spec) {
20346
+ const path = launchdPlistPath(specHome(spec));
20347
+ try {
20348
+ runner.run("launchctl", ["bootout", `${domain2()}/${SERVICE_LABEL}`]);
20349
+ } catch {
20350
+ }
20351
+ runner.removeFile(path);
20352
+ return { ok: true, manager: "launchd" };
20353
+ },
20354
+ restart(_spec) {
20355
+ runner.run("launchctl", ["kickstart", "-k", `${domain2()}/${SERVICE_LABEL}`]);
20356
+ return { ok: true, manager: "launchd" };
20357
+ },
20358
+ stop(_spec) {
20359
+ runner.run("launchctl", ["kill", "SIGTERM", `${domain2()}/${SERVICE_LABEL}`]);
20360
+ return { ok: true, manager: "launchd" };
20361
+ },
20362
+ isRegistered(spec) {
20363
+ return runner.fileExists(launchdPlistPath(specHome(spec)));
20364
+ }
20365
+ };
20366
+ }
20367
+ function systemdController(runner) {
20368
+ const unit = `${SERVICE_LABEL}.service`;
20369
+ return {
20370
+ manager: "systemd-user",
20371
+ register(spec) {
20372
+ const path = systemdUnitPath(specHome(spec));
20373
+ runner.writeFile(path, renderSystemdUnit(spec));
20374
+ runner.run("systemctl", ["--user", "daemon-reload"]);
20375
+ runner.run("systemctl", ["--user", "enable", "--now", unit]);
20376
+ return { ok: true, manager: "systemd-user" };
20377
+ },
20378
+ unregister(spec) {
20379
+ try {
20380
+ runner.run("systemctl", ["--user", "disable", "--now", unit]);
20381
+ } catch {
20382
+ }
20383
+ runner.removeFile(systemdUnitPath(specHome(spec)));
20384
+ try {
20385
+ runner.run("systemctl", ["--user", "daemon-reload"]);
20386
+ } catch {
20387
+ }
20388
+ return { ok: true, manager: "systemd-user" };
20389
+ },
20390
+ restart(_spec) {
20391
+ runner.run("systemctl", ["--user", "restart", unit]);
20392
+ return { ok: true, manager: "systemd-user" };
20393
+ },
20394
+ stop(_spec) {
20395
+ runner.run("systemctl", ["--user", "stop", unit]);
20396
+ return { ok: true, manager: "systemd-user" };
20397
+ },
20398
+ isRegistered(spec) {
20399
+ return runner.fileExists(systemdUnitPath(specHome(spec)));
20400
+ }
20401
+ };
20402
+ }
20403
+ function schtasksController(runner) {
20404
+ return {
20405
+ manager: "schtasks",
20406
+ register(spec) {
20407
+ runner.run("schtasks", buildSchtasksCreateArgs(spec));
20408
+ runner.run("schtasks", ["/Run", "/TN", SERVICE_TASK_NAME]);
20409
+ return { ok: true, manager: "schtasks" };
20410
+ },
20411
+ unregister(_spec) {
20412
+ try {
20413
+ runner.run("schtasks", ["/End", "/TN", SERVICE_TASK_NAME]);
20414
+ } catch {
20415
+ }
20416
+ try {
20417
+ runner.run("schtasks", ["/Delete", "/TN", SERVICE_TASK_NAME, "/F"]);
20418
+ } catch {
20419
+ }
20420
+ return { ok: true, manager: "schtasks" };
20421
+ },
20422
+ restart(spec) {
20423
+ try {
20424
+ runner.run("schtasks", ["/End", "/TN", SERVICE_TASK_NAME]);
20425
+ } catch {
20426
+ }
20427
+ runner.run("schtasks", ["/Run", "/TN", SERVICE_TASK_NAME]);
20428
+ return { ok: true, manager: "schtasks" };
20429
+ },
20430
+ stop(_spec) {
20431
+ runner.run("schtasks", ["/End", "/TN", SERVICE_TASK_NAME]);
20432
+ return { ok: true, manager: "schtasks" };
20433
+ },
20434
+ isRegistered(_spec) {
20435
+ try {
20436
+ runner.run("schtasks", ["/Query", "/TN", SERVICE_TASK_NAME]);
20437
+ return true;
20438
+ } catch {
20439
+ return false;
20440
+ }
20441
+ }
20442
+ };
20443
+ }
20444
+
20122
20445
  // dist/src/cli/runtime.js
20123
20446
  function daemonBaseUrl2() {
20124
20447
  return `http://${DAEMON_HOST}:${DAEMON_PORT}`;
@@ -20136,19 +20459,38 @@ function tenancyHeaders(creds) {
20136
20459
  var DAEMON_NODE_FLAGS = ["--experimental-sqlite"];
20137
20460
  function resolveDaemonEntry() {
20138
20461
  const here = dirname8(fileURLToPath3(import.meta.url));
20139
- const bundledSibling = resolve7(here, "..", "daemon", "index.js");
20140
- const devSibling = resolve7(here, "..", "..", "..", "daemon", "index.js");
20462
+ const bundledSibling = resolve8(here, "..", "daemon", "index.js");
20463
+ const devSibling = resolve8(here, "..", "..", "..", "daemon", "index.js");
20141
20464
  return process.env.HONEYCOMB_DAEMON_ENTRY ?? bundledSibling ?? devSibling;
20142
20465
  }
20143
20466
  var DEFAULT_START_TIMEOUT_MS = 45e3;
20144
20467
  var START_POLL_INTERVAL_MS = 150;
20145
20468
  function runtimeDir() {
20146
- return join12(homedir12(), ".honeycomb");
20469
+ return join13(homedir13(), ".honeycomb");
20470
+ }
20471
+ function resolveDaemonWorkspace() {
20472
+ const fromEnv = process.env.HONEYCOMB_WORKSPACE;
20473
+ const candidates = [fromEnv !== void 0 && fromEnv.length > 0 ? fromEnv : process.cwd(), runtimeDir()];
20474
+ for (const dir of candidates) {
20475
+ if (canWriteDir(dir))
20476
+ return dir;
20477
+ }
20478
+ return runtimeDir();
20479
+ }
20480
+ function canWriteDir(dir) {
20481
+ try {
20482
+ mkdirSync8(dir, { recursive: true });
20483
+ const probe = mkdtempSync(join13(dir, ".hc-spawn-probe-"));
20484
+ rmSync2(probe, { recursive: true, force: true });
20485
+ return true;
20486
+ } catch {
20487
+ return false;
20488
+ }
20147
20489
  }
20148
20490
  async function readDaemonPid() {
20149
20491
  const { readFile: readFile2 } = await import("node:fs/promises");
20150
20492
  try {
20151
- const raw = (await readFile2(join12(runtimeDir(), "daemon.pid"), "utf8")).trim();
20493
+ const raw = (await readFile2(join13(runtimeDir(), "daemon.pid"), "utf8")).trim();
20152
20494
  const pid = Number.parseInt(raw, 10);
20153
20495
  return Number.isInteger(pid) && pid > 0 ? pid : null;
20154
20496
  } catch {
@@ -20163,44 +20505,128 @@ function isPidAlive(pid) {
20163
20505
  return err?.code === "EPERM";
20164
20506
  }
20165
20507
  }
20166
- function buildDaemonLifecycle(client) {
20508
+ function buildServiceSpec() {
20509
+ return {
20510
+ nodePath: process.execPath,
20511
+ entry: resolveDaemonEntry(),
20512
+ nodeFlags: DAEMON_NODE_FLAGS,
20513
+ workspace: resolveDaemonWorkspace()
20514
+ };
20515
+ }
20516
+ async function spawnDaemonAndWait(client) {
20517
+ const entry = resolveDaemonEntry();
20518
+ const workspace = resolveDaemonWorkspace();
20519
+ const child = spawn(process.execPath, [...DAEMON_NODE_FLAGS, entry], {
20520
+ detached: true,
20521
+ stdio: "ignore",
20522
+ cwd: workspace,
20523
+ env: { ...process.env, HONEYCOMB_WORKSPACE: workspace },
20524
+ // Hide the transient console window on Windows: a detached daemon spawn is never an
20525
+ // interactive terminal the user needs to see.
20526
+ windowsHide: true
20527
+ });
20528
+ child.unref();
20529
+ const deadline = Date.now() + DEFAULT_START_TIMEOUT_MS;
20530
+ while (Date.now() < deadline) {
20531
+ await new Promise((r) => setTimeout(r, START_POLL_INTERVAL_MS));
20532
+ if (await client.ping())
20533
+ return { started: true, alreadyRunning: false };
20534
+ }
20535
+ return { started: false, alreadyRunning: false };
20536
+ }
20537
+ async function waitForHealth(client) {
20538
+ const deadline = Date.now() + DEFAULT_START_TIMEOUT_MS;
20539
+ while (Date.now() < deadline) {
20540
+ if (await client.ping())
20541
+ return true;
20542
+ await new Promise((r) => setTimeout(r, START_POLL_INTERVAL_MS));
20543
+ }
20544
+ return false;
20545
+ }
20546
+ async function signalDaemonStop() {
20547
+ const pid = await readDaemonPid();
20548
+ if (pid === null || !isPidAlive(pid))
20549
+ return false;
20550
+ try {
20551
+ process.kill(pid, "SIGTERM");
20552
+ return true;
20553
+ } catch {
20554
+ return false;
20555
+ }
20556
+ }
20557
+ function buildDaemonLifecycle(client, options = {}) {
20558
+ const manager = options.serviceManager !== void 0 ? options.serviceManager : detectServiceManager();
20559
+ const controllerFor = options.controllerFor ?? ((m) => createDaemonServiceController(m));
20560
+ let controller = null;
20561
+ function serviceController() {
20562
+ if (manager === null)
20563
+ return null;
20564
+ if (controller === null) {
20565
+ try {
20566
+ controller = controllerFor(manager);
20567
+ } catch {
20568
+ return null;
20569
+ }
20570
+ }
20571
+ return controller;
20572
+ }
20167
20573
  return {
20168
20574
  async start() {
20169
20575
  if (await client.ping())
20170
20576
  return { started: false, alreadyRunning: true };
20171
- const entry = resolveDaemonEntry();
20172
- const child = spawn(process.execPath, [...DAEMON_NODE_FLAGS, entry], {
20173
- detached: true,
20174
- stdio: "ignore",
20175
- env: process.env,
20176
- // Hide the transient console window on Windows — a detached daemon spawn is never
20177
- // an interactive terminal the user needs to see.
20178
- windowsHide: true
20179
- });
20180
- child.unref();
20181
- const deadline = Date.now() + DEFAULT_START_TIMEOUT_MS;
20182
- while (Date.now() < deadline) {
20183
- await new Promise((r) => setTimeout(r, START_POLL_INTERVAL_MS));
20184
- if (await client.ping())
20185
- return { started: true, alreadyRunning: false };
20186
- }
20187
- return { started: false, alreadyRunning: false };
20577
+ const svc = serviceController();
20578
+ if (svc !== null) {
20579
+ try {
20580
+ svc.register(buildServiceSpec());
20581
+ if (await waitForHealth(client))
20582
+ return { started: true, alreadyRunning: false };
20583
+ return { started: false, alreadyRunning: false };
20584
+ } catch {
20585
+ }
20586
+ }
20587
+ return spawnDaemonAndWait(client);
20188
20588
  },
20189
20589
  async stop() {
20190
- const pid = await readDaemonPid();
20191
- if (pid === null || !isPidAlive(pid))
20192
- return { stopped: false };
20193
- try {
20194
- process.kill(pid, "SIGTERM");
20195
- return { stopped: true };
20196
- } catch {
20197
- return { stopped: false };
20590
+ const svc = serviceController();
20591
+ if (svc !== null) {
20592
+ try {
20593
+ svc.stop(buildServiceSpec());
20594
+ return { stopped: true };
20595
+ } catch {
20596
+ }
20198
20597
  }
20598
+ return { stopped: await signalDaemonStop() };
20199
20599
  },
20200
20600
  async status() {
20201
20601
  const pid = await readDaemonPid();
20202
20602
  const running = pid !== null && isPidAlive(pid);
20203
- return running ? { running, pid, port: DAEMON_PORT } : { running: false, port: DAEMON_PORT };
20603
+ const svc = serviceController();
20604
+ let serviceManager;
20605
+ if (svc !== null) {
20606
+ try {
20607
+ if (svc.isRegistered(buildServiceSpec()))
20608
+ serviceManager = svc.manager;
20609
+ } catch {
20610
+ }
20611
+ }
20612
+ if (running) {
20613
+ return serviceManager !== void 0 ? { running, pid, port: DAEMON_PORT, serviceManager } : { running, pid, port: DAEMON_PORT };
20614
+ }
20615
+ return serviceManager !== void 0 ? { running: false, port: DAEMON_PORT, serviceManager } : { running: false, port: DAEMON_PORT };
20616
+ },
20617
+ async restart() {
20618
+ const svc = serviceController();
20619
+ if (svc !== null && svc.isRegistered(buildServiceSpec())) {
20620
+ try {
20621
+ svc.restart(buildServiceSpec());
20622
+ const ok2 = await waitForHealth(client);
20623
+ return { restarted: ok2, viaService: true };
20624
+ } catch {
20625
+ }
20626
+ }
20627
+ await signalDaemonStop();
20628
+ const { started, alreadyRunning } = await spawnDaemonAndWait(client);
20629
+ return { restarted: started || alreadyRunning, viaService: false };
20204
20630
  }
20205
20631
  };
20206
20632
  }
@@ -20314,7 +20740,18 @@ async function main(argv = process.argv.slice(2)) {
20314
20740
  const result = await dispatcher.dispatch(inv, deps);
20315
20741
  return result.exitCode;
20316
20742
  }
20317
- if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("cli.js")) {
20743
+ function isCliEntry(importMetaUrl, argv1) {
20744
+ if (typeof argv1 !== "string" || argv1.length === 0)
20745
+ return false;
20746
+ try {
20747
+ if (importMetaUrl === pathToFileURL(argv1).href)
20748
+ return true;
20749
+ return importMetaUrl === pathToFileURL(realpathSync(argv1)).href;
20750
+ } catch {
20751
+ return false;
20752
+ }
20753
+ }
20754
+ if (isCliEntry(import.meta.url, process.argv[1])) {
20318
20755
  main().then(async (code) => {
20319
20756
  await finalizeCliExit();
20320
20757
  process.exitCode = code;
@@ -20326,5 +20763,6 @@ if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith
20326
20763
  });
20327
20764
  }
20328
20765
  export {
20766
+ isCliEntry,
20329
20767
  main
20330
20768
  };