@ouro.bot/cli 0.0.1-alpha.0 → 0.1.0-alpha.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/AdoptionSpecialist.ouro/agent.json +20 -0
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +24 -0
- package/AdoptionSpecialist.ouro/psyche/identities/basilisk.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/jafar.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/jormungandr.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/kaa.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/medusa.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/nagini.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/ouroboros.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/python.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/quetzalcoatl.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/sir-hiss.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/the-serpent.md +31 -0
- package/AdoptionSpecialist.ouro/psyche/identities/the-snake.md +31 -0
- package/README.md +224 -6
- package/dist/heart/agent-entry.js +17 -0
- package/dist/heart/api-error.js +34 -0
- package/dist/heart/config.js +330 -0
- package/dist/heart/core.js +524 -0
- package/dist/heart/daemon/daemon-cli.js +884 -0
- package/dist/heart/daemon/daemon-entry.js +74 -0
- package/dist/heart/daemon/daemon.js +313 -0
- package/dist/heart/daemon/hatch-animation.js +28 -0
- package/dist/heart/daemon/hatch-flow.js +286 -0
- package/dist/heart/daemon/hatch-specialist.js +112 -0
- package/dist/heart/daemon/health-monitor.js +79 -0
- package/dist/heart/daemon/log-tailer.js +146 -0
- package/dist/heart/daemon/message-router.js +98 -0
- package/dist/heart/daemon/os-cron.js +260 -0
- package/dist/heart/daemon/ouro-bot-entry.js +23 -0
- package/dist/heart/daemon/ouro-bot-wrapper.js +91 -0
- package/dist/heart/daemon/ouro-entry.js +23 -0
- package/dist/heart/daemon/ouro-uti.js +212 -0
- package/dist/heart/daemon/process-manager.js +237 -0
- package/dist/heart/daemon/runtime-logging.js +102 -0
- package/dist/heart/daemon/specialist-orchestrator.js +161 -0
- package/dist/heart/daemon/specialist-prompt.js +56 -0
- package/dist/heart/daemon/specialist-session.js +150 -0
- package/dist/heart/daemon/specialist-tools.js +132 -0
- package/dist/heart/daemon/subagent-installer.js +125 -0
- package/dist/heart/daemon/task-scheduler.js +240 -0
- package/dist/heart/harness.js +26 -0
- package/dist/heart/identity.js +295 -0
- package/dist/heart/kicks.js +144 -0
- package/dist/heart/primitives.js +4 -0
- package/dist/heart/providers/anthropic.js +332 -0
- package/dist/heart/providers/azure.js +66 -0
- package/dist/heart/providers/minimax.js +53 -0
- package/dist/heart/providers/openai-codex.js +162 -0
- package/dist/heart/streaming.js +415 -0
- package/dist/heart/turn-coordinator.js +62 -0
- package/dist/inner-worker-entry.js +4 -0
- package/dist/mind/associative-recall.js +197 -0
- package/dist/mind/bundle-manifest.js +118 -0
- package/dist/mind/context.js +302 -0
- package/dist/mind/first-impressions.js +43 -0
- package/dist/mind/format.js +56 -0
- package/dist/mind/friends/channel.js +49 -0
- package/dist/mind/friends/resolver.js +84 -0
- package/dist/mind/friends/store-file.js +171 -0
- package/dist/mind/friends/store.js +4 -0
- package/dist/mind/friends/tokens.js +26 -0
- package/dist/mind/friends/types.js +21 -0
- package/dist/mind/memory.js +388 -0
- package/dist/mind/pending.js +93 -0
- package/dist/mind/phrases.js +43 -0
- package/dist/mind/prompt-refresh.js +20 -0
- package/dist/mind/prompt.js +355 -0
- package/dist/mind/token-estimate.js +119 -0
- package/dist/nerves/cli-logging.js +31 -0
- package/dist/nerves/coverage/audit-rules.js +81 -0
- package/dist/nerves/coverage/audit.js +200 -0
- package/dist/nerves/coverage/cli-main.js +5 -0
- package/dist/nerves/coverage/cli.js +51 -0
- package/dist/nerves/coverage/contract.js +23 -0
- package/dist/nerves/coverage/file-completeness.js +56 -0
- package/dist/nerves/coverage/run-artifacts.js +77 -0
- package/dist/nerves/coverage/source-scanner.js +34 -0
- package/dist/nerves/index.js +152 -0
- package/dist/nerves/runtime.js +38 -0
- package/dist/repertoire/ado-client.js +211 -0
- package/dist/repertoire/ado-context.js +73 -0
- package/dist/repertoire/ado-semantic.js +841 -0
- package/dist/repertoire/ado-templates.js +146 -0
- package/dist/repertoire/coding/index.js +36 -0
- package/dist/repertoire/coding/manager.js +489 -0
- package/dist/repertoire/coding/monitor.js +60 -0
- package/dist/repertoire/coding/reporter.js +45 -0
- package/dist/repertoire/coding/spawner.js +102 -0
- package/dist/repertoire/coding/tools.js +167 -0
- package/dist/repertoire/coding/types.js +2 -0
- package/dist/repertoire/data/ado-endpoints.json +122 -0
- package/dist/repertoire/data/graph-endpoints.json +212 -0
- package/dist/repertoire/github-client.js +64 -0
- package/dist/repertoire/graph-client.js +118 -0
- package/dist/repertoire/skills.js +156 -0
- package/dist/repertoire/tasks/board.js +122 -0
- package/dist/repertoire/tasks/index.js +210 -0
- package/dist/repertoire/tasks/lifecycle.js +80 -0
- package/dist/repertoire/tasks/middleware.js +65 -0
- package/dist/repertoire/tasks/parser.js +173 -0
- package/dist/repertoire/tasks/scanner.js +132 -0
- package/dist/repertoire/tasks/transitions.js +145 -0
- package/dist/repertoire/tasks/types.js +2 -0
- package/dist/repertoire/tools-base.js +714 -0
- package/dist/repertoire/tools-github.js +53 -0
- package/dist/repertoire/tools-teams.js +308 -0
- package/dist/repertoire/tools.js +199 -0
- package/dist/senses/bluebubbles-client.js +279 -0
- package/dist/senses/bluebubbles-entry.js +11 -0
- package/dist/senses/bluebubbles-model.js +253 -0
- package/dist/senses/bluebubbles-mutation-log.js +76 -0
- package/dist/senses/bluebubbles.js +332 -0
- package/dist/senses/cli-entry.js +15 -0
- package/dist/senses/cli.js +604 -0
- package/dist/senses/commands.js +98 -0
- package/dist/senses/inner-dialog-worker.js +61 -0
- package/dist/senses/inner-dialog.js +231 -0
- package/dist/senses/session-lock.js +119 -0
- package/dist/senses/teams-entry.js +15 -0
- package/dist/senses/teams.js +696 -0
- package/dist/senses/trust-gate.js +150 -0
- package/package.json +35 -11
- package/subagents/README.md +73 -0
- package/subagents/work-doer.md +233 -0
- package/subagents/work-merger.md +624 -0
- package/subagents/work-planner.md +373 -0
- package/bin/ouro.js +0 -6
|
@@ -0,0 +1,212 @@
|
|
|
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.registerOuroBundleUti = registerOuroBundleUti;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const identity_1 = require("../identity");
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const LSREGISTER_PATH = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
|
|
44
|
+
const ICON_SIZES = [16, 32, 128, 256, 512];
|
|
45
|
+
function resolveIconSourcePath(repoRoot) {
|
|
46
|
+
return path.resolve(repoRoot, "..", "ouroboros-website", "public", "images", "ouroboros.png");
|
|
47
|
+
}
|
|
48
|
+
function buildIconAsset(iconSourcePath, icnsPath, iconsetDir, deps) {
|
|
49
|
+
try {
|
|
50
|
+
deps.mkdirSync(iconsetDir, { recursive: true });
|
|
51
|
+
for (const size of ICON_SIZES) {
|
|
52
|
+
const basePng = path.join(iconsetDir, `icon_${size}x${size}.png`);
|
|
53
|
+
const retinaPng = path.join(iconsetDir, `icon_${size}x${size}@2x.png`);
|
|
54
|
+
deps.execFileSync("sips", ["-z", String(size), String(size), iconSourcePath, "--out", basePng]);
|
|
55
|
+
deps.execFileSync("sips", ["-z", String(size * 2), String(size * 2), iconSourcePath, "--out", retinaPng]);
|
|
56
|
+
}
|
|
57
|
+
deps.execFileSync("iconutil", ["-c", "icns", iconsetDir, "-o", icnsPath]);
|
|
58
|
+
deps.rmSync(iconsetDir, { recursive: true, force: true });
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
deps.rmSync(iconsetDir, { recursive: true, force: true });
|
|
63
|
+
(0, runtime_1.emitNervesEvent)({
|
|
64
|
+
level: "warn",
|
|
65
|
+
component: "daemon",
|
|
66
|
+
event: "daemon.ouro_uti_icon_error",
|
|
67
|
+
message: "failed building .ouro icon; continuing without custom icon",
|
|
68
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
69
|
+
});
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function buildInfoPlist(iconInstalled) {
|
|
74
|
+
const iconTag = iconInstalled ? "\n <key>CFBundleTypeIconFile</key>\n <string>ouro</string>" : "";
|
|
75
|
+
return [
|
|
76
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
77
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
78
|
+
'<plist version="1.0">',
|
|
79
|
+
"<dict>",
|
|
80
|
+
" <key>CFBundleIdentifier</key>",
|
|
81
|
+
" <string>bot.ouro.bundle-registry</string>",
|
|
82
|
+
" <key>CFBundleName</key>",
|
|
83
|
+
" <string>Ouro Bundle Registry</string>",
|
|
84
|
+
" <key>CFBundlePackageType</key>",
|
|
85
|
+
" <string>APPL</string>",
|
|
86
|
+
" <key>UTExportedTypeDeclarations</key>",
|
|
87
|
+
" <array>",
|
|
88
|
+
" <dict>",
|
|
89
|
+
" <key>UTTypeIdentifier</key>",
|
|
90
|
+
" <string>bot.ouro.bundle</string>",
|
|
91
|
+
" <key>UTTypeConformsTo</key>",
|
|
92
|
+
" <array>",
|
|
93
|
+
" <string>public.folder</string>",
|
|
94
|
+
" </array>",
|
|
95
|
+
" <key>UTTypeTagSpecification</key>",
|
|
96
|
+
" <dict>",
|
|
97
|
+
" <key>public.filename-extension</key>",
|
|
98
|
+
" <array>",
|
|
99
|
+
" <string>ouro</string>",
|
|
100
|
+
" </array>",
|
|
101
|
+
" </dict>",
|
|
102
|
+
" </dict>",
|
|
103
|
+
" </array>",
|
|
104
|
+
" <key>CFBundleDocumentTypes</key>",
|
|
105
|
+
" <array>",
|
|
106
|
+
" <dict>",
|
|
107
|
+
" <key>CFBundleTypeName</key>",
|
|
108
|
+
" <string>Ouro Agent Bundle</string>",
|
|
109
|
+
" <key>LSItemContentTypes</key>",
|
|
110
|
+
" <array>",
|
|
111
|
+
" <string>bot.ouro.bundle</string>",
|
|
112
|
+
" </array>",
|
|
113
|
+
" <key>CFBundleTypeRole</key>",
|
|
114
|
+
" <string>Editor</string>",
|
|
115
|
+
` ${iconTag.trim()}`,
|
|
116
|
+
" </dict>",
|
|
117
|
+
" </array>",
|
|
118
|
+
"</dict>",
|
|
119
|
+
"</plist>",
|
|
120
|
+
"",
|
|
121
|
+
]
|
|
122
|
+
.filter((line) => line.length > 0)
|
|
123
|
+
.join("\n");
|
|
124
|
+
}
|
|
125
|
+
function registerOuroBundleUti(deps = {}) {
|
|
126
|
+
const platform = deps.platform ?? process.platform;
|
|
127
|
+
if (platform !== "darwin") {
|
|
128
|
+
(0, runtime_1.emitNervesEvent)({
|
|
129
|
+
component: "daemon",
|
|
130
|
+
event: "daemon.ouro_uti_register_skip",
|
|
131
|
+
message: "skipped .ouro UTI registration on non-macOS platform",
|
|
132
|
+
meta: { platform },
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
attempted: false,
|
|
136
|
+
registered: false,
|
|
137
|
+
iconInstalled: false,
|
|
138
|
+
skippedReason: "non-macos",
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const homeDir = deps.homeDir ?? os.homedir();
|
|
142
|
+
const repoRoot = deps.repoRoot ?? (0, identity_1.getRepoRoot)();
|
|
143
|
+
const existsSync = deps.existsSync ?? fs.existsSync;
|
|
144
|
+
const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
|
|
145
|
+
const writeFileSync = deps.writeFileSync ?? fs.writeFileSync;
|
|
146
|
+
const rmSync = deps.rmSync ?? fs.rmSync;
|
|
147
|
+
const exec = deps.execFileSync ?? ((file, args) => (0, child_process_1.execFileSync)(file, args));
|
|
148
|
+
const supportRoot = path.join(homeDir, "Library", "Application Support", "ouro", "uti");
|
|
149
|
+
const appBundlePath = path.join(supportRoot, "OuroBundleRegistry.app");
|
|
150
|
+
const contentsDir = path.join(appBundlePath, "Contents");
|
|
151
|
+
const resourcesDir = path.join(contentsDir, "Resources");
|
|
152
|
+
const plistPath = path.join(contentsDir, "Info.plist");
|
|
153
|
+
const icnsPath = path.join(resourcesDir, "ouro.icns");
|
|
154
|
+
const iconsetDir = path.join(supportRoot, "ouro.iconset");
|
|
155
|
+
const iconSourcePath = resolveIconSourcePath(repoRoot);
|
|
156
|
+
(0, runtime_1.emitNervesEvent)({
|
|
157
|
+
component: "daemon",
|
|
158
|
+
event: "daemon.ouro_uti_register_start",
|
|
159
|
+
message: "registering .ouro UTI on macOS",
|
|
160
|
+
meta: { appBundlePath },
|
|
161
|
+
});
|
|
162
|
+
let iconInstalled = false;
|
|
163
|
+
try {
|
|
164
|
+
mkdirSync(resourcesDir, { recursive: true });
|
|
165
|
+
if (existsSync(iconSourcePath)) {
|
|
166
|
+
iconInstalled = buildIconAsset(iconSourcePath, icnsPath, iconsetDir, {
|
|
167
|
+
mkdirSync,
|
|
168
|
+
rmSync,
|
|
169
|
+
execFileSync: exec,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
(0, runtime_1.emitNervesEvent)({
|
|
174
|
+
component: "daemon",
|
|
175
|
+
event: "daemon.ouro_uti_icon_skip",
|
|
176
|
+
message: "icon source image missing; continuing without custom icon",
|
|
177
|
+
meta: { iconSourcePath },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
writeFileSync(plistPath, buildInfoPlist(iconInstalled), "utf-8");
|
|
181
|
+
exec(LSREGISTER_PATH, ["-f", appBundlePath]);
|
|
182
|
+
(0, runtime_1.emitNervesEvent)({
|
|
183
|
+
component: "daemon",
|
|
184
|
+
event: "daemon.ouro_uti_register_end",
|
|
185
|
+
message: "registered .ouro UTI on macOS",
|
|
186
|
+
meta: { iconInstalled },
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
attempted: true,
|
|
190
|
+
registered: true,
|
|
191
|
+
iconInstalled,
|
|
192
|
+
registrationBundlePath: appBundlePath,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
197
|
+
(0, runtime_1.emitNervesEvent)({
|
|
198
|
+
level: "warn",
|
|
199
|
+
component: "daemon",
|
|
200
|
+
event: "daemon.ouro_uti_register_error",
|
|
201
|
+
message: "failed .ouro UTI registration; continuing non-blocking",
|
|
202
|
+
meta: { reason },
|
|
203
|
+
});
|
|
204
|
+
return {
|
|
205
|
+
attempted: true,
|
|
206
|
+
registered: false,
|
|
207
|
+
iconInstalled,
|
|
208
|
+
skippedReason: reason,
|
|
209
|
+
registrationBundlePath: appBundlePath,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
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.DaemonProcessManager = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const identity_1 = require("../identity");
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
function startOfHour(ms) {
|
|
42
|
+
return ms - 60 * 60 * 1000;
|
|
43
|
+
}
|
|
44
|
+
class DaemonProcessManager {
|
|
45
|
+
agents = new Map();
|
|
46
|
+
maxRestartsPerHour;
|
|
47
|
+
stabilityThresholdMs;
|
|
48
|
+
initialBackoffMs;
|
|
49
|
+
maxBackoffMs;
|
|
50
|
+
spawnFn;
|
|
51
|
+
now;
|
|
52
|
+
setTimeoutFn;
|
|
53
|
+
clearTimeoutFn;
|
|
54
|
+
constructor(options) {
|
|
55
|
+
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
56
|
+
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
57
|
+
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
58
|
+
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
59
|
+
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
60
|
+
this.now = options.now ?? (() => Date.now());
|
|
61
|
+
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
62
|
+
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
63
|
+
for (const agent of options.agents) {
|
|
64
|
+
this.agents.set(agent.name, {
|
|
65
|
+
config: agent,
|
|
66
|
+
process: null,
|
|
67
|
+
restartTimer: null,
|
|
68
|
+
crashTimestamps: [],
|
|
69
|
+
stopRequested: false,
|
|
70
|
+
snapshot: {
|
|
71
|
+
name: agent.name,
|
|
72
|
+
channel: agent.channel,
|
|
73
|
+
status: "stopped",
|
|
74
|
+
pid: null,
|
|
75
|
+
restartCount: 0,
|
|
76
|
+
startedAt: null,
|
|
77
|
+
lastCrashAt: null,
|
|
78
|
+
backoffMs: this.initialBackoffMs,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async startAutoStartAgents() {
|
|
84
|
+
for (const state of this.agents.values()) {
|
|
85
|
+
if (state.config.autoStart) {
|
|
86
|
+
await this.startAgent(state.config.name);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async startAgent(agent) {
|
|
91
|
+
const state = this.requireAgent(agent);
|
|
92
|
+
if (state.process)
|
|
93
|
+
return;
|
|
94
|
+
this.clearRestartTimer(state);
|
|
95
|
+
state.stopRequested = false;
|
|
96
|
+
state.snapshot.status = "starting";
|
|
97
|
+
const runCwd = (0, identity_1.getRepoRoot)();
|
|
98
|
+
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
99
|
+
const args = [entryScript, "--agent", agent, ...(state.config.args ?? [])];
|
|
100
|
+
const child = this.spawnFn("node", args, {
|
|
101
|
+
cwd: runCwd,
|
|
102
|
+
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
103
|
+
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
|
104
|
+
});
|
|
105
|
+
state.process = child;
|
|
106
|
+
state.snapshot.status = "running";
|
|
107
|
+
state.snapshot.pid = child.pid ?? null;
|
|
108
|
+
state.snapshot.startedAt = new Date(this.now()).toISOString();
|
|
109
|
+
(0, runtime_1.emitNervesEvent)({
|
|
110
|
+
component: "daemon",
|
|
111
|
+
event: "daemon.agent_started",
|
|
112
|
+
message: "daemon started managed agent process",
|
|
113
|
+
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
114
|
+
});
|
|
115
|
+
child.once("exit", (code, signal) => {
|
|
116
|
+
this.onExit(state, code, signal);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async stopAgent(agent) {
|
|
120
|
+
const state = this.requireAgent(agent);
|
|
121
|
+
this.clearRestartTimer(state);
|
|
122
|
+
state.stopRequested = true;
|
|
123
|
+
if (!state.process) {
|
|
124
|
+
state.snapshot.status = "stopped";
|
|
125
|
+
state.snapshot.pid = null;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const child = state.process;
|
|
129
|
+
state.process = null;
|
|
130
|
+
state.snapshot.status = "stopped";
|
|
131
|
+
state.snapshot.pid = null;
|
|
132
|
+
try {
|
|
133
|
+
child.kill("SIGTERM");
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
(0, runtime_1.emitNervesEvent)({
|
|
137
|
+
level: "warn",
|
|
138
|
+
component: "daemon",
|
|
139
|
+
event: "daemon.agent_stop_error",
|
|
140
|
+
message: "failed to send SIGTERM to managed agent",
|
|
141
|
+
meta: { agent },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async restartAgent(agent) {
|
|
146
|
+
await this.stopAgent(agent);
|
|
147
|
+
await this.startAgent(agent);
|
|
148
|
+
}
|
|
149
|
+
async stopAll() {
|
|
150
|
+
for (const state of this.agents.values()) {
|
|
151
|
+
await this.stopAgent(state.config.name);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
sendToAgent(agent, message) {
|
|
155
|
+
const state = this.requireAgent(agent);
|
|
156
|
+
if (!state.process)
|
|
157
|
+
return;
|
|
158
|
+
try {
|
|
159
|
+
state.process.send(message);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
(0, runtime_1.emitNervesEvent)({
|
|
163
|
+
level: "warn",
|
|
164
|
+
component: "daemon",
|
|
165
|
+
event: "daemon.agent_ipc_send_error",
|
|
166
|
+
message: "failed to send IPC message to managed agent",
|
|
167
|
+
meta: { agent },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
getAgentSnapshot(agent) {
|
|
172
|
+
return this.agents.get(agent)?.snapshot;
|
|
173
|
+
}
|
|
174
|
+
listAgentSnapshots() {
|
|
175
|
+
return [...this.agents.values()].map((state) => state.snapshot);
|
|
176
|
+
}
|
|
177
|
+
onExit(state, code, signal) {
|
|
178
|
+
if (!state.process)
|
|
179
|
+
return;
|
|
180
|
+
state.process = null;
|
|
181
|
+
state.snapshot.pid = null;
|
|
182
|
+
const crashed = !state.stopRequested && code !== 0;
|
|
183
|
+
const now = this.now();
|
|
184
|
+
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
185
|
+
const runDuration = Math.max(0, now - startedAt);
|
|
186
|
+
(0, runtime_1.emitNervesEvent)({
|
|
187
|
+
level: crashed ? "warn" : "info",
|
|
188
|
+
component: "daemon",
|
|
189
|
+
event: "daemon.agent_exit",
|
|
190
|
+
message: "managed agent process exited",
|
|
191
|
+
meta: { agent: state.config.name, code, signal, crashed, runDurationMs: runDuration },
|
|
192
|
+
});
|
|
193
|
+
if (!crashed) {
|
|
194
|
+
state.snapshot.status = "stopped";
|
|
195
|
+
if (runDuration >= this.stabilityThresholdMs) {
|
|
196
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
201
|
+
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
202
|
+
state.crashTimestamps.push(now);
|
|
203
|
+
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
204
|
+
state.snapshot.status = "crashed";
|
|
205
|
+
(0, runtime_1.emitNervesEvent)({
|
|
206
|
+
level: "error",
|
|
207
|
+
component: "daemon",
|
|
208
|
+
event: "daemon.agent_restart_exhausted",
|
|
209
|
+
message: "managed agent exceeded restart limit and is marked crashed",
|
|
210
|
+
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
state.snapshot.status = "starting";
|
|
215
|
+
state.snapshot.restartCount += 1;
|
|
216
|
+
const waitMs = state.snapshot.backoffMs;
|
|
217
|
+
state.snapshot.backoffMs = Math.min(state.snapshot.backoffMs * 2, this.maxBackoffMs);
|
|
218
|
+
this.clearRestartTimer(state);
|
|
219
|
+
state.restartTimer = this.setTimeoutFn(() => {
|
|
220
|
+
void this.startAgent(state.config.name);
|
|
221
|
+
}, waitMs);
|
|
222
|
+
}
|
|
223
|
+
clearRestartTimer(state) {
|
|
224
|
+
if (state.restartTimer === null)
|
|
225
|
+
return;
|
|
226
|
+
this.clearTimeoutFn(state.restartTimer);
|
|
227
|
+
state.restartTimer = null;
|
|
228
|
+
}
|
|
229
|
+
requireAgent(agent) {
|
|
230
|
+
const state = this.agents.get(agent);
|
|
231
|
+
if (!state) {
|
|
232
|
+
throw new Error(`Unknown managed agent '${agent}'.`);
|
|
233
|
+
}
|
|
234
|
+
return state;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.DaemonProcessManager = DaemonProcessManager;
|
|
@@ -0,0 +1,102 @@
|
|
|
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.configureDaemonRuntimeLogger = configureDaemonRuntimeLogger;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const nerves_1 = require("../../nerves");
|
|
41
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
+
const DEFAULT_RUNTIME_LOGGING = {
|
|
43
|
+
level: "info",
|
|
44
|
+
sinks: ["terminal", "ndjson"],
|
|
45
|
+
};
|
|
46
|
+
function defaultLevelForProcess(processName) {
|
|
47
|
+
return processName === "daemon" ? "info" : "warn";
|
|
48
|
+
}
|
|
49
|
+
function isLogLevel(value) {
|
|
50
|
+
return value === "debug" || value === "info" || value === "warn" || value === "error";
|
|
51
|
+
}
|
|
52
|
+
function resolveRuntimeLoggingConfig(configPath, processName) {
|
|
53
|
+
const defaultLevel = defaultLevelForProcess(processName);
|
|
54
|
+
let parsed = null;
|
|
55
|
+
try {
|
|
56
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
57
|
+
parsed = JSON.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
|
|
61
|
+
}
|
|
62
|
+
if (!parsed || typeof parsed !== "object") {
|
|
63
|
+
return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
|
|
64
|
+
}
|
|
65
|
+
const candidate = parsed;
|
|
66
|
+
const level = isLogLevel(candidate.level) ? candidate.level : defaultLevel;
|
|
67
|
+
const sinks = Array.isArray(candidate.sinks)
|
|
68
|
+
? candidate.sinks.filter((entry) => entry === "terminal" || entry === "ndjson")
|
|
69
|
+
: DEFAULT_RUNTIME_LOGGING.sinks;
|
|
70
|
+
return {
|
|
71
|
+
level,
|
|
72
|
+
sinks: sinks.length > 0 ? [...new Set(sinks)] : [...DEFAULT_RUNTIME_LOGGING.sinks],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function configureDaemonRuntimeLogger(processName, options = {}) {
|
|
76
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
77
|
+
const configPath = options.configPath ?? path.join(homeDir, ".agentstate", "daemon", "logging.json");
|
|
78
|
+
const config = resolveRuntimeLoggingConfig(configPath, processName);
|
|
79
|
+
const sinks = config.sinks.map((sinkName) => {
|
|
80
|
+
if (sinkName === "terminal") {
|
|
81
|
+
return (0, nerves_1.createTerminalSink)();
|
|
82
|
+
}
|
|
83
|
+
const ndjsonPath = path.join(homeDir, ".agentstate", "daemon", "logs", `${processName}.ndjson`);
|
|
84
|
+
return (0, nerves_1.createNdjsonFileSink)(ndjsonPath);
|
|
85
|
+
});
|
|
86
|
+
const logger = (0, nerves_1.createLogger)({
|
|
87
|
+
level: config.level,
|
|
88
|
+
sinks,
|
|
89
|
+
});
|
|
90
|
+
(0, runtime_1.setRuntimeLogger)(logger);
|
|
91
|
+
(0, runtime_1.emitNervesEvent)({
|
|
92
|
+
component: "daemon",
|
|
93
|
+
event: "daemon.runtime_logger_configured",
|
|
94
|
+
message: "configured daemon runtime logger",
|
|
95
|
+
meta: {
|
|
96
|
+
processName,
|
|
97
|
+
level: config.level,
|
|
98
|
+
sinks: config.sinks,
|
|
99
|
+
configPath,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|