@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.
- package/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +147 -205
- package/assets/ouroboros.png +0 -0
- package/changelog.json +596 -0
- package/dist/heart/active-work.js +251 -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 +109 -0
- package/dist/heart/config.js +102 -23
- package/dist/heart/core.js +512 -94
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/auth-flow.js +430 -0
- package/dist/heart/daemon/daemon-cli.js +1935 -185
- package/dist/heart/daemon/daemon-entry.js +55 -6
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +218 -9
- package/dist/heart/daemon/hatch-animation.js +35 -0
- package/dist/heart/daemon/hatch-flow.js +10 -83
- package/dist/heart/daemon/hatch-specialist.js +6 -1
- 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 +147 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron.js +260 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-bot-wrapper.js +4 -3
- package/dist/heart/daemon/ouro-path-installer.js +260 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/process-manager.js +32 -2
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +61 -14
- 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 +307 -0
- 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 +129 -0
- package/dist/heart/daemon/specialist-prompt.js +99 -0
- package/dist/heart/daemon/specialist-tools.js +283 -0
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/task-scheduler.js +4 -1
- 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 +153 -23
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +191 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/providers/anthropic.js +77 -9
- package/dist/heart/providers/azure.js +86 -7
- package/dist/heart/providers/github-copilot.js +149 -0
- package/dist/heart/providers/minimax.js +4 -0
- package/dist/heart/providers/openai-codex.js +12 -3
- package/dist/heart/safe-workspace.js +381 -0
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/heart/session-recall.js +116 -0
- package/dist/heart/streaming.js +103 -22
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +37 -4
- package/dist/mind/bundle-manifest.js +70 -0
- package/dist/mind/context.js +141 -11
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +43 -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 +9 -1
- package/dist/mind/memory.js +89 -26
- package/dist/mind/obligation-steering.js +31 -0
- package/dist/mind/pending.js +160 -0
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt-refresh.js +20 -0
- package/dist/mind/prompt.js +499 -8
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/file-completeness.js +14 -4
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/index.js +12 -0
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +210 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +69 -4
- package/dist/repertoire/coding/spawner.js +21 -3
- package/dist/repertoire/coding/tools.js +105 -2
- 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 +195 -0
- package/dist/repertoire/skills.js +3 -26
- 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 +770 -213
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +106 -53
- package/dist/senses/bluebubbles-client.js +484 -0
- package/dist/senses/bluebubbles-entry.js +13 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +261 -0
- package/dist/senses/bluebubbles-mutation-log.js +116 -0
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +1181 -0
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +452 -99
- 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 +387 -70
- package/dist/senses/pipeline.js +307 -0
- package/dist/senses/session-lock.js +119 -0
- package/dist/senses/teams.js +574 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +16 -4
- package/subagents/README.md +4 -68
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -593
- 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
|
-
|
|
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:
|
|
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
|
-
|
|
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 ??
|
|
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
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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",
|