@ouro.bot/cli 0.1.0-alpha.9 → 0.1.0-alpha.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +147 -205
- package/assets/ouroboros.png +0 -0
- package/changelog.json +529 -0
- package/dist/heart/active-work.js +251 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/commitments.js +109 -0
- package/dist/heart/config.js +68 -23
- package/dist/heart/core.js +452 -93
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/auth-flow.js +430 -0
- package/dist/heart/daemon/daemon-cli.js +1737 -269
- package/dist/heart/daemon/daemon-entry.js +55 -6
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +216 -10
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +7 -82
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +159 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/ouro-bot-entry.js +0 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-entry.js +0 -0
- package/dist/heart/daemon/ouro-path-installer.js +260 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/process-manager.js +14 -1
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/sense-manager.js +307 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +53 -84
- package/dist/heart/daemon/specialist-prompt.js +63 -11
- package/dist/heart/daemon/specialist-tools.js +211 -60
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +126 -21
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +191 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/providers/anthropic.js +74 -9
- package/dist/heart/providers/azure.js +86 -7
- package/dist/heart/providers/github-copilot.js +149 -0
- package/dist/heart/providers/minimax.js +4 -0
- package/dist/heart/providers/openai-codex.js +12 -3
- package/dist/heart/safe-workspace.js +362 -0
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/heart/session-recall.js +116 -0
- package/dist/heart/streaming.js +100 -22
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +14 -2
- package/dist/mind/bundle-manifest.js +70 -0
- package/dist/mind/context.js +57 -11
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +35 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/memory.js +27 -26
- package/dist/mind/obligation-steering.js +31 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +467 -77
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/index.js +12 -0
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +180 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +69 -4
- package/dist/repertoire/coding/spawner.js +21 -3
- package/dist/repertoire/coding/tools.js +105 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +290 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +195 -0
- package/dist/repertoire/skills.js +3 -26
- package/dist/repertoire/tasks/board.js +12 -0
- package/dist/repertoire/tasks/index.js +23 -9
- package/dist/repertoire/tasks/transitions.js +1 -2
- package/dist/repertoire/tools-base.js +714 -249
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +106 -53
- package/dist/senses/bluebubbles-client.js +210 -5
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +894 -45
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +400 -164
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +154 -0
- package/dist/senses/inner-dialog-worker.js +47 -18
- package/dist/senses/inner-dialog.js +377 -83
- package/dist/senses/pipeline.js +307 -0
- package/dist/senses/teams.js +573 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +14 -3
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/specialist-session.js +0 -142
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
|
@@ -34,99 +34,250 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getSpecialistTools = getSpecialistTools;
|
|
37
|
-
exports.
|
|
37
|
+
exports.createSpecialistExecTool = createSpecialistExecTool;
|
|
38
|
+
const crypto = __importStar(require("crypto"));
|
|
38
39
|
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
39
41
|
const tools_base_1 = require("../../repertoire/tools-base");
|
|
40
42
|
const hatch_flow_1 = require("./hatch-flow");
|
|
41
43
|
const hatch_animation_1 = require("./hatch-animation");
|
|
44
|
+
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
42
45
|
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
-
const
|
|
46
|
+
const completeAdoptionTool = {
|
|
44
47
|
type: "function",
|
|
45
48
|
function: {
|
|
46
|
-
name: "
|
|
47
|
-
description: "
|
|
49
|
+
name: "complete_adoption",
|
|
50
|
+
description: "finalize the agent bundle and hatch the new agent. call this only when you have written all 5 psyche files and agent.json to the temp directory, and the human has approved the bundle.",
|
|
48
51
|
parameters: {
|
|
49
52
|
type: "object",
|
|
50
53
|
properties: {
|
|
51
54
|
name: {
|
|
52
55
|
type: "string",
|
|
53
|
-
description: "the name for the new agent (
|
|
56
|
+
description: "the PascalCase name for the new agent (e.g. 'Slugger')",
|
|
54
57
|
},
|
|
55
|
-
|
|
58
|
+
handoff_message: {
|
|
56
59
|
type: "string",
|
|
57
|
-
description: "
|
|
60
|
+
description: "a warm handoff message to display to the human after the agent is hatched",
|
|
61
|
+
},
|
|
62
|
+
phone: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "the human's phone number (optional, for iMessage contact recognition)",
|
|
65
|
+
},
|
|
66
|
+
teams_handle: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "the human's Teams email/handle (optional, for Teams contact recognition)",
|
|
58
69
|
},
|
|
59
70
|
},
|
|
60
|
-
required: ["name", "
|
|
71
|
+
required: ["name", "handoff_message"],
|
|
61
72
|
},
|
|
62
73
|
},
|
|
63
74
|
};
|
|
64
75
|
const readFileTool = tools_base_1.baseToolDefinitions.find((d) => d.tool.function.name === "read_file");
|
|
65
|
-
const
|
|
76
|
+
const writeFileTool = tools_base_1.baseToolDefinitions.find((d) => d.tool.function.name === "write_file");
|
|
77
|
+
const listDirToolSchema = {
|
|
78
|
+
type: "function",
|
|
79
|
+
function: {
|
|
80
|
+
name: "list_directory",
|
|
81
|
+
description: "list directory contents",
|
|
82
|
+
parameters: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: { path: { type: "string" } },
|
|
85
|
+
required: ["path"],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
66
89
|
/**
|
|
67
90
|
* Returns the specialist's tool schema array.
|
|
68
91
|
*/
|
|
69
92
|
function getSpecialistTools() {
|
|
70
|
-
return [
|
|
93
|
+
return [completeAdoptionTool, tools_base_1.finalAnswerTool, readFileTool.tool, writeFileTool.tool, listDirToolSchema];
|
|
94
|
+
}
|
|
95
|
+
const PSYCHE_FILES = ["SOUL.md", "IDENTITY.md", "LORE.md", "TACIT.md", "ASPIRATIONS.md"];
|
|
96
|
+
function isPascalCase(name) {
|
|
97
|
+
return /^[A-Z][a-zA-Z0-9]*$/.test(name);
|
|
98
|
+
}
|
|
99
|
+
function writeReadme(dir, purpose) {
|
|
100
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
101
|
+
const readmePath = path.join(dir, "README.md");
|
|
102
|
+
/* v8 ignore next -- defensive: guard against re-scaffold on existing bundle @preserve */
|
|
103
|
+
if (!fs.existsSync(readmePath)) {
|
|
104
|
+
fs.writeFileSync(readmePath, `# ${path.basename(dir)}\n\n${purpose}\n`, "utf-8");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function scaffoldBundle(bundleRoot) {
|
|
108
|
+
writeReadme(path.join(bundleRoot, "memory"), "Persistent memory store.");
|
|
109
|
+
writeReadme(path.join(bundleRoot, "memory", "daily"), "Daily memory entries.");
|
|
110
|
+
writeReadme(path.join(bundleRoot, "memory", "archive"), "Archived memory.");
|
|
111
|
+
writeReadme(path.join(bundleRoot, "friends"), "Known friend records.");
|
|
112
|
+
writeReadme(path.join(bundleRoot, "tasks"), "Task files.");
|
|
113
|
+
writeReadme(path.join(bundleRoot, "tasks", "habits"), "Recurring tasks.");
|
|
114
|
+
writeReadme(path.join(bundleRoot, "tasks", "one-shots"), "One-shot tasks.");
|
|
115
|
+
writeReadme(path.join(bundleRoot, "tasks", "ongoing"), "Ongoing tasks.");
|
|
116
|
+
writeReadme(path.join(bundleRoot, "skills"), "Local skill files.");
|
|
117
|
+
writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
|
|
118
|
+
writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
|
|
119
|
+
// Memory scaffold files
|
|
120
|
+
const memoryRoot = path.join(bundleRoot, "memory");
|
|
121
|
+
const factsPath = path.join(memoryRoot, "facts.jsonl");
|
|
122
|
+
const entitiesPath = path.join(memoryRoot, "entities.json");
|
|
123
|
+
/* v8 ignore next -- defensive: guard against re-scaffold on existing bundle @preserve */
|
|
124
|
+
if (!fs.existsSync(factsPath))
|
|
125
|
+
fs.writeFileSync(factsPath, "", "utf-8");
|
|
126
|
+
/* v8 ignore next -- defensive: guard against re-scaffold on existing bundle @preserve */
|
|
127
|
+
if (!fs.existsSync(entitiesPath))
|
|
128
|
+
fs.writeFileSync(entitiesPath, "{}\n", "utf-8");
|
|
129
|
+
// bundle-meta.json
|
|
130
|
+
const meta = (0, bundle_manifest_1.createBundleMeta)();
|
|
131
|
+
fs.writeFileSync(path.join(bundleRoot, "bundle-meta.json"), JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
132
|
+
}
|
|
133
|
+
function moveDir(src, dest) {
|
|
134
|
+
try {
|
|
135
|
+
fs.renameSync(src, dest);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
/* v8 ignore start -- cross-device fallback: only triggers on EXDEV (e.g. /tmp → different mount), untestable in CI @preserve */
|
|
139
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
140
|
+
fs.rmSync(src, { recursive: true, force: true });
|
|
141
|
+
/* v8 ignore stop */
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function execCompleteAdoption(args, deps) {
|
|
145
|
+
const name = args.name;
|
|
146
|
+
const handoffMessage = args.handoff_message;
|
|
147
|
+
if (!name) {
|
|
148
|
+
return "error: missing required 'name' parameter";
|
|
149
|
+
}
|
|
150
|
+
if (!isPascalCase(name)) {
|
|
151
|
+
return `error: name '${name}' must be PascalCase (e.g. 'Slugger', 'MyAgent')`;
|
|
152
|
+
}
|
|
153
|
+
// Validate psyche files exist
|
|
154
|
+
const psycheDir = path.join(deps.tempDir, "psyche");
|
|
155
|
+
const missingPsyche = PSYCHE_FILES.filter((f) => !fs.existsSync(path.join(psycheDir, f)));
|
|
156
|
+
if (missingPsyche.length > 0) {
|
|
157
|
+
return `error: missing psyche files in temp directory: ${missingPsyche.join(", ")}. write them first using write_file.`;
|
|
158
|
+
}
|
|
159
|
+
// Validate agent.json exists
|
|
160
|
+
const agentJsonPath = path.join(deps.tempDir, "agent.json");
|
|
161
|
+
if (!fs.existsSync(agentJsonPath)) {
|
|
162
|
+
return "error: agent.json not found in temp directory. write it first using write_file.";
|
|
163
|
+
}
|
|
164
|
+
// Validate target doesn't exist
|
|
165
|
+
const targetBundle = path.join(deps.bundlesRoot, `${name}.ouro`);
|
|
166
|
+
if (fs.existsSync(targetBundle)) {
|
|
167
|
+
return `error: bundle '${name}.ouro' already exists at ${deps.bundlesRoot}. choose a different name.`;
|
|
168
|
+
}
|
|
169
|
+
// Scaffold structural dirs into tempDir
|
|
170
|
+
scaffoldBundle(deps.tempDir);
|
|
171
|
+
// Move tempDir -> final bundle location
|
|
172
|
+
moveDir(deps.tempDir, targetBundle);
|
|
173
|
+
// Write secrets
|
|
174
|
+
try {
|
|
175
|
+
(0, hatch_flow_1.writeSecretsFile)(name, deps.provider, deps.credentials, deps.secretsRoot);
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
// Rollback: remove the moved bundle
|
|
179
|
+
try {
|
|
180
|
+
fs.rmSync(targetBundle, { recursive: true, force: true });
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// Best effort cleanup
|
|
184
|
+
}
|
|
185
|
+
return `error: failed to write secrets: ${e instanceof Error ? e.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(e)}`;
|
|
186
|
+
}
|
|
187
|
+
// Create initial friend record if contact info provided
|
|
188
|
+
const phone = args.phone;
|
|
189
|
+
const teamsHandle = args.teams_handle;
|
|
190
|
+
if (phone || teamsHandle) {
|
|
191
|
+
const friendId = crypto.randomUUID();
|
|
192
|
+
const now = new Date().toISOString();
|
|
193
|
+
const externalIds = [];
|
|
194
|
+
if (phone)
|
|
195
|
+
externalIds.push({ provider: "imessage-handle", externalId: phone, linkedAt: now });
|
|
196
|
+
if (teamsHandle)
|
|
197
|
+
externalIds.push({ provider: "aad", externalId: teamsHandle, linkedAt: now });
|
|
198
|
+
const friendRecord = {
|
|
199
|
+
id: friendId,
|
|
200
|
+
name: deps.humanName ?? "primary",
|
|
201
|
+
trustLevel: "family",
|
|
202
|
+
externalIds,
|
|
203
|
+
tenantMemberships: [],
|
|
204
|
+
toolPreferences: {},
|
|
205
|
+
notes: {},
|
|
206
|
+
createdAt: now,
|
|
207
|
+
updatedAt: now,
|
|
208
|
+
schemaVersion: 1,
|
|
209
|
+
};
|
|
210
|
+
const friendPath = path.join(targetBundle, "friends", `${friendId}.json`);
|
|
211
|
+
fs.writeFileSync(friendPath, JSON.stringify(friendRecord, null, 2), "utf-8");
|
|
212
|
+
}
|
|
213
|
+
// Play hatch animation
|
|
214
|
+
await (0, hatch_animation_1.playHatchAnimation)(name, deps.animationWriter);
|
|
215
|
+
// Display handoff message
|
|
216
|
+
/* v8 ignore next -- UI-only: handoff message display, covered by integration @preserve */
|
|
217
|
+
if (handoffMessage && deps.animationWriter) {
|
|
218
|
+
deps.animationWriter(`\n${handoffMessage}\n`);
|
|
219
|
+
}
|
|
220
|
+
(0, runtime_1.emitNervesEvent)({
|
|
221
|
+
component: "daemon",
|
|
222
|
+
event: "daemon.adoption_complete",
|
|
223
|
+
message: "adoption completed successfully",
|
|
224
|
+
meta: { agentName: name, bundlePath: targetBundle },
|
|
225
|
+
});
|
|
226
|
+
return JSON.stringify({ success: true, agentName: name, bundlePath: targetBundle });
|
|
71
227
|
}
|
|
72
228
|
/**
|
|
73
|
-
*
|
|
74
|
-
* Returns the tool result string.
|
|
229
|
+
* Create a specialist tool executor with the given dependencies captured in closure.
|
|
75
230
|
*/
|
|
76
|
-
|
|
231
|
+
function createSpecialistExecTool(deps) {
|
|
77
232
|
(0, runtime_1.emitNervesEvent)({
|
|
78
233
|
component: "daemon",
|
|
79
|
-
event: "daemon.
|
|
80
|
-
message: "
|
|
81
|
-
meta: {
|
|
234
|
+
event: "daemon.specialist_exec_tool_created",
|
|
235
|
+
message: "specialist exec tool created",
|
|
236
|
+
meta: { tempDir: deps.tempDir },
|
|
82
237
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
agentName,
|
|
90
|
-
humanName: args.humanName || deps.humanName,
|
|
91
|
-
provider: deps.provider,
|
|
92
|
-
credentials: deps.credentials,
|
|
93
|
-
};
|
|
94
|
-
// Pass identity dirs to prevent hatch flow from syncing to ~/AgentBundles/AdoptionSpecialist.ouro/
|
|
95
|
-
// or cwd/AdoptionSpecialist.ouro/. The specialist already picked its identity; the hatch flow
|
|
96
|
-
// just needs a valid source dir to pick from for the hatchling's LORE.md seed.
|
|
97
|
-
const identitiesDir = deps.specialistIdentitiesDir;
|
|
98
|
-
const result = await (0, hatch_flow_1.runHatchFlow)(input, {
|
|
99
|
-
bundlesRoot: deps.bundlesRoot,
|
|
100
|
-
secretsRoot: deps.secretsRoot,
|
|
101
|
-
...(identitiesDir ? { specialistIdentitySourceDir: identitiesDir, specialistIdentityTargetDir: identitiesDir } : {}),
|
|
238
|
+
return async (name, args) => {
|
|
239
|
+
(0, runtime_1.emitNervesEvent)({
|
|
240
|
+
component: "daemon",
|
|
241
|
+
event: "daemon.specialist_tool_exec",
|
|
242
|
+
message: "executing specialist tool",
|
|
243
|
+
meta: { tool: name },
|
|
102
244
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
`hatched ${agentName} successfully.`,
|
|
106
|
-
`bundle path: ${result.bundleRoot}`,
|
|
107
|
-
`identity seed: ${result.selectedIdentity}`,
|
|
108
|
-
`specialist secrets: ${result.specialistSecretsPath}`,
|
|
109
|
-
`hatchling secrets: ${result.hatchlingSecretsPath}`,
|
|
110
|
-
].join("\n");
|
|
111
|
-
}
|
|
112
|
-
if (name === "read_file") {
|
|
113
|
-
try {
|
|
114
|
-
return fs.readFileSync(args.path, "utf-8");
|
|
245
|
+
if (name === "complete_adoption") {
|
|
246
|
+
return execCompleteAdoption(args, deps);
|
|
115
247
|
}
|
|
116
|
-
|
|
117
|
-
|
|
248
|
+
if (name === "read_file") {
|
|
249
|
+
try {
|
|
250
|
+
return fs.readFileSync(args.path, "utf-8");
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
return `error: ${e instanceof Error ? e.message : /* v8 ignore next -- defensive @preserve */ String(e)}`;
|
|
254
|
+
}
|
|
118
255
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
256
|
+
if (name === "write_file") {
|
|
257
|
+
try {
|
|
258
|
+
const dir = path.dirname(args.path);
|
|
259
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
260
|
+
const content = typeof args.content === "string"
|
|
261
|
+
? args.content
|
|
262
|
+
: JSON.stringify(args.content, null, 2);
|
|
263
|
+
fs.writeFileSync(args.path, content, "utf-8");
|
|
264
|
+
return `wrote ${args.path}`;
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
return `error: ${e instanceof Error ? e.message : /* v8 ignore next -- defensive @preserve */ String(e)}`;
|
|
268
|
+
}
|
|
126
269
|
}
|
|
127
|
-
|
|
128
|
-
|
|
270
|
+
if (name === "list_directory") {
|
|
271
|
+
try {
|
|
272
|
+
return fs
|
|
273
|
+
.readdirSync(args.path, { withFileTypes: true })
|
|
274
|
+
.map((e) => `${e.isDirectory() ? "d" : "-"} ${e.name}`)
|
|
275
|
+
.join("\n");
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
return `error: ${e instanceof Error ? e.message : /* v8 ignore next -- defensive @preserve */ String(e)}`;
|
|
279
|
+
}
|
|
129
280
|
}
|
|
130
|
-
|
|
131
|
-
|
|
281
|
+
return `error: unknown tool '${name}'`;
|
|
282
|
+
};
|
|
132
283
|
}
|
|
@@ -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
|
+
}
|