@ouro.bot/cli 0.1.0-alpha.14 → 0.1.0-alpha.140
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/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +151 -205
- package/changelog.json +902 -0
- package/dist/heart/active-work.js +633 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/commitments.js +105 -0
- package/dist/heart/config.js +66 -21
- package/dist/heart/core.js +579 -112
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +467 -0
- package/dist/heart/daemon/auth-flow.js +457 -0
- package/dist/heart/daemon/daemon-cli.js +1840 -196
- package/dist/heart/daemon/daemon-entry.js +80 -3
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +301 -1
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +15 -80
- package/dist/heart/daemon/heartbeat-timer.js +131 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +159 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/mcp-server.js +464 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-path-installer.js +67 -34
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/process-manager.js +13 -0
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/sense-manager.js +50 -2
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +2 -2
- package/dist/heart/daemon/specialist-prompt.js +10 -7
- package/dist/heart/daemon/specialist-tools.js +52 -3
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +64 -21
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +197 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +159 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +195 -34
- package/dist/heart/providers/azure.js +115 -9
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +49 -14
- package/dist/heart/session-activity.js +173 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/streaming.js +117 -33
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/tool-loop.js +198 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +122 -32
- package/dist/mind/bundle-manifest.js +14 -0
- package/dist/mind/context.js +124 -14
- package/dist/mind/{memory.js → diary.js} +70 -56
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +35 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/obligations.js +134 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +554 -88
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/index.js +12 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/ado-semantic.js +6 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +254 -0
- package/dist/repertoire/coding/feedback.js +301 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +210 -4
- package/dist/repertoire/coding/spawner.js +59 -12
- package/dist/repertoire/coding/tools.js +176 -4
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +290 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +198 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/tasks/board.js +12 -0
- package/dist/repertoire/tasks/index.js +23 -9
- package/dist/repertoire/tasks/transitions.js +1 -2
- package/dist/repertoire/tools-base.js +975 -271
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-github.js +1 -6
- package/dist/repertoire/tools-teams.js +65 -59
- package/dist/repertoire/tools.js +265 -91
- package/dist/senses/attention-queue.js +97 -0
- package/dist/senses/bluebubbles-client.js +210 -5
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +944 -45
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +374 -131
- package/dist/senses/contextual-heartbeat.js +102 -0
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +154 -0
- package/dist/senses/inner-dialog-worker.js +47 -18
- package/dist/senses/inner-dialog.js +500 -84
- package/dist/senses/pipeline.js +454 -0
- package/dist/senses/surface-tool.js +82 -0
- package/dist/senses/teams.js +607 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +12 -4
- package/skills/configure-dev-tools.md +109 -0
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HeartbeatTimer = exports.DEFAULT_CADENCE_MS = void 0;
|
|
4
|
+
exports.parseCadenceMs = parseCadenceMs;
|
|
5
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
6
|
+
const parser_1 = require("../../repertoire/tasks/parser");
|
|
7
|
+
exports.DEFAULT_CADENCE_MS = 30 * 60 * 1000; // 30 minutes
|
|
8
|
+
function parseCadenceMs(raw) {
|
|
9
|
+
if (typeof raw !== "string")
|
|
10
|
+
return null;
|
|
11
|
+
const value = raw.trim();
|
|
12
|
+
if (!value)
|
|
13
|
+
return null;
|
|
14
|
+
const match = /^(\d+)(m|h|d)$/.exec(value);
|
|
15
|
+
if (!match)
|
|
16
|
+
return null;
|
|
17
|
+
const interval = Number.parseInt(match[1], 10);
|
|
18
|
+
if (!Number.isFinite(interval) || interval <= 0)
|
|
19
|
+
return null;
|
|
20
|
+
const unit = match[2];
|
|
21
|
+
if (unit === "m")
|
|
22
|
+
return interval * 60 * 1000;
|
|
23
|
+
if (unit === "h")
|
|
24
|
+
return interval * 60 * 60 * 1000;
|
|
25
|
+
return interval * 24 * 60 * 60 * 1000;
|
|
26
|
+
}
|
|
27
|
+
class HeartbeatTimer {
|
|
28
|
+
agent;
|
|
29
|
+
sendToAgent;
|
|
30
|
+
readFileSync;
|
|
31
|
+
readdirSync;
|
|
32
|
+
heartbeatTaskDir;
|
|
33
|
+
runtimeStatePath;
|
|
34
|
+
now;
|
|
35
|
+
pendingTimer = null;
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.agent = options.agent;
|
|
38
|
+
this.sendToAgent = options.sendToAgent;
|
|
39
|
+
this.readFileSync = options.deps.readFileSync;
|
|
40
|
+
this.readdirSync = options.deps.readdirSync;
|
|
41
|
+
this.heartbeatTaskDir = options.deps.heartbeatTaskDir;
|
|
42
|
+
this.runtimeStatePath = options.deps.runtimeStatePath;
|
|
43
|
+
this.now = options.deps.now ?? (() => Date.now());
|
|
44
|
+
}
|
|
45
|
+
start() {
|
|
46
|
+
this.scheduleNext();
|
|
47
|
+
}
|
|
48
|
+
stop() {
|
|
49
|
+
if (this.pendingTimer !== null) {
|
|
50
|
+
clearTimeout(this.pendingTimer);
|
|
51
|
+
this.pendingTimer = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
scheduleNext() {
|
|
55
|
+
const cadenceMs = this.readCadence();
|
|
56
|
+
const lastCompletedAt = this.readLastCompletedAt();
|
|
57
|
+
const nowMs = this.now();
|
|
58
|
+
let delay;
|
|
59
|
+
if (lastCompletedAt === null) {
|
|
60
|
+
// Never run before — fire immediately
|
|
61
|
+
delay = 0;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const elapsed = nowMs - lastCompletedAt;
|
|
65
|
+
delay = Math.max(0, cadenceMs - elapsed);
|
|
66
|
+
}
|
|
67
|
+
this.pendingTimer = setTimeout(() => {
|
|
68
|
+
this.fire();
|
|
69
|
+
}, delay);
|
|
70
|
+
}
|
|
71
|
+
fire() {
|
|
72
|
+
this.pendingTimer = null;
|
|
73
|
+
(0, runtime_1.emitNervesEvent)({
|
|
74
|
+
component: "daemon",
|
|
75
|
+
event: "daemon.heartbeat_fire",
|
|
76
|
+
message: "heartbeat timer fired",
|
|
77
|
+
meta: { agent: this.agent },
|
|
78
|
+
});
|
|
79
|
+
this.sendToAgent(this.agent, { type: "heartbeat" });
|
|
80
|
+
this.scheduleNext();
|
|
81
|
+
}
|
|
82
|
+
readCadence() {
|
|
83
|
+
// Scan habits dir for *-heartbeat.md
|
|
84
|
+
let files;
|
|
85
|
+
try {
|
|
86
|
+
files = this.readdirSync(this.heartbeatTaskDir);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return exports.DEFAULT_CADENCE_MS;
|
|
90
|
+
}
|
|
91
|
+
const heartbeatFile = files.find((f) => f.endsWith("-heartbeat.md"));
|
|
92
|
+
if (!heartbeatFile)
|
|
93
|
+
return exports.DEFAULT_CADENCE_MS;
|
|
94
|
+
try {
|
|
95
|
+
const content = this.readFileSync(`${this.heartbeatTaskDir}/${heartbeatFile}`, "utf-8");
|
|
96
|
+
const cadence = this.extractCadenceFromTaskFile(content);
|
|
97
|
+
return cadence ?? exports.DEFAULT_CADENCE_MS;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return exports.DEFAULT_CADENCE_MS;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
extractCadenceFromTaskFile(content) {
|
|
104
|
+
// Parse frontmatter from task file to get cadence value
|
|
105
|
+
const lines = content.split(/\r?\n/);
|
|
106
|
+
if (lines[0]?.trim() !== "---")
|
|
107
|
+
return null;
|
|
108
|
+
const closing = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
109
|
+
if (closing === -1)
|
|
110
|
+
return null;
|
|
111
|
+
const rawFrontmatter = lines.slice(1, closing).join("\n");
|
|
112
|
+
const frontmatter = (0, parser_1.parseFrontmatter)(rawFrontmatter);
|
|
113
|
+
return parseCadenceMs(frontmatter.cadence);
|
|
114
|
+
}
|
|
115
|
+
readLastCompletedAt() {
|
|
116
|
+
try {
|
|
117
|
+
const raw = this.readFileSync(this.runtimeStatePath, "utf-8");
|
|
118
|
+
const state = JSON.parse(raw);
|
|
119
|
+
if (typeof state.lastCompletedAt !== "string")
|
|
120
|
+
return null;
|
|
121
|
+
const ms = new Date(state.lastCompletedAt).getTime();
|
|
122
|
+
if (Number.isNaN(ms))
|
|
123
|
+
return null;
|
|
124
|
+
return ms;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.HeartbeatTimer = HeartbeatTimer;
|
|
@@ -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
|
+
}
|
|
@@ -37,10 +37,10 @@ exports.discoverLogFiles = discoverLogFiles;
|
|
|
37
37
|
exports.readLastLines = readLastLines;
|
|
38
38
|
exports.formatLogLine = formatLogLine;
|
|
39
39
|
exports.tailLogs = tailLogs;
|
|
40
|
-
const os = __importStar(require("os"));
|
|
41
40
|
const path = __importStar(require("path"));
|
|
42
41
|
const nerves_1 = require("../../nerves");
|
|
43
42
|
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const identity_1 = require("../identity");
|
|
44
44
|
const LEVEL_COLORS = {
|
|
45
45
|
debug: "\x1b[2m",
|
|
46
46
|
info: "\x1b[36m",
|
|
@@ -49,11 +49,12 @@ const LEVEL_COLORS = {
|
|
|
49
49
|
};
|
|
50
50
|
function discoverLogFiles(options) {
|
|
51
51
|
/* v8 ignore start -- integration: default DI stubs for real OS @preserve */
|
|
52
|
-
const homeDir = options.homeDir ?? os.homedir();
|
|
53
52
|
const existsSync = options.existsSync ?? (() => false);
|
|
54
53
|
const readdirSync = options.readdirSync ?? (() => []);
|
|
55
54
|
/* v8 ignore stop */
|
|
56
|
-
const logDir =
|
|
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);
|
|
57
58
|
const files = [];
|
|
58
59
|
if (existsSync(logDir)) {
|
|
59
60
|
for (const name of readdirSync(logDir)) {
|