@ouro.bot/cli 0.1.0-alpha.21 → 0.1.0-alpha.23
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 +32 -0
- package/dist/heart/daemon/daemon-cli.js +2 -2
- 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/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/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,32 @@
|
|
|
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.23",
|
|
6
|
+
"changes": [
|
|
7
|
+
"You can now use 'ouro down' as an alias for 'ouro stop' to pair naturally with 'ouro up'."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.22",
|
|
12
|
+
"changes": [
|
|
13
|
+
"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.",
|
|
14
|
+
"A changelog file is now shipped with the runtime. You can read it to understand exactly what changed between versions.",
|
|
15
|
+
"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.",
|
|
16
|
+
"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.",
|
|
17
|
+
"The daemon can now be managed as a macOS LaunchAgent for automatic restart on crash.",
|
|
18
|
+
"The daemon now auto-syncs the ouro.bot npm wrapper version on startup to keep the npx entry point current."
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"version": "0.1.0-alpha.21",
|
|
23
|
+
"changes": [
|
|
24
|
+
"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.",
|
|
25
|
+
"A changelog file is now shipped with the runtime. You can read it to understand exactly what changed between versions.",
|
|
26
|
+
"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.",
|
|
27
|
+
"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.",
|
|
28
|
+
"The daemon can now be managed as a macOS LaunchAgent for automatic restart on crash."
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -223,7 +223,7 @@ function usage() {
|
|
|
223
223
|
return [
|
|
224
224
|
"Usage:",
|
|
225
225
|
" ouro [up]",
|
|
226
|
-
" ouro stop|status|logs|hatch",
|
|
226
|
+
" ouro stop|down|status|logs|hatch",
|
|
227
227
|
" ouro -v|--version",
|
|
228
228
|
" ouro chat <agent>",
|
|
229
229
|
" ouro msg --to <agent> [--session <id>] [--task <ref>] <message>",
|
|
@@ -433,7 +433,7 @@ function parseOuroCommand(args) {
|
|
|
433
433
|
return { kind: "daemon.up" };
|
|
434
434
|
if (head === "up")
|
|
435
435
|
return { kind: "daemon.up" };
|
|
436
|
-
if (head === "stop")
|
|
436
|
+
if (head === "stop" || head === "down")
|
|
437
437
|
return { kind: "daemon.stop" };
|
|
438
438
|
if (head === "status")
|
|
439
439
|
return { kind: "daemon.status" };
|
|
@@ -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
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -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.23",
|
|
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",
|