@ouro.bot/cli 0.1.0-alpha.1 → 0.1.0-alpha.100

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.
Files changed (132) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +70 -9
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +147 -205
  5. package/assets/ouroboros.png +0 -0
  6. package/changelog.json +596 -0
  7. package/dist/heart/active-work.js +251 -0
  8. package/dist/heart/bridges/manager.js +358 -0
  9. package/dist/heart/bridges/state-machine.js +135 -0
  10. package/dist/heart/bridges/store.js +123 -0
  11. package/dist/heart/commitments.js +109 -0
  12. package/dist/heart/config.js +102 -23
  13. package/dist/heart/core.js +512 -94
  14. package/dist/heart/cross-chat-delivery.js +146 -0
  15. package/dist/heart/daemon/agent-discovery.js +81 -0
  16. package/dist/heart/daemon/auth-flow.js +430 -0
  17. package/dist/heart/daemon/daemon-cli.js +1935 -185
  18. package/dist/heart/daemon/daemon-entry.js +55 -6
  19. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  20. package/dist/heart/daemon/daemon.js +218 -9
  21. package/dist/heart/daemon/hatch-animation.js +35 -0
  22. package/dist/heart/daemon/hatch-flow.js +10 -83
  23. package/dist/heart/daemon/hatch-specialist.js +6 -1
  24. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  25. package/dist/heart/daemon/launchd.js +159 -0
  26. package/dist/heart/daemon/log-tailer.js +147 -0
  27. package/dist/heart/daemon/message-router.js +17 -8
  28. package/dist/heart/daemon/os-cron.js +260 -0
  29. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  30. package/dist/heart/daemon/ouro-bot-wrapper.js +4 -3
  31. package/dist/heart/daemon/ouro-path-installer.js +260 -0
  32. package/dist/heart/daemon/ouro-uti.js +11 -2
  33. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  34. package/dist/heart/daemon/process-manager.js +32 -2
  35. package/dist/heart/daemon/run-hooks.js +37 -0
  36. package/dist/heart/daemon/runtime-logging.js +61 -14
  37. package/dist/heart/daemon/runtime-metadata.js +219 -0
  38. package/dist/heart/daemon/runtime-mode.js +67 -0
  39. package/dist/heart/daemon/sense-manager.js +307 -0
  40. package/dist/heart/daemon/skill-management-installer.js +94 -0
  41. package/dist/heart/daemon/socket-client.js +202 -0
  42. package/dist/heart/daemon/specialist-orchestrator.js +129 -0
  43. package/dist/heart/daemon/specialist-prompt.js +99 -0
  44. package/dist/heart/daemon/specialist-tools.js +283 -0
  45. package/dist/heart/daemon/staged-restart.js +114 -0
  46. package/dist/heart/daemon/task-scheduler.js +4 -1
  47. package/dist/heart/daemon/thoughts.js +507 -0
  48. package/dist/heart/daemon/update-checker.js +111 -0
  49. package/dist/heart/daemon/update-hooks.js +138 -0
  50. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  51. package/dist/heart/delegation.js +62 -0
  52. package/dist/heart/identity.js +153 -23
  53. package/dist/heart/kicks.js +1 -19
  54. package/dist/heart/model-capabilities.js +48 -0
  55. package/dist/heart/obligations.js +191 -0
  56. package/dist/heart/progress-story.js +42 -0
  57. package/dist/heart/providers/anthropic.js +77 -9
  58. package/dist/heart/providers/azure.js +86 -7
  59. package/dist/heart/providers/github-copilot.js +149 -0
  60. package/dist/heart/providers/minimax.js +4 -0
  61. package/dist/heart/providers/openai-codex.js +12 -3
  62. package/dist/heart/safe-workspace.js +381 -0
  63. package/dist/heart/sense-truth.js +61 -0
  64. package/dist/heart/session-activity.js +169 -0
  65. package/dist/heart/session-recall.js +116 -0
  66. package/dist/heart/streaming.js +103 -22
  67. package/dist/heart/target-resolution.js +123 -0
  68. package/dist/heart/turn-coordinator.js +28 -0
  69. package/dist/mind/associative-recall.js +37 -4
  70. package/dist/mind/bundle-manifest.js +70 -0
  71. package/dist/mind/context.js +141 -11
  72. package/dist/mind/first-impressions.js +16 -2
  73. package/dist/mind/friends/channel.js +43 -0
  74. package/dist/mind/friends/group-context.js +144 -0
  75. package/dist/mind/friends/store-file.js +19 -0
  76. package/dist/mind/friends/trust-explanation.js +74 -0
  77. package/dist/mind/friends/types.js +9 -1
  78. package/dist/mind/memory.js +89 -26
  79. package/dist/mind/obligation-steering.js +31 -0
  80. package/dist/mind/pending.js +160 -0
  81. package/dist/mind/phrases.js +1 -0
  82. package/dist/mind/prompt-refresh.js +20 -0
  83. package/dist/mind/prompt.js +499 -8
  84. package/dist/mind/token-estimate.js +8 -12
  85. package/dist/nerves/cli-logging.js +15 -2
  86. package/dist/nerves/coverage/file-completeness.js +14 -4
  87. package/dist/nerves/coverage/run-artifacts.js +1 -1
  88. package/dist/nerves/index.js +12 -0
  89. package/dist/repertoire/ado-client.js +4 -2
  90. package/dist/repertoire/coding/feedback.js +210 -0
  91. package/dist/repertoire/coding/index.js +4 -1
  92. package/dist/repertoire/coding/manager.js +69 -4
  93. package/dist/repertoire/coding/spawner.js +21 -3
  94. package/dist/repertoire/coding/tools.js +105 -2
  95. package/dist/repertoire/data/ado-endpoints.json +188 -0
  96. package/dist/repertoire/guardrails.js +290 -0
  97. package/dist/repertoire/mcp-client.js +254 -0
  98. package/dist/repertoire/mcp-manager.js +195 -0
  99. package/dist/repertoire/skills.js +3 -26
  100. package/dist/repertoire/tasks/board.js +12 -0
  101. package/dist/repertoire/tasks/index.js +23 -9
  102. package/dist/repertoire/tasks/transitions.js +1 -2
  103. package/dist/repertoire/tools-base.js +770 -213
  104. package/dist/repertoire/tools-bluebubbles.js +93 -0
  105. package/dist/repertoire/tools-teams.js +58 -25
  106. package/dist/repertoire/tools.js +106 -53
  107. package/dist/senses/bluebubbles-client.js +484 -0
  108. package/dist/senses/bluebubbles-entry.js +13 -0
  109. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  110. package/dist/senses/bluebubbles-media.js +339 -0
  111. package/dist/senses/bluebubbles-model.js +261 -0
  112. package/dist/senses/bluebubbles-mutation-log.js +116 -0
  113. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  114. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  115. package/dist/senses/bluebubbles.js +1181 -0
  116. package/dist/senses/cli-layout.js +187 -0
  117. package/dist/senses/cli.js +452 -99
  118. package/dist/senses/continuity.js +94 -0
  119. package/dist/senses/debug-activity.js +154 -0
  120. package/dist/senses/inner-dialog-worker.js +47 -18
  121. package/dist/senses/inner-dialog.js +387 -70
  122. package/dist/senses/pipeline.js +307 -0
  123. package/dist/senses/session-lock.js +119 -0
  124. package/dist/senses/teams.js +574 -129
  125. package/dist/senses/trust-gate.js +112 -2
  126. package/package.json +16 -4
  127. package/subagents/README.md +4 -68
  128. package/dist/heart/daemon/subagent-installer.js +0 -125
  129. package/dist/inner-worker-entry.js +0 -4
  130. package/subagents/work-doer.md +0 -233
  131. package/subagents/work-merger.md +0 -593
  132. package/subagents/work-planner.md +0 -373
@@ -33,18 +33,24 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.writeSecretsFile = writeSecretsFile;
36
37
  exports.runHatchFlow = runHatchFlow;
37
38
  const fs = __importStar(require("fs"));
38
39
  const os = __importStar(require("os"));
39
40
  const path = __importStar(require("path"));
40
41
  const identity_1 = require("../identity");
42
+ const config_1 = require("../config");
41
43
  const runtime_1 = require("../../nerves/runtime");
44
+ const auth_flow_1 = require("./auth-flow");
42
45
  const hatch_specialist_1 = require("./hatch-specialist");
43
46
  function requiredCredentialKeys(provider) {
44
47
  if (provider === "anthropic")
45
48
  return ["setupToken"];
46
49
  if (provider === "openai-codex")
47
50
  return ["oauthAccessToken"];
51
+ /* v8 ignore next -- branch tested via requiredCredentialKeys unit test @preserve */
52
+ if (provider === "github-copilot")
53
+ return ["githubToken"];
48
54
  if (provider === "minimax")
49
55
  return ["apiKey"];
50
56
  return ["apiKey", "endpoint", "deployment"];
@@ -65,70 +71,8 @@ function validateCredentials(provider, credentials) {
65
71
  throw new Error(`Missing required credentials for ${provider}: ${missing.join(", ")}`);
66
72
  }
67
73
  }
68
- function buildSecretsTemplate() {
69
- return {
70
- providers: {
71
- azure: {
72
- modelName: "gpt-4o-mini",
73
- apiKey: "",
74
- endpoint: "",
75
- deployment: "",
76
- apiVersion: "2025-04-01-preview",
77
- },
78
- minimax: {
79
- model: "minimax-text-01",
80
- apiKey: "",
81
- },
82
- anthropic: {
83
- model: "claude-opus-4-6",
84
- setupToken: "",
85
- },
86
- "openai-codex": {
87
- model: "gpt-5.2",
88
- oauthAccessToken: "",
89
- },
90
- },
91
- teams: {
92
- clientId: "",
93
- clientSecret: "",
94
- tenantId: "",
95
- },
96
- oauth: {
97
- graphConnectionName: "graph",
98
- adoConnectionName: "ado",
99
- githubConnectionName: "",
100
- },
101
- teamsChannel: {
102
- skipConfirmation: true,
103
- port: 3978,
104
- },
105
- integrations: {
106
- perplexityApiKey: "",
107
- openaiEmbeddingsApiKey: "",
108
- },
109
- };
110
- }
111
74
  function writeSecretsFile(agentName, provider, credentials, secretsRoot) {
112
- const secrets = buildSecretsTemplate();
113
- if (provider === "anthropic") {
114
- secrets.providers.anthropic.setupToken = credentials.setupToken.trim();
115
- }
116
- else if (provider === "openai-codex") {
117
- secrets.providers["openai-codex"].oauthAccessToken = credentials.oauthAccessToken.trim();
118
- }
119
- else if (provider === "minimax") {
120
- secrets.providers.minimax.apiKey = credentials.apiKey.trim();
121
- }
122
- else {
123
- secrets.providers.azure.apiKey = credentials.apiKey.trim();
124
- secrets.providers.azure.endpoint = credentials.endpoint.trim();
125
- secrets.providers.azure.deployment = credentials.deployment.trim();
126
- }
127
- const secretsDir = path.join(secretsRoot, agentName);
128
- fs.mkdirSync(secretsDir, { recursive: true });
129
- const secretsPath = path.join(secretsDir, "secrets.json");
130
- fs.writeFileSync(secretsPath, `${JSON.stringify(secrets, null, 2)}\n`, "utf-8");
131
- return secretsPath;
75
+ return (0, auth_flow_1.writeProviderCredentials)(agentName, provider, credentials, { secretsRoot }).secretsPath;
132
76
  }
133
77
  function writeReadme(dir, purpose) {
134
78
  fs.mkdirSync(dir, { recursive: true });
@@ -137,14 +81,6 @@ function writeReadme(dir, purpose) {
137
81
  fs.writeFileSync(readmePath, `# ${path.basename(dir)}\n\n${purpose}\n`, "utf-8");
138
82
  }
139
83
  }
140
- function slugify(value) {
141
- const trimmed = value.trim().toLowerCase();
142
- const slug = trimmed
143
- .replace(/[^a-z0-9]+/g, "-")
144
- .replace(/^-+/, "")
145
- .replace(/-+$/, "");
146
- return slug || "friend";
147
- }
148
84
  function pad(value) {
149
85
  return String(value).padStart(2, "0");
150
86
  }
@@ -181,7 +117,8 @@ function writeFriendImprint(bundleRoot, humanName, now) {
181
117
  const friendsDir = path.join(bundleRoot, "friends");
182
118
  fs.mkdirSync(friendsDir, { recursive: true });
183
119
  const nowIso = now.toISOString();
184
- const id = `friend-${slugify(humanName)}`;
120
+ const id = `friend-${(0, config_1.slugify)(humanName) || "friend"}`;
121
+ const localExternalId = `${os.userInfo().username}@${os.hostname()}`;
185
122
  const record = {
186
123
  id,
187
124
  name: humanName,
@@ -191,7 +128,7 @@ function writeFriendImprint(bundleRoot, humanName, now) {
191
128
  externalIds: [
192
129
  {
193
130
  provider: "local",
194
- externalId: slugify(humanName),
131
+ externalId: localExternalId,
195
132
  linkedAt: nowIso,
196
133
  },
197
134
  ],
@@ -205,15 +142,6 @@ function writeFriendImprint(bundleRoot, humanName, now) {
205
142
  };
206
143
  fs.writeFileSync(path.join(friendsDir, `${id}.json`), `${JSON.stringify(record, null, 2)}\n`, "utf-8");
207
144
  }
208
- function writeHatchlingPsyche(bundleRoot, input, identityFileName) {
209
- const psycheDir = path.join(bundleRoot, "psyche");
210
- fs.mkdirSync(psycheDir, { recursive: true });
211
- fs.writeFileSync(path.join(psycheDir, "SOUL.md"), "# SOUL\n\nI am a practical, collaborative agent. I keep commitments and communicate clearly.\n", "utf-8");
212
- fs.writeFileSync(path.join(psycheDir, "IDENTITY.md"), `# IDENTITY\n\nI'm ${input.agentName}, newly hatched and ready to help ${input.humanName}.`, "utf-8");
213
- fs.writeFileSync(path.join(psycheDir, "LORE.md"), `# LORE\n\nHatched with specialist identity seed: ${identityFileName}.`, "utf-8");
214
- fs.writeFileSync(path.join(psycheDir, "TACIT.md"), "# TACIT\n\n- Save what I learn.\n- Keep tasks current.\n", "utf-8");
215
- fs.writeFileSync(path.join(psycheDir, "ASPIRATIONS.md"), "# ASPIRATIONS\n\n- Become a reliable partner for my primary friend.\n", "utf-8");
216
- }
217
145
  function writeMemoryScaffold(bundleRoot) {
218
146
  const memoryRoot = path.join(bundleRoot, "psyche", "memory");
219
147
  fs.mkdirSync(path.join(memoryRoot, "daily"), { recursive: true });
@@ -265,7 +193,6 @@ async function runHatchFlow(input, deps = {}) {
265
193
  writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
266
194
  writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
267
195
  writeHatchlingAgentConfig(bundleRoot, input);
268
- writeHatchlingPsyche(bundleRoot, input, selected.fileName);
269
196
  writeMemoryScaffold(bundleRoot);
270
197
  writeFriendImprint(bundleRoot, input.humanName, now);
271
198
  writeHeartbeatTask(bundleRoot, now);
@@ -42,7 +42,12 @@ const os = __importStar(require("os"));
42
42
  const path = __importStar(require("path"));
43
43
  const runtime_1 = require("../../nerves/runtime");
44
44
  function getSpecialistIdentitySourceDir() {
45
- return path.join(os.homedir(), "AgentBundles", "AdoptionSpecialist.ouro", "psyche", "identities");
45
+ // Prefer ~/AgentBundles/ if it exists (user may have customized identities)
46
+ const userSource = path.join(os.homedir(), "AgentBundles", "AdoptionSpecialist.ouro", "psyche", "identities");
47
+ if (fs.existsSync(userSource))
48
+ return userSource;
49
+ // Fall back to the bundled copy shipped with the npm package
50
+ return path.join(__dirname, "..", "..", "..", "AdoptionSpecialist.ouro", "psyche", "identities");
46
51
  }
47
52
  function getRepoSpecialistIdentitiesDir() {
48
53
  return path.join(process.cwd(), "AdoptionSpecialist.ouro", "psyche", "identities");
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.bundleMetaHook = bundleMetaHook;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../../nerves/runtime");
40
+ async function bundleMetaHook(ctx) {
41
+ (0, runtime_1.emitNervesEvent)({
42
+ component: "daemon",
43
+ event: "daemon.bundle_meta_hook_start",
44
+ message: "running bundle-meta update hook",
45
+ meta: { agentRoot: ctx.agentRoot, currentVersion: ctx.currentVersion },
46
+ });
47
+ const metaPath = path.join(ctx.agentRoot, "bundle-meta.json");
48
+ let existing;
49
+ try {
50
+ if (fs.existsSync(metaPath)) {
51
+ const raw = fs.readFileSync(metaPath, "utf-8");
52
+ existing = JSON.parse(raw);
53
+ }
54
+ }
55
+ catch {
56
+ // Malformed JSON -- treat as missing, will overwrite with fresh
57
+ existing = undefined;
58
+ }
59
+ const updated = {
60
+ runtimeVersion: ctx.currentVersion,
61
+ bundleSchemaVersion: existing?.bundleSchemaVersion ?? 1,
62
+ lastUpdated: new Date().toISOString(),
63
+ };
64
+ // Save old runtimeVersion as previousRuntimeVersion (if there was one)
65
+ if (existing?.runtimeVersion) {
66
+ updated.previousRuntimeVersion = existing.runtimeVersion;
67
+ }
68
+ try {
69
+ fs.writeFileSync(metaPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
70
+ }
71
+ catch (err) {
72
+ const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
73
+ (0, runtime_1.emitNervesEvent)({
74
+ component: "daemon",
75
+ event: "daemon.bundle_meta_hook_error",
76
+ message: "bundle-meta hook write failed",
77
+ meta: { agentRoot: ctx.agentRoot, error: errorMessage },
78
+ });
79
+ return { ok: false, error: errorMessage };
80
+ }
81
+ (0, runtime_1.emitNervesEvent)({
82
+ component: "daemon",
83
+ event: "daemon.bundle_meta_hook_end",
84
+ message: "bundle-meta updated",
85
+ meta: {
86
+ agentRoot: ctx.agentRoot,
87
+ runtimeVersion: updated.runtimeVersion,
88
+ previousRuntimeVersion: updated.previousRuntimeVersion,
89
+ },
90
+ });
91
+ return { ok: true };
92
+ }
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DAEMON_PLIST_LABEL = void 0;
37
+ exports.generateDaemonPlist = generateDaemonPlist;
38
+ exports.writeLaunchAgentPlist = writeLaunchAgentPlist;
39
+ exports.installLaunchAgent = installLaunchAgent;
40
+ exports.uninstallLaunchAgent = uninstallLaunchAgent;
41
+ exports.isDaemonInstalled = isDaemonInstalled;
42
+ const path = __importStar(require("path"));
43
+ const runtime_1 = require("../../nerves/runtime");
44
+ exports.DAEMON_PLIST_LABEL = "bot.ouro.daemon";
45
+ function plistFilePath(homeDir) {
46
+ return path.join(homeDir, "Library", "LaunchAgents", `${exports.DAEMON_PLIST_LABEL}.plist`);
47
+ }
48
+ function userLaunchDomain(userUid) {
49
+ return `gui/${userUid}`;
50
+ }
51
+ function generateDaemonPlist(options) {
52
+ (0, runtime_1.emitNervesEvent)({
53
+ component: "daemon",
54
+ event: "daemon.launchd_generate_plist",
55
+ message: "generating daemon plist",
56
+ meta: { entryPath: options.entryPath, socketPath: options.socketPath },
57
+ });
58
+ const lines = [
59
+ `<?xml version="1.0" encoding="UTF-8"?>`,
60
+ `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
61
+ `<plist version="1.0">`,
62
+ `<dict>`,
63
+ ` <key>Label</key>`,
64
+ ` <string>${exports.DAEMON_PLIST_LABEL}</string>`,
65
+ ` <key>ProgramArguments</key>`,
66
+ ` <array>`,
67
+ ` <string>${options.nodePath}</string>`,
68
+ ` <string>${options.entryPath}</string>`,
69
+ ` <string>--socket</string>`,
70
+ ` <string>${options.socketPath}</string>`,
71
+ ` </array>`,
72
+ ` <key>RunAtLoad</key>`,
73
+ ` <true/>`,
74
+ ` <key>KeepAlive</key>`,
75
+ ` <true/>`,
76
+ ];
77
+ if (options.envPath) {
78
+ lines.push(` <key>EnvironmentVariables</key>`, ` <dict>`, ` <key>PATH</key>`, ` <string>${options.envPath}</string>`, ` </dict>`);
79
+ }
80
+ if (options.logDir) {
81
+ lines.push(` <key>StandardOutPath</key>`, ` <string>${path.join(options.logDir, "ouro-daemon-stdout.log")}</string>`, ` <key>StandardErrorPath</key>`, ` <string>${path.join(options.logDir, "ouro-daemon-stderr.log")}</string>`);
82
+ }
83
+ lines.push(`</dict>`, `</plist>`, ``);
84
+ return lines.join("\n");
85
+ }
86
+ function writeLaunchAgentPlist(deps, options) {
87
+ const launchAgentsDir = path.join(deps.homeDir, "Library", "LaunchAgents");
88
+ deps.mkdirp(launchAgentsDir);
89
+ if (options.logDir) {
90
+ deps.mkdirp(options.logDir);
91
+ }
92
+ const fullPath = plistFilePath(deps.homeDir);
93
+ const xml = generateDaemonPlist(options);
94
+ deps.writeFile(fullPath, xml);
95
+ (0, runtime_1.emitNervesEvent)({
96
+ component: "daemon",
97
+ event: "daemon.launchd_plist_written",
98
+ message: "daemon launch agent plist written",
99
+ meta: { plistPath: fullPath, entryPath: options.entryPath, socketPath: options.socketPath },
100
+ });
101
+ return fullPath;
102
+ }
103
+ function installLaunchAgent(deps, options) {
104
+ (0, runtime_1.emitNervesEvent)({
105
+ component: "daemon",
106
+ event: "daemon.launchd_install",
107
+ message: "installing launch agent",
108
+ meta: { entryPath: options.entryPath, socketPath: options.socketPath },
109
+ });
110
+ const fullPath = plistFilePath(deps.homeDir);
111
+ const domain = userLaunchDomain(deps.userUid);
112
+ // Unload existing (best effort) for idempotent re-install
113
+ if (deps.existsFile(fullPath)) {
114
+ try {
115
+ deps.exec(`launchctl bootout ${domain} "${fullPath}"`);
116
+ }
117
+ catch { /* best effort */ }
118
+ }
119
+ writeLaunchAgentPlist(deps, options);
120
+ deps.exec(`launchctl bootstrap ${domain} "${fullPath}"`);
121
+ (0, runtime_1.emitNervesEvent)({
122
+ component: "daemon",
123
+ event: "daemon.launchd_installed",
124
+ message: "launch agent installed",
125
+ meta: { plistPath: fullPath },
126
+ });
127
+ }
128
+ function uninstallLaunchAgent(deps) {
129
+ (0, runtime_1.emitNervesEvent)({
130
+ component: "daemon",
131
+ event: "daemon.launchd_uninstall",
132
+ message: "uninstalling launch agent",
133
+ meta: {},
134
+ });
135
+ const fullPath = plistFilePath(deps.homeDir);
136
+ const domain = userLaunchDomain(deps.userUid);
137
+ if (deps.existsFile(fullPath)) {
138
+ try {
139
+ deps.exec(`launchctl bootout ${domain} "${fullPath}"`);
140
+ }
141
+ catch { /* best effort */ }
142
+ deps.removeFile(fullPath);
143
+ }
144
+ (0, runtime_1.emitNervesEvent)({
145
+ component: "daemon",
146
+ event: "daemon.launchd_uninstalled",
147
+ message: "launch agent uninstalled",
148
+ meta: { plistPath: fullPath },
149
+ });
150
+ }
151
+ function isDaemonInstalled(deps) {
152
+ (0, runtime_1.emitNervesEvent)({
153
+ component: "daemon",
154
+ event: "daemon.launchd_check_installed",
155
+ message: "checking if daemon is installed",
156
+ meta: {},
157
+ });
158
+ return deps.existsFile(plistFilePath(deps.homeDir));
159
+ }
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.discoverLogFiles = discoverLogFiles;
37
+ exports.readLastLines = readLastLines;
38
+ exports.formatLogLine = formatLogLine;
39
+ exports.tailLogs = tailLogs;
40
+ const path = __importStar(require("path"));
41
+ const nerves_1 = require("../../nerves");
42
+ const runtime_1 = require("../../nerves/runtime");
43
+ const identity_1 = require("../identity");
44
+ const LEVEL_COLORS = {
45
+ debug: "\x1b[2m",
46
+ info: "\x1b[36m",
47
+ warn: "\x1b[33m",
48
+ error: "\x1b[31m",
49
+ };
50
+ function discoverLogFiles(options) {
51
+ /* v8 ignore start -- integration: default DI stubs for real OS @preserve */
52
+ const existsSync = options.existsSync ?? (() => false);
53
+ const readdirSync = options.readdirSync ?? (() => []);
54
+ /* v8 ignore stop */
55
+ const logDir = options.homeDir
56
+ ? path.join(options.homeDir, "AgentBundles", `${options.agentName ?? "slugger"}.ouro`, "state", "daemon", "logs")
57
+ : (0, identity_1.getAgentDaemonLogsDir)(options.agentName);
58
+ const files = [];
59
+ if (existsSync(logDir)) {
60
+ for (const name of readdirSync(logDir)) {
61
+ if (!name.endsWith(".ndjson"))
62
+ continue;
63
+ if (options.agentFilter && !name.includes(options.agentFilter))
64
+ continue;
65
+ files.push(path.join(logDir, name));
66
+ }
67
+ }
68
+ return files.sort();
69
+ }
70
+ function readLastLines(filePath, count, readFileSync) {
71
+ let content;
72
+ try {
73
+ content = readFileSync(filePath, "utf-8");
74
+ }
75
+ catch {
76
+ return [];
77
+ }
78
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
79
+ return lines.slice(-count);
80
+ }
81
+ function formatLogLine(ndjsonLine) {
82
+ try {
83
+ const entry = JSON.parse(ndjsonLine);
84
+ const formatted = (0, nerves_1.formatTerminalEntry)(entry);
85
+ const color = LEVEL_COLORS[entry.level] ?? "";
86
+ return `${color}${formatted}\x1b[0m`;
87
+ }
88
+ catch {
89
+ return ndjsonLine;
90
+ }
91
+ }
92
+ function tailLogs(options = {}) {
93
+ /* v8 ignore start -- integration: default DI stubs for real OS @preserve */
94
+ const writer = options.writer ?? ((text) => process.stdout.write(text));
95
+ const lineCount = options.lines ?? 20;
96
+ const readFileSync = options.readFileSync ?? (() => "");
97
+ /* v8 ignore stop */
98
+ const watchFile = options.watchFile;
99
+ const unwatchFile = options.unwatchFile;
100
+ const files = discoverLogFiles(options);
101
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.log_tailer_started", message: "log tailer started", meta: { fileCount: files.length, follow: !!options.follow } });
102
+ const fileSizes = new Map();
103
+ // Read initial lines
104
+ for (const file of files) {
105
+ const lines = readLastLines(file, lineCount, readFileSync);
106
+ for (const line of lines) {
107
+ writer(`${formatLogLine(line)}\n`);
108
+ }
109
+ try {
110
+ const content = readFileSync(file, "utf-8");
111
+ fileSizes.set(file, content.length);
112
+ }
113
+ catch {
114
+ fileSizes.set(file, 0);
115
+ }
116
+ }
117
+ // Follow mode
118
+ if (options.follow && watchFile && unwatchFile) {
119
+ for (const file of files) {
120
+ watchFile(file, () => {
121
+ let content;
122
+ try {
123
+ content = readFileSync(file, "utf-8");
124
+ }
125
+ catch {
126
+ return;
127
+ }
128
+ /* v8 ignore next -- defensive: fileSizes always populated above @preserve */
129
+ const prevSize = fileSizes.get(file) ?? 0;
130
+ if (content.length <= prevSize)
131
+ return;
132
+ fileSizes.set(file, content.length);
133
+ const newContent = content.slice(prevSize);
134
+ const newLines = newContent.split("\n").filter((l) => l.trim().length > 0);
135
+ for (const line of newLines) {
136
+ writer(`${formatLogLine(line)}\n`);
137
+ }
138
+ });
139
+ }
140
+ return () => {
141
+ for (const file of files) {
142
+ unwatchFile(file);
143
+ }
144
+ };
145
+ }
146
+ return () => { };
147
+ }
@@ -35,9 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FileMessageRouter = void 0;
37
37
  const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
38
  const path = __importStar(require("path"));
40
39
  const runtime_1 = require("../../nerves/runtime");
40
+ const identity_1 = require("../identity");
41
41
  function messageId(nowIso) {
42
42
  return `msg-${nowIso.replace(/[^0-9]/g, "")}`;
43
43
  }
@@ -45,7 +45,7 @@ class FileMessageRouter {
45
45
  baseDir;
46
46
  now;
47
47
  constructor(options = {}) {
48
- this.baseDir = options.baseDir ?? path.join(os.homedir(), ".agentstate", "messages");
48
+ this.baseDir = options.baseDir ?? (0, identity_1.getAgentMessagesRoot)();
49
49
  this.now = options.now ?? (() => new Date().toISOString());
50
50
  fs.mkdirSync(this.baseDir, { recursive: true });
51
51
  }
@@ -77,12 +77,21 @@ class FileMessageRouter {
77
77
  if (!fs.existsSync(inboxPath))
78
78
  return [];
79
79
  const raw = fs.readFileSync(inboxPath, "utf-8");
80
- fs.writeFileSync(inboxPath, "", "utf-8");
81
- const messages = raw
82
- .split("\n")
83
- .map((line) => line.trim())
84
- .filter((line) => line.length > 0)
85
- .map((line) => JSON.parse(line));
80
+ const messages = [];
81
+ const unparsed = [];
82
+ for (const line of raw.split("\n")) {
83
+ const trimmed = line.trim();
84
+ if (!trimmed)
85
+ continue;
86
+ try {
87
+ messages.push(JSON.parse(trimmed));
88
+ }
89
+ catch {
90
+ unparsed.push(trimmed);
91
+ }
92
+ }
93
+ // Only clear inbox after parsing; preserve lines that failed to parse.
94
+ fs.writeFileSync(inboxPath, unparsed.length > 0 ? unparsed.map((l) => `${l}\n`).join("") : "", "utf-8");
86
95
  (0, runtime_1.emitNervesEvent)({
87
96
  component: "daemon",
88
97
  event: "daemon.message_polled",