@ouro.bot/cli 0.1.0-alpha.20 → 0.1.0-alpha.22
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/changelog.json +26 -0
- package/dist/heart/daemon/daemon-cli.js +16 -4
- package/dist/heart/daemon/daemon-runtime-sync.js +90 -0
- package/dist/heart/daemon/daemon.js +6 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +134 -0
- package/dist/heart/daemon/ouro-path-installer.js +2 -1
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/update-checker.js +103 -0
- package/dist/heart/daemon/update-hooks.js +120 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +48 -0
- package/dist/mind/bundle-manifest.js +11 -0
- package/dist/mind/prompt.js +17 -0
- package/dist/senses/cli.js +6 -0
- package/package.json +6 -3
package/changelog.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Human-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
|
+
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.22",
|
|
6
|
+
"changes": [
|
|
7
|
+
"You now know your runtime version and can see when it changed. Your system prompt shows your current version and what you were running before, so you can track what's new.",
|
|
8
|
+
"A changelog file is now shipped with the runtime. You can read it to understand exactly what changed between versions.",
|
|
9
|
+
"When the runtime updates, your bundle-meta.json is automatically updated with the new version info. Your previous version is preserved so you always know your version history.",
|
|
10
|
+
"The daemon can now check for new versions of the runtime on npm and auto-update with a staged restart that validates hooks before switching to new code.",
|
|
11
|
+
"The daemon can now be managed as a macOS LaunchAgent for automatic restart on crash.",
|
|
12
|
+
"The daemon now auto-syncs the ouro.bot npm wrapper version on startup to keep the npx entry point current."
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"version": "0.1.0-alpha.21",
|
|
17
|
+
"changes": [
|
|
18
|
+
"You now know your runtime version and can see when it changed. Your system prompt shows your current version and what you were running before, so you can track what's new.",
|
|
19
|
+
"A changelog file is now shipped with the runtime. You can read it to understand exactly what changed between versions.",
|
|
20
|
+
"When the runtime updates, your bundle-meta.json is automatically updated with the new version info. Your previous version is preserved so you always know your version history.",
|
|
21
|
+
"The daemon can now check for new versions of the runtime on npm and auto-update with a staged restart that validates hooks before switching to new code.",
|
|
22
|
+
"The daemon can now be managed as a macOS LaunchAgent for automatic restart on crash."
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -55,6 +55,7 @@ const specialist_orchestrator_1 = require("./specialist-orchestrator");
|
|
|
55
55
|
const specialist_prompt_1 = require("./specialist-prompt");
|
|
56
56
|
const specialist_tools_1 = require("./specialist-tools");
|
|
57
57
|
const runtime_metadata_1 = require("./runtime-metadata");
|
|
58
|
+
const daemon_runtime_sync_1 = require("./daemon-runtime-sync");
|
|
58
59
|
function stringField(value) {
|
|
59
60
|
return typeof value === "string" ? value : null;
|
|
60
61
|
}
|
|
@@ -195,10 +196,21 @@ function formatDaemonStatusOutput(response, fallback) {
|
|
|
195
196
|
async function ensureDaemonRunning(deps) {
|
|
196
197
|
const alive = await deps.checkSocketAlive(deps.socketPath);
|
|
197
198
|
if (alive) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
const localRuntime = (0, runtime_metadata_1.getRuntimeMetadata)();
|
|
200
|
+
return (0, daemon_runtime_sync_1.ensureCurrentDaemonRuntime)({
|
|
201
|
+
socketPath: deps.socketPath,
|
|
202
|
+
localVersion: localRuntime.version,
|
|
203
|
+
fetchRunningVersion: async () => {
|
|
204
|
+
const status = await deps.sendCommand(deps.socketPath, { kind: "daemon.status" });
|
|
205
|
+
const payload = parseStatusPayload(status.data);
|
|
206
|
+
return payload?.overview.version ?? "unknown";
|
|
207
|
+
},
|
|
208
|
+
stopDaemon: async () => {
|
|
209
|
+
await deps.sendCommand(deps.socketPath, { kind: "daemon.stop" });
|
|
210
|
+
},
|
|
211
|
+
cleanupStaleSocket: deps.cleanupStaleSocket,
|
|
212
|
+
startDaemonProcess: deps.startDaemonProcess,
|
|
213
|
+
});
|
|
202
214
|
}
|
|
203
215
|
deps.cleanupStaleSocket(deps.socketPath);
|
|
204
216
|
const started = await deps.startDaemonProcess(deps.socketPath);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureCurrentDaemonRuntime = ensureCurrentDaemonRuntime;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
function isKnownVersion(version) {
|
|
6
|
+
return version !== "unknown" && version.trim().length > 0;
|
|
7
|
+
}
|
|
8
|
+
function formatErrorReason(error) {
|
|
9
|
+
return error instanceof Error ? error.message : String(error);
|
|
10
|
+
}
|
|
11
|
+
async function ensureCurrentDaemonRuntime(deps) {
|
|
12
|
+
try {
|
|
13
|
+
const runningVersion = await deps.fetchRunningVersion();
|
|
14
|
+
let result;
|
|
15
|
+
if (isKnownVersion(deps.localVersion) &&
|
|
16
|
+
isKnownVersion(runningVersion) &&
|
|
17
|
+
runningVersion !== deps.localVersion) {
|
|
18
|
+
try {
|
|
19
|
+
await deps.stopDaemon();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const reason = formatErrorReason(error);
|
|
23
|
+
result = {
|
|
24
|
+
alreadyRunning: true,
|
|
25
|
+
message: `daemon already running (${deps.socketPath}; could not replace stale daemon ${runningVersion} -> ${deps.localVersion}: ${reason})`,
|
|
26
|
+
};
|
|
27
|
+
(0, runtime_1.emitNervesEvent)({
|
|
28
|
+
level: "warn",
|
|
29
|
+
component: "daemon",
|
|
30
|
+
event: "daemon.runtime_sync_decision",
|
|
31
|
+
message: "evaluated daemon runtime sync outcome",
|
|
32
|
+
meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, runningVersion, action: "stale_replace_failed", reason },
|
|
33
|
+
});
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
deps.cleanupStaleSocket(deps.socketPath);
|
|
37
|
+
const started = await deps.startDaemonProcess(deps.socketPath);
|
|
38
|
+
result = {
|
|
39
|
+
alreadyRunning: false,
|
|
40
|
+
message: `restarted stale daemon from ${runningVersion} to ${deps.localVersion} (pid ${started.pid ?? "unknown"})`,
|
|
41
|
+
};
|
|
42
|
+
(0, runtime_1.emitNervesEvent)({
|
|
43
|
+
component: "daemon",
|
|
44
|
+
event: "daemon.runtime_sync_decision",
|
|
45
|
+
message: "evaluated daemon runtime sync outcome",
|
|
46
|
+
meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, runningVersion, action: "stale_restarted", pid: started.pid ?? null },
|
|
47
|
+
});
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
if (!isKnownVersion(deps.localVersion) || !isKnownVersion(runningVersion)) {
|
|
51
|
+
result = {
|
|
52
|
+
alreadyRunning: true,
|
|
53
|
+
message: `daemon already running (${deps.socketPath}; unable to verify version)`,
|
|
54
|
+
};
|
|
55
|
+
(0, runtime_1.emitNervesEvent)({
|
|
56
|
+
component: "daemon",
|
|
57
|
+
event: "daemon.runtime_sync_decision",
|
|
58
|
+
message: "evaluated daemon runtime sync outcome",
|
|
59
|
+
meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, runningVersion, action: "unknown_version" },
|
|
60
|
+
});
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const reason = formatErrorReason(error);
|
|
66
|
+
const result = {
|
|
67
|
+
alreadyRunning: true,
|
|
68
|
+
message: `daemon already running (${deps.socketPath}; unable to verify version: ${reason})`,
|
|
69
|
+
};
|
|
70
|
+
(0, runtime_1.emitNervesEvent)({
|
|
71
|
+
level: "warn",
|
|
72
|
+
component: "daemon",
|
|
73
|
+
event: "daemon.runtime_sync_decision",
|
|
74
|
+
message: "evaluated daemon runtime sync outcome",
|
|
75
|
+
meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, action: "status_lookup_failed", reason },
|
|
76
|
+
});
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
const result = {
|
|
80
|
+
alreadyRunning: true,
|
|
81
|
+
message: `daemon already running (${deps.socketPath})`,
|
|
82
|
+
};
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "daemon",
|
|
85
|
+
event: "daemon.runtime_sync_decision",
|
|
86
|
+
message: "evaluated daemon runtime sync outcome",
|
|
87
|
+
meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, action: "already_current" },
|
|
88
|
+
});
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
@@ -40,6 +40,9 @@ const path = __importStar(require("path"));
|
|
|
40
40
|
const identity_1 = require("../identity");
|
|
41
41
|
const runtime_1 = require("../../nerves/runtime");
|
|
42
42
|
const runtime_metadata_1 = require("./runtime-metadata");
|
|
43
|
+
const update_hooks_1 = require("./update-hooks");
|
|
44
|
+
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
45
|
+
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
43
46
|
function buildWorkerRows(snapshots) {
|
|
44
47
|
return snapshots.map((snapshot) => ({
|
|
45
48
|
agent: snapshot.name,
|
|
@@ -107,6 +110,9 @@ class OuroDaemon {
|
|
|
107
110
|
message: "starting daemon server",
|
|
108
111
|
meta: { socketPath: this.socketPath },
|
|
109
112
|
});
|
|
113
|
+
// Register update hooks and apply pending updates before starting agents
|
|
114
|
+
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
115
|
+
await (0, update_hooks_1.applyPendingUpdates)(this.bundlesRoot, (0, bundle_manifest_1.getPackageVersion)());
|
|
110
116
|
await this.processManager.startAutoStartAgents();
|
|
111
117
|
await this.senseManager?.startAutoStartSenses();
|
|
112
118
|
this.scheduler.start?.();
|
|
@@ -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,134 @@
|
|
|
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.installLaunchAgent = installLaunchAgent;
|
|
39
|
+
exports.uninstallLaunchAgent = uninstallLaunchAgent;
|
|
40
|
+
exports.isDaemonInstalled = isDaemonInstalled;
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
exports.DAEMON_PLIST_LABEL = "bot.ouro.daemon";
|
|
44
|
+
function plistFilePath(homeDir) {
|
|
45
|
+
return path.join(homeDir, "Library", "LaunchAgents", `${exports.DAEMON_PLIST_LABEL}.plist`);
|
|
46
|
+
}
|
|
47
|
+
function generateDaemonPlist(options) {
|
|
48
|
+
(0, runtime_1.emitNervesEvent)({
|
|
49
|
+
component: "daemon",
|
|
50
|
+
event: "daemon.launchd_generate_plist",
|
|
51
|
+
message: "generating daemon plist",
|
|
52
|
+
meta: { entryPath: options.entryPath, socketPath: options.socketPath },
|
|
53
|
+
});
|
|
54
|
+
const lines = [
|
|
55
|
+
`<?xml version="1.0" encoding="UTF-8"?>`,
|
|
56
|
+
`<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
|
|
57
|
+
`<plist version="1.0">`,
|
|
58
|
+
`<dict>`,
|
|
59
|
+
` <key>Label</key>`,
|
|
60
|
+
` <string>${exports.DAEMON_PLIST_LABEL}</string>`,
|
|
61
|
+
` <key>ProgramArguments</key>`,
|
|
62
|
+
` <array>`,
|
|
63
|
+
` <string>${options.nodePath}</string>`,
|
|
64
|
+
` <string>${options.entryPath}</string>`,
|
|
65
|
+
` <string>--socket</string>`,
|
|
66
|
+
` <string>${options.socketPath}</string>`,
|
|
67
|
+
` </array>`,
|
|
68
|
+
` <key>KeepAlive</key>`,
|
|
69
|
+
` <true/>`,
|
|
70
|
+
];
|
|
71
|
+
if (options.logDir) {
|
|
72
|
+
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>`);
|
|
73
|
+
}
|
|
74
|
+
lines.push(`</dict>`, `</plist>`, ``);
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
77
|
+
function installLaunchAgent(deps, options) {
|
|
78
|
+
(0, runtime_1.emitNervesEvent)({
|
|
79
|
+
component: "daemon",
|
|
80
|
+
event: "daemon.launchd_install",
|
|
81
|
+
message: "installing launch agent",
|
|
82
|
+
meta: { entryPath: options.entryPath, socketPath: options.socketPath },
|
|
83
|
+
});
|
|
84
|
+
const launchAgentsDir = path.join(deps.homeDir, "Library", "LaunchAgents");
|
|
85
|
+
deps.mkdirp(launchAgentsDir);
|
|
86
|
+
const fullPath = plistFilePath(deps.homeDir);
|
|
87
|
+
// Unload existing (best effort) for idempotent re-install
|
|
88
|
+
if (deps.existsFile(fullPath)) {
|
|
89
|
+
try {
|
|
90
|
+
deps.exec(`launchctl unload "${fullPath}"`);
|
|
91
|
+
}
|
|
92
|
+
catch { /* best effort */ }
|
|
93
|
+
}
|
|
94
|
+
const xml = generateDaemonPlist(options);
|
|
95
|
+
deps.writeFile(fullPath, xml);
|
|
96
|
+
deps.exec(`launchctl load "${fullPath}"`);
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
component: "daemon",
|
|
99
|
+
event: "daemon.launchd_installed",
|
|
100
|
+
message: "launch agent installed",
|
|
101
|
+
meta: { plistPath: fullPath },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function uninstallLaunchAgent(deps) {
|
|
105
|
+
(0, runtime_1.emitNervesEvent)({
|
|
106
|
+
component: "daemon",
|
|
107
|
+
event: "daemon.launchd_uninstall",
|
|
108
|
+
message: "uninstalling launch agent",
|
|
109
|
+
meta: {},
|
|
110
|
+
});
|
|
111
|
+
const fullPath = plistFilePath(deps.homeDir);
|
|
112
|
+
if (deps.existsFile(fullPath)) {
|
|
113
|
+
try {
|
|
114
|
+
deps.exec(`launchctl unload "${fullPath}"`);
|
|
115
|
+
}
|
|
116
|
+
catch { /* best effort */ }
|
|
117
|
+
deps.removeFile(fullPath);
|
|
118
|
+
}
|
|
119
|
+
(0, runtime_1.emitNervesEvent)({
|
|
120
|
+
component: "daemon",
|
|
121
|
+
event: "daemon.launchd_uninstalled",
|
|
122
|
+
message: "launch agent uninstalled",
|
|
123
|
+
meta: { plistPath: fullPath },
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function isDaemonInstalled(deps) {
|
|
127
|
+
(0, runtime_1.emitNervesEvent)({
|
|
128
|
+
component: "daemon",
|
|
129
|
+
event: "daemon.launchd_check_installed",
|
|
130
|
+
message: "checking if daemon is installed",
|
|
131
|
+
meta: {},
|
|
132
|
+
});
|
|
133
|
+
return deps.existsFile(plistFilePath(deps.homeDir));
|
|
134
|
+
}
|
|
@@ -38,8 +38,9 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const os = __importStar(require("os"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
const CLI_PACKAGE_SPECIFIER = "@ouro.bot/cli@alpha";
|
|
41
42
|
const WRAPPER_SCRIPT = `#!/bin/sh
|
|
42
|
-
exec npx --yes
|
|
43
|
+
exec npx --yes ${CLI_PACKAGE_SPECIFIER} "$@"
|
|
43
44
|
`;
|
|
44
45
|
function detectShellProfile(homeDir, shell) {
|
|
45
46
|
if (!shell)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runHooks = runHooks;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
6
|
+
async function runHooks(deps) {
|
|
7
|
+
(0, runtime_1.emitNervesEvent)({
|
|
8
|
+
component: "daemon",
|
|
9
|
+
event: "daemon.run_hooks_start",
|
|
10
|
+
message: "running update hooks",
|
|
11
|
+
meta: { bundlesRoot: deps.bundlesRoot },
|
|
12
|
+
});
|
|
13
|
+
try {
|
|
14
|
+
deps.registerUpdateHook(bundle_meta_1.bundleMetaHook);
|
|
15
|
+
const currentVersion = deps.getPackageVersion();
|
|
16
|
+
await deps.applyPendingUpdates(deps.bundlesRoot, currentVersion);
|
|
17
|
+
(0, runtime_1.emitNervesEvent)({
|
|
18
|
+
component: "daemon",
|
|
19
|
+
event: "daemon.run_hooks_success",
|
|
20
|
+
message: "update hooks completed successfully",
|
|
21
|
+
meta: { bundlesRoot: deps.bundlesRoot },
|
|
22
|
+
});
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
(0, runtime_1.emitNervesEvent)({
|
|
27
|
+
component: "daemon",
|
|
28
|
+
event: "daemon.run_hooks_error",
|
|
29
|
+
message: "update hooks failed",
|
|
30
|
+
meta: {
|
|
31
|
+
bundlesRoot: deps.bundlesRoot,
|
|
32
|
+
error: err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err),
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
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.performStagedRestart = performStagedRestart;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
39
|
+
async function performStagedRestart(version, deps) {
|
|
40
|
+
(0, runtime_1.emitNervesEvent)({
|
|
41
|
+
component: "daemon",
|
|
42
|
+
event: "daemon.staged_restart_start",
|
|
43
|
+
message: "starting staged restart",
|
|
44
|
+
meta: { version },
|
|
45
|
+
});
|
|
46
|
+
// Step 1: Install new version
|
|
47
|
+
try {
|
|
48
|
+
deps.execSync(`npm install -g @ouro.bot/cli@${version}`);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
|
|
52
|
+
(0, runtime_1.emitNervesEvent)({
|
|
53
|
+
component: "daemon",
|
|
54
|
+
event: "daemon.staged_restart_install_failed",
|
|
55
|
+
message: "npm install failed",
|
|
56
|
+
meta: { version, error: errorMessage },
|
|
57
|
+
});
|
|
58
|
+
return { ok: false, error: errorMessage };
|
|
59
|
+
}
|
|
60
|
+
// Step 2: Resolve new code path
|
|
61
|
+
const newCodePath = deps.resolveNewCodePath(version);
|
|
62
|
+
if (!newCodePath) {
|
|
63
|
+
(0, runtime_1.emitNervesEvent)({
|
|
64
|
+
component: "daemon",
|
|
65
|
+
event: "daemon.staged_restart_path_failed",
|
|
66
|
+
message: "could not resolve new code path",
|
|
67
|
+
meta: { version },
|
|
68
|
+
});
|
|
69
|
+
return { ok: false, error: "could not resolve new code path after install" };
|
|
70
|
+
}
|
|
71
|
+
// Step 3: Spawn hook runner on NEW code
|
|
72
|
+
const hookRunnerPath = path.join(newCodePath, "dist", "heart", "daemon", "run-hooks.js");
|
|
73
|
+
const spawnResult = deps.spawnSync(deps.nodePath, [hookRunnerPath, "--bundles-root", deps.bundlesRoot], { stdio: "inherit" });
|
|
74
|
+
if (spawnResult.error) {
|
|
75
|
+
const errorMessage = spawnResult.error.message;
|
|
76
|
+
(0, runtime_1.emitNervesEvent)({
|
|
77
|
+
component: "daemon",
|
|
78
|
+
event: "daemon.staged_restart_spawn_failed",
|
|
79
|
+
message: "hook runner spawn failed",
|
|
80
|
+
meta: { version, error: errorMessage },
|
|
81
|
+
});
|
|
82
|
+
return { ok: false, error: errorMessage };
|
|
83
|
+
}
|
|
84
|
+
if (spawnResult.status !== 0) {
|
|
85
|
+
(0, runtime_1.emitNervesEvent)({
|
|
86
|
+
component: "daemon",
|
|
87
|
+
event: "daemon.staged_restart_hooks_failed",
|
|
88
|
+
message: "hook runner exited with non-zero status",
|
|
89
|
+
meta: { version, exitCode: spawnResult.status },
|
|
90
|
+
});
|
|
91
|
+
return { ok: false, error: `hook runner exited with code ${spawnResult.status}` };
|
|
92
|
+
}
|
|
93
|
+
// Step 4: Graceful shutdown (launchd will restart with new code)
|
|
94
|
+
(0, runtime_1.emitNervesEvent)({
|
|
95
|
+
component: "daemon",
|
|
96
|
+
event: "daemon.staged_restart_hooks_passed",
|
|
97
|
+
message: "hooks passed, shutting down for restart",
|
|
98
|
+
meta: { version },
|
|
99
|
+
});
|
|
100
|
+
try {
|
|
101
|
+
await deps.gracefulShutdown();
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
const shutdownError = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
|
|
105
|
+
(0, runtime_1.emitNervesEvent)({
|
|
106
|
+
component: "daemon",
|
|
107
|
+
event: "daemon.staged_restart_shutdown_error",
|
|
108
|
+
message: "graceful shutdown encountered error",
|
|
109
|
+
meta: { version, error: shutdownError },
|
|
110
|
+
});
|
|
111
|
+
return { ok: true, shutdownError };
|
|
112
|
+
}
|
|
113
|
+
return { ok: true };
|
|
114
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
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.checkForUpdate = checkForUpdate;
|
|
37
|
+
exports.startUpdateChecker = startUpdateChecker;
|
|
38
|
+
exports.stopUpdateChecker = stopUpdateChecker;
|
|
39
|
+
const semver = __importStar(require("semver"));
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
const DEFAULT_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
|
|
42
|
+
async function checkForUpdate(currentVersion, deps) {
|
|
43
|
+
(0, runtime_1.emitNervesEvent)({
|
|
44
|
+
component: "daemon",
|
|
45
|
+
event: "daemon.update_check",
|
|
46
|
+
message: "checking for update",
|
|
47
|
+
meta: { currentVersion, distTag: deps.distTag },
|
|
48
|
+
});
|
|
49
|
+
let registryData;
|
|
50
|
+
try {
|
|
51
|
+
registryData = (await deps.fetchRegistryJson());
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
|
|
55
|
+
return { available: false, error: errorMessage };
|
|
56
|
+
}
|
|
57
|
+
const distTags = registryData?.["dist-tags"];
|
|
58
|
+
if (!distTags) {
|
|
59
|
+
return { available: false, error: "registry response missing dist-tags" };
|
|
60
|
+
}
|
|
61
|
+
const latestVersion = distTags[deps.distTag];
|
|
62
|
+
if (!latestVersion) {
|
|
63
|
+
return { available: false, error: `dist-tag "${deps.distTag}" not found in registry` };
|
|
64
|
+
}
|
|
65
|
+
const available = semver.gt(latestVersion, currentVersion);
|
|
66
|
+
(0, runtime_1.emitNervesEvent)({
|
|
67
|
+
component: "daemon",
|
|
68
|
+
event: "daemon.update_check_result",
|
|
69
|
+
message: available ? "update available" : "no update available",
|
|
70
|
+
meta: { currentVersion, latestVersion, available },
|
|
71
|
+
});
|
|
72
|
+
return { available, latestVersion };
|
|
73
|
+
}
|
|
74
|
+
let _intervalId = null;
|
|
75
|
+
function startUpdateChecker(options) {
|
|
76
|
+
const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
77
|
+
(0, runtime_1.emitNervesEvent)({
|
|
78
|
+
component: "daemon",
|
|
79
|
+
event: "daemon.update_checker_start",
|
|
80
|
+
message: "starting update checker",
|
|
81
|
+
meta: { intervalMs, currentVersion: options.currentVersion },
|
|
82
|
+
});
|
|
83
|
+
_intervalId = setInterval(() => {
|
|
84
|
+
void (async () => {
|
|
85
|
+
const result = await checkForUpdate(options.currentVersion, options.deps);
|
|
86
|
+
if (result.available && options.onUpdate) {
|
|
87
|
+
await options.onUpdate(result);
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
}, intervalMs);
|
|
91
|
+
}
|
|
92
|
+
function stopUpdateChecker() {
|
|
93
|
+
if (_intervalId) {
|
|
94
|
+
clearInterval(_intervalId);
|
|
95
|
+
_intervalId = null;
|
|
96
|
+
}
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
component: "daemon",
|
|
99
|
+
event: "daemon.update_checker_stop",
|
|
100
|
+
message: "stopping update checker",
|
|
101
|
+
meta: {},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
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.registerUpdateHook = registerUpdateHook;
|
|
37
|
+
exports.getRegisteredHooks = getRegisteredHooks;
|
|
38
|
+
exports.clearRegisteredHooks = clearRegisteredHooks;
|
|
39
|
+
exports.applyPendingUpdates = applyPendingUpdates;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const _hooks = [];
|
|
44
|
+
function registerUpdateHook(hook) {
|
|
45
|
+
_hooks.push(hook);
|
|
46
|
+
(0, runtime_1.emitNervesEvent)({
|
|
47
|
+
component: "daemon",
|
|
48
|
+
event: "daemon.update_hook_registered",
|
|
49
|
+
message: "registered update hook",
|
|
50
|
+
meta: {},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function getRegisteredHooks() {
|
|
54
|
+
return _hooks;
|
|
55
|
+
}
|
|
56
|
+
function clearRegisteredHooks() {
|
|
57
|
+
_hooks.length = 0;
|
|
58
|
+
}
|
|
59
|
+
async function applyPendingUpdates(bundlesRoot, currentVersion) {
|
|
60
|
+
(0, runtime_1.emitNervesEvent)({
|
|
61
|
+
component: "daemon",
|
|
62
|
+
event: "daemon.apply_pending_updates_start",
|
|
63
|
+
message: "applying pending updates",
|
|
64
|
+
meta: { bundlesRoot, currentVersion },
|
|
65
|
+
});
|
|
66
|
+
if (!fs.existsSync(bundlesRoot)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
let entries;
|
|
70
|
+
try {
|
|
71
|
+
entries = fs.readdirSync(bundlesRoot, { withFileTypes: true });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
|
|
78
|
+
continue;
|
|
79
|
+
const agentRoot = path.join(bundlesRoot, entry.name);
|
|
80
|
+
let previousVersion;
|
|
81
|
+
const metaPath = path.join(agentRoot, "bundle-meta.json");
|
|
82
|
+
try {
|
|
83
|
+
if (fs.existsSync(metaPath)) {
|
|
84
|
+
const raw = fs.readFileSync(metaPath, "utf-8");
|
|
85
|
+
const meta = JSON.parse(raw);
|
|
86
|
+
previousVersion = meta.runtimeVersion;
|
|
87
|
+
if (previousVersion === currentVersion) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Malformed or unreadable bundle-meta.json -- treat as needing update
|
|
94
|
+
previousVersion = undefined;
|
|
95
|
+
}
|
|
96
|
+
const ctx = { agentRoot, currentVersion, previousVersion };
|
|
97
|
+
for (const hook of _hooks) {
|
|
98
|
+
try {
|
|
99
|
+
await hook(ctx);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
(0, runtime_1.emitNervesEvent)({
|
|
103
|
+
component: "daemon",
|
|
104
|
+
event: "daemon.update_hook_error",
|
|
105
|
+
message: "update hook threw",
|
|
106
|
+
meta: {
|
|
107
|
+
agentRoot,
|
|
108
|
+
error: err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err),
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
(0, runtime_1.emitNervesEvent)({
|
|
115
|
+
component: "daemon",
|
|
116
|
+
event: "daemon.apply_pending_updates_end",
|
|
117
|
+
message: "pending updates applied",
|
|
118
|
+
meta: { bundlesRoot },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assessWrapperPublishSync = assessWrapperPublishSync;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
function wrapperPackageChanged(changedFiles) {
|
|
6
|
+
return changedFiles.some((file) => file.startsWith("packages/ouro.bot/"));
|
|
7
|
+
}
|
|
8
|
+
function assessWrapperPublishSync(input) {
|
|
9
|
+
let result;
|
|
10
|
+
if (!wrapperPackageChanged(input.changedFiles)) {
|
|
11
|
+
result = {
|
|
12
|
+
ok: true,
|
|
13
|
+
message: "wrapper package unchanged",
|
|
14
|
+
};
|
|
15
|
+
(0, runtime_1.emitNervesEvent)({
|
|
16
|
+
component: "daemon",
|
|
17
|
+
event: "daemon.wrapper_publish_guard_checked",
|
|
18
|
+
message: "evaluated wrapper publish sync",
|
|
19
|
+
meta: { changed: false, localVersion: input.localVersion, publishedVersion: input.publishedVersion, ok: result.ok },
|
|
20
|
+
});
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
if (input.publishedVersion === input.localVersion) {
|
|
24
|
+
result = {
|
|
25
|
+
ok: false,
|
|
26
|
+
message: `ouro.bot wrapper changed but ouro.bot@${input.localVersion} is already published; bump packages/ouro.bot/package.json before merging`,
|
|
27
|
+
};
|
|
28
|
+
(0, runtime_1.emitNervesEvent)({
|
|
29
|
+
level: "warn",
|
|
30
|
+
component: "daemon",
|
|
31
|
+
event: "daemon.wrapper_publish_guard_checked",
|
|
32
|
+
message: "evaluated wrapper publish sync",
|
|
33
|
+
meta: { changed: true, localVersion: input.localVersion, publishedVersion: input.publishedVersion, ok: result.ok },
|
|
34
|
+
});
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
result = {
|
|
38
|
+
ok: true,
|
|
39
|
+
message: "wrapper package changed and local wrapper version is unpublished",
|
|
40
|
+
};
|
|
41
|
+
(0, runtime_1.emitNervesEvent)({
|
|
42
|
+
component: "daemon",
|
|
43
|
+
event: "daemon.wrapper_publish_guard_checked",
|
|
44
|
+
message: "evaluated wrapper publish sync",
|
|
45
|
+
meta: { changed: true, localVersion: input.localVersion, publishedVersion: input.publishedVersion, ok: result.ok },
|
|
46
|
+
});
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.CANONICAL_BUNDLE_MANIFEST = void 0;
|
|
37
|
+
exports.getChangelogPath = getChangelogPath;
|
|
37
38
|
exports.getPackageVersion = getPackageVersion;
|
|
38
39
|
exports.createBundleMeta = createBundleMeta;
|
|
39
40
|
exports.backfillBundleMeta = backfillBundleMeta;
|
|
@@ -58,6 +59,16 @@ exports.CANONICAL_BUNDLE_MANIFEST = [
|
|
|
58
59
|
{ path: "senses", kind: "dir" },
|
|
59
60
|
{ path: "senses/teams", kind: "dir" },
|
|
60
61
|
];
|
|
62
|
+
function getChangelogPath() {
|
|
63
|
+
const changelogPath = path.resolve(__dirname, "../../changelog.json");
|
|
64
|
+
(0, runtime_1.emitNervesEvent)({
|
|
65
|
+
component: "mind",
|
|
66
|
+
event: "mind.changelog_path_resolved",
|
|
67
|
+
message: "resolved changelog path",
|
|
68
|
+
meta: { path: changelogPath },
|
|
69
|
+
});
|
|
70
|
+
return changelogPath;
|
|
71
|
+
}
|
|
61
72
|
function getPackageVersion() {
|
|
62
73
|
const packageJsonPath = path.resolve(__dirname, "../../package.json");
|
|
63
74
|
const raw = fs.readFileSync(packageJsonPath, "utf-8");
|
package/dist/mind/prompt.js
CHANGED
|
@@ -190,11 +190,28 @@ function aspirationsSection() {
|
|
|
190
190
|
return "";
|
|
191
191
|
return `## my aspirations\n${text}`;
|
|
192
192
|
}
|
|
193
|
+
function readBundleMeta() {
|
|
194
|
+
try {
|
|
195
|
+
const metaPath = path.join((0, identity_1.getAgentRoot)(), "bundle-meta.json");
|
|
196
|
+
const raw = fs.readFileSync(metaPath, "utf-8");
|
|
197
|
+
return JSON.parse(raw);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
193
203
|
function runtimeInfoSection(channel) {
|
|
194
204
|
const lines = [];
|
|
195
205
|
const agentName = (0, identity_1.getAgentName)();
|
|
206
|
+
const currentVersion = (0, bundle_manifest_1.getPackageVersion)();
|
|
196
207
|
lines.push(`## runtime`);
|
|
197
208
|
lines.push(`agent: ${agentName}`);
|
|
209
|
+
lines.push(`runtime version: ${currentVersion}`);
|
|
210
|
+
const bundleMeta = readBundleMeta();
|
|
211
|
+
if (bundleMeta?.previousRuntimeVersion && bundleMeta.previousRuntimeVersion !== currentVersion) {
|
|
212
|
+
lines.push(`previously: ${bundleMeta.previousRuntimeVersion}`);
|
|
213
|
+
}
|
|
214
|
+
lines.push(`changelog available at: ${(0, bundle_manifest_1.getChangelogPath)()}`);
|
|
198
215
|
lines.push(`cwd: ${process.cwd()}`);
|
|
199
216
|
lines.push(`channel: ${channel}`);
|
|
200
217
|
lines.push(`current sense: ${channel}`);
|
package/dist/senses/cli.js
CHANGED
|
@@ -62,6 +62,9 @@ const cli_logging_1 = require("../nerves/cli-logging");
|
|
|
62
62
|
const runtime_1 = require("../nerves/runtime");
|
|
63
63
|
const trust_gate_1 = require("./trust-gate");
|
|
64
64
|
const session_lock_1 = require("./session-lock");
|
|
65
|
+
const update_hooks_1 = require("../heart/daemon/update-hooks");
|
|
66
|
+
const bundle_meta_1 = require("../heart/daemon/hooks/bundle-meta");
|
|
67
|
+
const bundle_manifest_1 = require("../mind/bundle-manifest");
|
|
65
68
|
// spinner that only touches stderr, cleans up after itself
|
|
66
69
|
// exported for direct testability (stop-without-start branch)
|
|
67
70
|
class Spinner {
|
|
@@ -662,6 +665,9 @@ async function main(agentName, options) {
|
|
|
662
665
|
if (agentName)
|
|
663
666
|
(0, identity_1.setAgentName)(agentName);
|
|
664
667
|
const pasteDebounceMs = options?.pasteDebounceMs ?? 50;
|
|
668
|
+
// Fallback: apply pending updates for daemon-less direct CLI usage
|
|
669
|
+
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
670
|
+
await (0, update_hooks_1.applyPendingUpdates)((0, identity_1.getAgentBundlesRoot)(), (0, bundle_manifest_1.getPackageVersion)());
|
|
665
671
|
// Fail fast if provider is misconfigured (triggers human-readable error + exit)
|
|
666
672
|
(0, core_1.getProvider)();
|
|
667
673
|
// Resolve context kernel (identity + channel) for CLI
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.22",
|
|
4
4
|
"main": "dist/heart/daemon/ouro-entry.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cli": "dist/heart/daemon/ouro-bot-entry.js",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"dist/",
|
|
12
12
|
"AdoptionSpecialist.ouro/",
|
|
13
13
|
"subagents/",
|
|
14
|
-
"assets/"
|
|
14
|
+
"assets/",
|
|
15
|
+
"changelog.json"
|
|
15
16
|
],
|
|
16
17
|
"exports": {
|
|
17
18
|
".": "./dist/heart/daemon/daemon-cli.js",
|
|
@@ -34,13 +35,15 @@
|
|
|
34
35
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
35
36
|
"@microsoft/teams.apps": "^2.0.5",
|
|
36
37
|
"@microsoft/teams.dev": "^2.0.5",
|
|
37
|
-
"openai": "^6.27.0"
|
|
38
|
+
"openai": "^6.27.0",
|
|
39
|
+
"semver": "^7.7.4"
|
|
38
40
|
},
|
|
39
41
|
"repository": {
|
|
40
42
|
"type": "git",
|
|
41
43
|
"url": "https://github.com/ouroborosbot/ouroboros"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
46
|
+
"@types/semver": "^7.7.1",
|
|
44
47
|
"@vitest/coverage-v8": "^4.0.18",
|
|
45
48
|
"eslint": "^10.0.2",
|
|
46
49
|
"typescript": "^5.7.0",
|