@ouro.bot/cli 0.0.1-alpha.0 → 0.1.0-alpha.2
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 +22 -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 +296 -0
- package/dist/heart/core.js +515 -0
- package/dist/heart/daemon/daemon-cli.js +675 -0
- package/dist/heart/daemon/daemon-entry.js +74 -0
- package/dist/heart/daemon/daemon.js +313 -0
- package/dist/heart/daemon/hatch-flow.js +285 -0
- package/dist/heart/daemon/hatch-specialist.js +107 -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 +90 -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 +98 -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 +281 -0
- package/dist/heart/kicks.js +144 -0
- package/dist/heart/primitives.js +4 -0
- package/dist/heart/providers/anthropic.js +329 -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 +412 -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 +41 -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 +352 -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/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 +34 -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,107 @@
|
|
|
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.getSpecialistIdentitySourceDir = getSpecialistIdentitySourceDir;
|
|
37
|
+
exports.getRepoSpecialistIdentitiesDir = getRepoSpecialistIdentitiesDir;
|
|
38
|
+
exports.syncSpecialistIdentities = syncSpecialistIdentities;
|
|
39
|
+
exports.pickRandomSpecialistIdentity = pickRandomSpecialistIdentity;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
44
|
+
function getSpecialistIdentitySourceDir() {
|
|
45
|
+
return path.join(os.homedir(), "AgentBundles", "AdoptionSpecialist.ouro", "psyche", "identities");
|
|
46
|
+
}
|
|
47
|
+
function getRepoSpecialistIdentitiesDir() {
|
|
48
|
+
return path.join(process.cwd(), "AdoptionSpecialist.ouro", "psyche", "identities");
|
|
49
|
+
}
|
|
50
|
+
function listMarkdownIdentityFiles(dir) {
|
|
51
|
+
let entries;
|
|
52
|
+
try {
|
|
53
|
+
entries = fs.readdirSync(dir);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return entries
|
|
59
|
+
.filter((entry) => entry.endsWith(".md"))
|
|
60
|
+
.sort((left, right) => left.localeCompare(right));
|
|
61
|
+
}
|
|
62
|
+
function syncSpecialistIdentities(input) {
|
|
63
|
+
(0, runtime_1.emitNervesEvent)({
|
|
64
|
+
component: "daemon",
|
|
65
|
+
event: "daemon.hatch_identity_sync_start",
|
|
66
|
+
message: "syncing specialist identity files",
|
|
67
|
+
meta: { sourceDir: input.sourceDir, targetDir: input.targetDir },
|
|
68
|
+
});
|
|
69
|
+
const files = listMarkdownIdentityFiles(input.sourceDir);
|
|
70
|
+
fs.mkdirSync(input.targetDir, { recursive: true });
|
|
71
|
+
for (const fileName of files) {
|
|
72
|
+
const sourcePath = path.join(input.sourceDir, fileName);
|
|
73
|
+
const targetPath = path.join(input.targetDir, fileName);
|
|
74
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
75
|
+
}
|
|
76
|
+
(0, runtime_1.emitNervesEvent)({
|
|
77
|
+
component: "daemon",
|
|
78
|
+
event: "daemon.hatch_identity_sync_end",
|
|
79
|
+
message: "synced specialist identity files",
|
|
80
|
+
meta: { copiedCount: files.length },
|
|
81
|
+
});
|
|
82
|
+
return files;
|
|
83
|
+
}
|
|
84
|
+
function pickRandomSpecialistIdentity(input) {
|
|
85
|
+
const files = listMarkdownIdentityFiles(input.identitiesDir);
|
|
86
|
+
if (files.length === 0) {
|
|
87
|
+
(0, runtime_1.emitNervesEvent)({
|
|
88
|
+
level: "error",
|
|
89
|
+
component: "daemon",
|
|
90
|
+
event: "daemon.hatch_identity_pick_error",
|
|
91
|
+
message: "no specialist identities were found",
|
|
92
|
+
meta: { identitiesDir: input.identitiesDir },
|
|
93
|
+
});
|
|
94
|
+
throw new Error(`No specialist identities found in ${input.identitiesDir}`);
|
|
95
|
+
}
|
|
96
|
+
const random = input.random ?? Math.random;
|
|
97
|
+
const index = Math.min(files.length - 1, Math.floor(random() * files.length));
|
|
98
|
+
const fileName = files[index];
|
|
99
|
+
const content = fs.readFileSync(path.join(input.identitiesDir, fileName), "utf-8");
|
|
100
|
+
(0, runtime_1.emitNervesEvent)({
|
|
101
|
+
component: "daemon",
|
|
102
|
+
event: "daemon.hatch_identity_pick",
|
|
103
|
+
message: "picked specialist identity",
|
|
104
|
+
meta: { fileName },
|
|
105
|
+
});
|
|
106
|
+
return { fileName, content };
|
|
107
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HealthMonitor = void 0;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
class HealthMonitor {
|
|
6
|
+
processManager;
|
|
7
|
+
scheduler;
|
|
8
|
+
alertSink;
|
|
9
|
+
diskUsagePercent;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.processManager = options.processManager;
|
|
12
|
+
this.scheduler = options.scheduler;
|
|
13
|
+
this.alertSink = options.alertSink ?? (() => undefined);
|
|
14
|
+
this.diskUsagePercent = options.diskUsagePercent ?? (() => 0);
|
|
15
|
+
}
|
|
16
|
+
async runChecks() {
|
|
17
|
+
const results = [];
|
|
18
|
+
const snapshots = this.processManager.listAgentSnapshots();
|
|
19
|
+
const unhealthy = snapshots.filter((snapshot) => snapshot.status !== "running");
|
|
20
|
+
if (unhealthy.length > 0) {
|
|
21
|
+
results.push({
|
|
22
|
+
name: "agent-processes",
|
|
23
|
+
status: "critical",
|
|
24
|
+
message: `non-running agents: ${unhealthy.map((item) => item.name).join(", ")}`,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
results.push({ name: "agent-processes", status: "ok", message: "all managed agents running" });
|
|
29
|
+
}
|
|
30
|
+
const jobs = this.scheduler.listJobs();
|
|
31
|
+
const neverRan = jobs.filter((job) => !job.lastRun);
|
|
32
|
+
if (neverRan.length > 0) {
|
|
33
|
+
results.push({
|
|
34
|
+
name: "cron-health",
|
|
35
|
+
status: "warn",
|
|
36
|
+
message: `jobs never run: ${neverRan.map((job) => job.id).join(", ")}`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
results.push({ name: "cron-health", status: "ok", message: "cron jobs are healthy" });
|
|
41
|
+
}
|
|
42
|
+
const diskPercent = this.diskUsagePercent();
|
|
43
|
+
if (diskPercent >= 90) {
|
|
44
|
+
results.push({
|
|
45
|
+
name: "disk-space",
|
|
46
|
+
status: "critical",
|
|
47
|
+
message: `disk usage critical (${diskPercent}%)`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else if (diskPercent >= 80) {
|
|
51
|
+
results.push({
|
|
52
|
+
name: "disk-space",
|
|
53
|
+
status: "warn",
|
|
54
|
+
message: `disk usage high (${diskPercent}%)`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
results.push({
|
|
59
|
+
name: "disk-space",
|
|
60
|
+
status: "ok",
|
|
61
|
+
message: `disk usage healthy (${diskPercent}%)`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
for (const result of results) {
|
|
65
|
+
(0, runtime_1.emitNervesEvent)({
|
|
66
|
+
level: result.status === "critical" ? "error" : result.status === "warn" ? "warn" : "info",
|
|
67
|
+
component: "daemon",
|
|
68
|
+
event: "daemon.health_result",
|
|
69
|
+
message: "daemon health check result",
|
|
70
|
+
meta: { name: result.name, status: result.status },
|
|
71
|
+
});
|
|
72
|
+
if (result.status === "critical") {
|
|
73
|
+
await this.alertSink(`[critical] ${result.name}: ${result.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.HealthMonitor = HealthMonitor;
|
|
@@ -0,0 +1,146 @@
|
|
|
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 os = __importStar(require("os"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const nerves_1 = require("../../nerves");
|
|
43
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
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 homeDir = options.homeDir ?? os.homedir();
|
|
53
|
+
const existsSync = options.existsSync ?? (() => false);
|
|
54
|
+
const readdirSync = options.readdirSync ?? (() => []);
|
|
55
|
+
/* v8 ignore stop */
|
|
56
|
+
const logDir = path.join(homeDir, ".agentstate", "daemon", "logs");
|
|
57
|
+
const files = [];
|
|
58
|
+
if (existsSync(logDir)) {
|
|
59
|
+
for (const name of readdirSync(logDir)) {
|
|
60
|
+
if (!name.endsWith(".ndjson"))
|
|
61
|
+
continue;
|
|
62
|
+
if (options.agentFilter && !name.includes(options.agentFilter))
|
|
63
|
+
continue;
|
|
64
|
+
files.push(path.join(logDir, name));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return files.sort();
|
|
68
|
+
}
|
|
69
|
+
function readLastLines(filePath, count, readFileSync) {
|
|
70
|
+
let content;
|
|
71
|
+
try {
|
|
72
|
+
content = readFileSync(filePath, "utf-8");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
78
|
+
return lines.slice(-count);
|
|
79
|
+
}
|
|
80
|
+
function formatLogLine(ndjsonLine) {
|
|
81
|
+
try {
|
|
82
|
+
const entry = JSON.parse(ndjsonLine);
|
|
83
|
+
const formatted = (0, nerves_1.formatTerminalEntry)(entry);
|
|
84
|
+
const color = LEVEL_COLORS[entry.level] ?? "";
|
|
85
|
+
return `${color}${formatted}\x1b[0m`;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return ndjsonLine;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function tailLogs(options = {}) {
|
|
92
|
+
/* v8 ignore start -- integration: default DI stubs for real OS @preserve */
|
|
93
|
+
const writer = options.writer ?? ((text) => process.stdout.write(text));
|
|
94
|
+
const lineCount = options.lines ?? 20;
|
|
95
|
+
const readFileSync = options.readFileSync ?? (() => "");
|
|
96
|
+
/* v8 ignore stop */
|
|
97
|
+
const watchFile = options.watchFile;
|
|
98
|
+
const unwatchFile = options.unwatchFile;
|
|
99
|
+
const files = discoverLogFiles(options);
|
|
100
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.log_tailer_started", message: "log tailer started", meta: { fileCount: files.length, follow: !!options.follow } });
|
|
101
|
+
const fileSizes = new Map();
|
|
102
|
+
// Read initial lines
|
|
103
|
+
for (const file of files) {
|
|
104
|
+
const lines = readLastLines(file, lineCount, readFileSync);
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
writer(`${formatLogLine(line)}\n`);
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const content = readFileSync(file, "utf-8");
|
|
110
|
+
fileSizes.set(file, content.length);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
fileSizes.set(file, 0);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Follow mode
|
|
117
|
+
if (options.follow && watchFile && unwatchFile) {
|
|
118
|
+
for (const file of files) {
|
|
119
|
+
watchFile(file, () => {
|
|
120
|
+
let content;
|
|
121
|
+
try {
|
|
122
|
+
content = readFileSync(file, "utf-8");
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
/* v8 ignore next -- defensive: fileSizes always populated above @preserve */
|
|
128
|
+
const prevSize = fileSizes.get(file) ?? 0;
|
|
129
|
+
if (content.length <= prevSize)
|
|
130
|
+
return;
|
|
131
|
+
fileSizes.set(file, content.length);
|
|
132
|
+
const newContent = content.slice(prevSize);
|
|
133
|
+
const newLines = newContent.split("\n").filter((l) => l.trim().length > 0);
|
|
134
|
+
for (const line of newLines) {
|
|
135
|
+
writer(`${formatLogLine(line)}\n`);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return () => {
|
|
140
|
+
for (const file of files) {
|
|
141
|
+
unwatchFile(file);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return () => { };
|
|
146
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
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.FileMessageRouter = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
function messageId(nowIso) {
|
|
42
|
+
return `msg-${nowIso.replace(/[^0-9]/g, "")}`;
|
|
43
|
+
}
|
|
44
|
+
class FileMessageRouter {
|
|
45
|
+
baseDir;
|
|
46
|
+
now;
|
|
47
|
+
constructor(options = {}) {
|
|
48
|
+
this.baseDir = options.baseDir ?? path.join(os.homedir(), ".agentstate", "messages");
|
|
49
|
+
this.now = options.now ?? (() => new Date().toISOString());
|
|
50
|
+
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
async send(input) {
|
|
53
|
+
const queuedAt = this.now();
|
|
54
|
+
const id = messageId(queuedAt);
|
|
55
|
+
const message = {
|
|
56
|
+
id,
|
|
57
|
+
from: input.from,
|
|
58
|
+
to: input.to,
|
|
59
|
+
content: input.content,
|
|
60
|
+
queuedAt,
|
|
61
|
+
priority: input.priority ?? "normal",
|
|
62
|
+
sessionId: input.sessionId,
|
|
63
|
+
taskRef: input.taskRef,
|
|
64
|
+
};
|
|
65
|
+
const inboxPath = this.inboxPath(input.to);
|
|
66
|
+
fs.appendFileSync(inboxPath, `${JSON.stringify(message)}\n`, "utf-8");
|
|
67
|
+
(0, runtime_1.emitNervesEvent)({
|
|
68
|
+
component: "daemon",
|
|
69
|
+
event: "daemon.message_queued",
|
|
70
|
+
message: "queued inter-agent message",
|
|
71
|
+
meta: { id, from: input.from, to: input.to },
|
|
72
|
+
});
|
|
73
|
+
return { id, queuedAt };
|
|
74
|
+
}
|
|
75
|
+
pollInbox(agent) {
|
|
76
|
+
const inboxPath = this.inboxPath(agent);
|
|
77
|
+
if (!fs.existsSync(inboxPath))
|
|
78
|
+
return [];
|
|
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));
|
|
86
|
+
(0, runtime_1.emitNervesEvent)({
|
|
87
|
+
component: "daemon",
|
|
88
|
+
event: "daemon.message_polled",
|
|
89
|
+
message: "polled inter-agent inbox",
|
|
90
|
+
meta: { agent, messageCount: messages.length },
|
|
91
|
+
});
|
|
92
|
+
return messages;
|
|
93
|
+
}
|
|
94
|
+
inboxPath(agent) {
|
|
95
|
+
return path.join(this.baseDir, `${agent}-inbox.jsonl`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.FileMessageRouter = FileMessageRouter;
|
|
@@ -0,0 +1,260 @@
|
|
|
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.CrontabCronManager = exports.LaunchdCronManager = void 0;
|
|
37
|
+
exports.createOsCronManager = createOsCronManager;
|
|
38
|
+
exports.cadenceToSeconds = cadenceToSeconds;
|
|
39
|
+
exports.scheduleToCalendarInterval = scheduleToCalendarInterval;
|
|
40
|
+
exports.generatePlistXml = generatePlistXml;
|
|
41
|
+
exports.plistLabel = plistLabel;
|
|
42
|
+
exports.crontabLine = crontabLine;
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
45
|
+
const PLIST_PREFIX = "bot.ouro.";
|
|
46
|
+
function plistLabel(job) {
|
|
47
|
+
return `${PLIST_PREFIX}${job.agent}.${job.taskId}`;
|
|
48
|
+
}
|
|
49
|
+
function cadenceToSeconds(schedule) {
|
|
50
|
+
const parts = schedule.trim().split(/\s+/);
|
|
51
|
+
if (parts.length !== 5)
|
|
52
|
+
return null;
|
|
53
|
+
const [minute, hour, day, month, weekday] = parts;
|
|
54
|
+
// Simple interval patterns only
|
|
55
|
+
if (month !== "*" || weekday !== "*" || day !== "*")
|
|
56
|
+
return null;
|
|
57
|
+
const everyNMinutes = /^\*\/(\d+)$/.exec(minute);
|
|
58
|
+
if (everyNMinutes && hour === "*") {
|
|
59
|
+
return parseInt(everyNMinutes[1], 10) * 60;
|
|
60
|
+
}
|
|
61
|
+
const everyNHours = /^\*\/(\d+)$/.exec(hour);
|
|
62
|
+
if (everyNHours && minute === "0") {
|
|
63
|
+
return parseInt(everyNHours[1], 10) * 3600;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function scheduleToCalendarInterval(schedule) {
|
|
68
|
+
const parts = schedule.trim().split(/\s+/);
|
|
69
|
+
if (parts.length !== 5)
|
|
70
|
+
return null;
|
|
71
|
+
const [minute, hour, day, month] = parts;
|
|
72
|
+
const result = {};
|
|
73
|
+
if (minute !== "*" && !/^\*\//.test(minute))
|
|
74
|
+
result.Minute = parseInt(minute, 10);
|
|
75
|
+
if (hour !== "*" && !/^\*\//.test(hour))
|
|
76
|
+
result.Hour = parseInt(hour, 10);
|
|
77
|
+
if (day !== "*")
|
|
78
|
+
result.Day = parseInt(day, 10);
|
|
79
|
+
if (month !== "*")
|
|
80
|
+
result.Month = parseInt(month, 10);
|
|
81
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
82
|
+
}
|
|
83
|
+
function generatePlistXml(job) {
|
|
84
|
+
const label = plistLabel(job);
|
|
85
|
+
const seconds = cadenceToSeconds(job.schedule);
|
|
86
|
+
const calendar = seconds === null ? scheduleToCalendarInterval(job.schedule) : null;
|
|
87
|
+
let triggerXml;
|
|
88
|
+
if (seconds !== null) {
|
|
89
|
+
triggerXml = ` <key>StartInterval</key>\n <integer>${seconds}</integer>`;
|
|
90
|
+
}
|
|
91
|
+
else if (calendar !== null) {
|
|
92
|
+
const entries = Object.entries(calendar)
|
|
93
|
+
.map(([k, v]) => ` <key>${k}</key>\n <integer>${v}</integer>`)
|
|
94
|
+
.join("\n");
|
|
95
|
+
triggerXml = ` <key>StartCalendarInterval</key>\n <dict>\n${entries}\n </dict>`;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
triggerXml = ` <key>StartInterval</key>\n <integer>1800</integer>`;
|
|
99
|
+
}
|
|
100
|
+
return [
|
|
101
|
+
`<?xml version="1.0" encoding="UTF-8"?>`,
|
|
102
|
+
`<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
|
|
103
|
+
`<plist version="1.0">`,
|
|
104
|
+
`<dict>`,
|
|
105
|
+
` <key>Label</key>`,
|
|
106
|
+
` <string>${label}</string>`,
|
|
107
|
+
` <key>ProgramArguments</key>`,
|
|
108
|
+
` <array>`,
|
|
109
|
+
` <string>${job.command.split(" ")[0]}</string>`,
|
|
110
|
+
...job.command.split(" ").slice(1).map((arg) => ` <string>${arg}</string>`),
|
|
111
|
+
` </array>`,
|
|
112
|
+
triggerXml,
|
|
113
|
+
` <key>StandardOutPath</key>`,
|
|
114
|
+
` <string>/tmp/${label}.stdout.log</string>`,
|
|
115
|
+
` <key>StandardErrorPath</key>`,
|
|
116
|
+
` <string>/tmp/${label}.stderr.log</string>`,
|
|
117
|
+
`</dict>`,
|
|
118
|
+
`</plist>`,
|
|
119
|
+
``,
|
|
120
|
+
].join("\n");
|
|
121
|
+
}
|
|
122
|
+
class LaunchdCronManager {
|
|
123
|
+
deps;
|
|
124
|
+
constructor(deps) {
|
|
125
|
+
this.deps = deps;
|
|
126
|
+
}
|
|
127
|
+
get launchAgentsDir() {
|
|
128
|
+
return `${this.deps.homeDir}/Library/LaunchAgents`;
|
|
129
|
+
}
|
|
130
|
+
sync(jobs) {
|
|
131
|
+
this.deps.mkdirp(this.launchAgentsDir);
|
|
132
|
+
const desiredLabels = new Set(jobs.map(plistLabel));
|
|
133
|
+
// Remove stale plists
|
|
134
|
+
const existing = this.listPlistFiles();
|
|
135
|
+
for (const filename of existing) {
|
|
136
|
+
const label = filename.replace(".plist", "");
|
|
137
|
+
if (!desiredLabels.has(label)) {
|
|
138
|
+
const fullPath = `${this.launchAgentsDir}/${filename}`;
|
|
139
|
+
try {
|
|
140
|
+
this.deps.exec(`launchctl unload "${fullPath}"`);
|
|
141
|
+
}
|
|
142
|
+
catch { /* best effort */ }
|
|
143
|
+
this.deps.removeFile(fullPath);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Write current plists
|
|
147
|
+
for (const job of jobs) {
|
|
148
|
+
const label = plistLabel(job);
|
|
149
|
+
const filename = `${label}.plist`;
|
|
150
|
+
const fullPath = `${this.launchAgentsDir}/${filename}`;
|
|
151
|
+
const xml = generatePlistXml(job);
|
|
152
|
+
try {
|
|
153
|
+
this.deps.exec(`launchctl unload "${fullPath}"`);
|
|
154
|
+
}
|
|
155
|
+
catch { /* best effort */ }
|
|
156
|
+
this.deps.writeFile(fullPath, xml);
|
|
157
|
+
try {
|
|
158
|
+
this.deps.exec(`launchctl load "${fullPath}"`);
|
|
159
|
+
}
|
|
160
|
+
catch { /* best effort */ }
|
|
161
|
+
}
|
|
162
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.os_cron_synced", message: "synced OS cron entries", meta: { platform: "darwin", jobCount: jobs.length } });
|
|
163
|
+
}
|
|
164
|
+
removeAll() {
|
|
165
|
+
const existing = this.listPlistFiles();
|
|
166
|
+
for (const filename of existing) {
|
|
167
|
+
const fullPath = `${this.launchAgentsDir}/${filename}`;
|
|
168
|
+
try {
|
|
169
|
+
this.deps.exec(`launchctl unload "${fullPath}"`);
|
|
170
|
+
}
|
|
171
|
+
catch { /* best effort */ }
|
|
172
|
+
this.deps.removeFile(fullPath);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
list() {
|
|
176
|
+
return this.listPlistFiles().map((f) => f.replace(".plist", ""));
|
|
177
|
+
}
|
|
178
|
+
listPlistFiles() {
|
|
179
|
+
if (!this.deps.existsFile(this.launchAgentsDir))
|
|
180
|
+
return [];
|
|
181
|
+
return this.deps.listDir(this.launchAgentsDir).filter((f) => f.startsWith(PLIST_PREFIX) && f.endsWith(".plist"));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.LaunchdCronManager = LaunchdCronManager;
|
|
185
|
+
const CRONTAB_MARKER_PREFIX = "# ouro:";
|
|
186
|
+
function crontabLine(job) {
|
|
187
|
+
return `${CRONTAB_MARKER_PREFIX}${job.id}\n${job.schedule} ${job.command}`;
|
|
188
|
+
}
|
|
189
|
+
class CrontabCronManager {
|
|
190
|
+
deps;
|
|
191
|
+
constructor(deps) {
|
|
192
|
+
this.deps = deps;
|
|
193
|
+
}
|
|
194
|
+
sync(jobs) {
|
|
195
|
+
const currentLines = this.readCrontab();
|
|
196
|
+
const cleaned = this.removeOuroLines(currentLines);
|
|
197
|
+
const newLines = jobs.map(crontabLine);
|
|
198
|
+
const combined = [...cleaned, ...newLines].join("\n").trim();
|
|
199
|
+
this.deps.execWrite("crontab -", combined ? `${combined}\n` : "");
|
|
200
|
+
}
|
|
201
|
+
removeAll() {
|
|
202
|
+
const currentLines = this.readCrontab();
|
|
203
|
+
const cleaned = this.removeOuroLines(currentLines);
|
|
204
|
+
const combined = cleaned.join("\n").trim();
|
|
205
|
+
this.deps.execWrite("crontab -", combined ? `${combined}\n` : "");
|
|
206
|
+
}
|
|
207
|
+
list() {
|
|
208
|
+
const lines = this.readCrontab();
|
|
209
|
+
return lines
|
|
210
|
+
.filter((l) => l.startsWith(CRONTAB_MARKER_PREFIX))
|
|
211
|
+
.map((l) => l.slice(CRONTAB_MARKER_PREFIX.length));
|
|
212
|
+
}
|
|
213
|
+
readCrontab() {
|
|
214
|
+
try {
|
|
215
|
+
return this.deps.execOutput("crontab -l").split("\n");
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
removeOuroLines(lines) {
|
|
222
|
+
const result = [];
|
|
223
|
+
let skipNext = false;
|
|
224
|
+
for (const line of lines) {
|
|
225
|
+
if (line.startsWith(CRONTAB_MARKER_PREFIX)) {
|
|
226
|
+
skipNext = true;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (skipNext) {
|
|
230
|
+
skipNext = false;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
result.push(line);
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.CrontabCronManager = CrontabCronManager;
|
|
239
|
+
function createOsCronManager(options = {}) {
|
|
240
|
+
const platform = options.platform ?? process.platform;
|
|
241
|
+
if (platform === "darwin") {
|
|
242
|
+
/* v8 ignore start -- integration: default stubs for real OS operations @preserve */
|
|
243
|
+
const deps = options.launchdDeps ?? {
|
|
244
|
+
exec: () => { },
|
|
245
|
+
writeFile: () => { },
|
|
246
|
+
removeFile: () => { },
|
|
247
|
+
existsFile: () => false,
|
|
248
|
+
listDir: () => [],
|
|
249
|
+
mkdirp: () => { },
|
|
250
|
+
homeDir: os.homedir(),
|
|
251
|
+
};
|
|
252
|
+
/* v8 ignore stop */
|
|
253
|
+
return new LaunchdCronManager(deps);
|
|
254
|
+
}
|
|
255
|
+
const deps = options.crontabDeps ?? {
|
|
256
|
+
execOutput: () => "",
|
|
257
|
+
execWrite: () => { },
|
|
258
|
+
};
|
|
259
|
+
return new CrontabCronManager(deps);
|
|
260
|
+
}
|