@ouro.bot/cli 0.1.0-alpha.24 → 0.1.0-alpha.241
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/README.md +187 -191
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
- package/changelog.json +1459 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +119 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +120 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/presence.js +185 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +37 -2
- package/dist/heart/auth/auth-flow.js +447 -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 +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +63 -30
- package/dist/heart/core.js +620 -159
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +149 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +557 -0
- package/dist/heart/daemon/cli-exec.js +1847 -0
- package/dist/heart/daemon/cli-parse.js +759 -0
- package/dist/heart/daemon/cli-render.js +399 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +29 -1177
- package/dist/heart/daemon/daemon-entry.js +202 -4
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
- package/dist/heart/daemon/daemon-tombstone.js +123 -0
- package/dist/heart/daemon/daemon.js +492 -20
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +39 -8
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +1 -1
- package/dist/heart/daemon/process-manager.js +123 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +101 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +72 -3
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +181 -0
- package/dist/heart/habits/habit-parser.js +119 -0
- package/dist/heart/habits/habit-scheduler.js +369 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +30 -120
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
- package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
- package/dist/heart/identity.js +157 -55
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/mcp/mcp-server.js +651 -0
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/outlook/outlook-http.js +439 -0
- package/dist/heart/outlook/outlook-read.js +1618 -0
- package/dist/heart/outlook/outlook-render.js +1032 -0
- package/dist/heart/outlook/outlook-types.js +7 -0
- package/dist/heart/outlook/outlook-view.js +194 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +162 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +187 -41
- package/dist/heart/providers/azure.js +116 -10
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +51 -16
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/start-of-turn-packet.js +330 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/sync.js +217 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +358 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
- package/dist/heart/versioning/ouro-version-manager.js +171 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +30 -6
- package/dist/heart/{daemon → versioning}/update-checker.js +9 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +11 -0
- package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +41 -3
- package/dist/mind/associative-recall.js +126 -66
- package/dist/mind/bundle-manifest.js +8 -1
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +73 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +37 -0
- package/dist/mind/friends/store-file.js +58 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +762 -109
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/coverage/audit.js +1 -1
- package/dist/nerves/coverage/file-completeness.js +50 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +64 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
- package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +319 -0
- package/dist/repertoire/bw-installer.js +79 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +149 -2
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +515 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +369 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +303 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +11 -28
- package/dist/repertoire/tools-base.js +43 -771
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +182 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +62 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-memory.js +347 -0
- package/dist/repertoire/tools-session.js +739 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +12 -62
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +110 -0
- package/dist/repertoire/tools.js +147 -130
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +241 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +97 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +109 -10
- package/dist/senses/bluebubbles/entry.js +13 -0
- package/dist/senses/bluebubbles/inbound-log.js +109 -0
- package/dist/senses/bluebubbles/index.js +1374 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +119 -24
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +13 -5
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/bluebubbles/session-cleanup.js +72 -0
- package/dist/senses/cli/adapter.js +274 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +358 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +198 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +510 -246
- package/dist/senses/commands.js +65 -1
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +593 -85
- package/dist/senses/pipeline.js +559 -0
- package/dist/senses/shared-turn.js +199 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams.js +664 -165
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +14 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +110 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +81 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -421
- package/dist/senses/debug-activity.js +0 -107
- package/subagents/README.md +0 -73
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
|
@@ -37,6 +37,115 @@ exports.bundleMetaHook = bundleMetaHook;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const runtime_1 = require("../../../nerves/runtime");
|
|
40
|
+
/**
|
|
41
|
+
* Migrate bundle from schema 1 to schema 2:
|
|
42
|
+
* - Move state/{episodes,obligations,cares,intentions}/* to arc/{name}/*
|
|
43
|
+
* - Move psyche/memory/* to diary/
|
|
44
|
+
* Idempotent: skips missing sources; on collision, newer mtime wins.
|
|
45
|
+
*/
|
|
46
|
+
function migrateToSchema2(agentRoot) {
|
|
47
|
+
(0, runtime_1.emitNervesEvent)({
|
|
48
|
+
component: "daemon",
|
|
49
|
+
event: "daemon.bundle_migration_start",
|
|
50
|
+
message: "migrating bundle to schema 2",
|
|
51
|
+
meta: { agentRoot },
|
|
52
|
+
});
|
|
53
|
+
// Migrate arc entities
|
|
54
|
+
for (const name of ["episodes", "obligations", "cares", "intentions"]) {
|
|
55
|
+
const src = path.join(agentRoot, "state", name);
|
|
56
|
+
const dest = path.join(agentRoot, "arc", name);
|
|
57
|
+
migrateDirectory(src, dest);
|
|
58
|
+
}
|
|
59
|
+
// Migrate diary
|
|
60
|
+
const memorySrc = path.join(agentRoot, "psyche", "memory");
|
|
61
|
+
const diaryDest = path.join(agentRoot, "diary");
|
|
62
|
+
migrateDirectory(memorySrc, diaryDest);
|
|
63
|
+
// Update bundle .gitignore
|
|
64
|
+
updateBundleGitignore(agentRoot);
|
|
65
|
+
(0, runtime_1.emitNervesEvent)({
|
|
66
|
+
component: "daemon",
|
|
67
|
+
event: "daemon.bundle_migration_end",
|
|
68
|
+
message: "bundle migration to schema 2 complete",
|
|
69
|
+
meta: { agentRoot },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Ensure bundle .gitignore has state/ ignored and does NOT ignore arc/, diary/, journal/.
|
|
74
|
+
*/
|
|
75
|
+
function updateBundleGitignore(agentRoot) {
|
|
76
|
+
const gitignorePath = path.join(agentRoot, ".gitignore");
|
|
77
|
+
let lines = [];
|
|
78
|
+
try {
|
|
79
|
+
if (fs.existsSync(gitignorePath)) {
|
|
80
|
+
lines = fs.readFileSync(gitignorePath, "utf-8").split("\n");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// If we can't read, start fresh
|
|
85
|
+
}
|
|
86
|
+
// Remove arc/, diary/, journal/ from ignore (they should be tracked)
|
|
87
|
+
const toRemove = new Set(["arc/", "diary/", "journal/"]);
|
|
88
|
+
lines = lines.filter((line) => !toRemove.has(line.trim()));
|
|
89
|
+
// Ensure state/ is in the ignore list
|
|
90
|
+
const hasState = lines.some((line) => line.trim() === "state/");
|
|
91
|
+
if (!hasState) {
|
|
92
|
+
lines.push("state/");
|
|
93
|
+
}
|
|
94
|
+
// Write back, trimming trailing empty lines and ensuring trailing newline
|
|
95
|
+
const content = lines.join("\n").replace(/\n+$/, "") + "\n";
|
|
96
|
+
try {
|
|
97
|
+
fs.writeFileSync(gitignorePath, content, "utf-8");
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Non-blocking: if we can't write .gitignore, migration still succeeds
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Recursively copy files from src to dest.
|
|
105
|
+
* Creates destination directories as needed. Skips if source doesn't exist.
|
|
106
|
+
* When both source and destination exist, compares mtimes: newer file wins.
|
|
107
|
+
* Logs a warning either way when a collision is detected.
|
|
108
|
+
*/
|
|
109
|
+
function migrateDirectory(src, dest) {
|
|
110
|
+
if (!fs.existsSync(src))
|
|
111
|
+
return;
|
|
112
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
113
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
const srcPath = path.join(src, entry.name);
|
|
116
|
+
const destPath = path.join(dest, entry.name);
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
migrateDirectory(srcPath, destPath);
|
|
119
|
+
}
|
|
120
|
+
else if (!fs.existsSync(destPath)) {
|
|
121
|
+
fs.copyFileSync(srcPath, destPath);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// Collision: both source and destination exist — compare mtimes
|
|
125
|
+
const srcMtime = fs.statSync(srcPath).mtimeMs;
|
|
126
|
+
const destMtime = fs.statSync(destPath).mtimeMs;
|
|
127
|
+
if (srcMtime > destMtime) {
|
|
128
|
+
fs.copyFileSync(srcPath, destPath);
|
|
129
|
+
(0, runtime_1.emitNervesEvent)({
|
|
130
|
+
level: "warn",
|
|
131
|
+
component: "daemon",
|
|
132
|
+
event: "daemon.bundle_migration_collision",
|
|
133
|
+
message: `migration collision: source newer, overwriting destination`,
|
|
134
|
+
meta: { srcPath, destPath, srcMtime, destMtime },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
(0, runtime_1.emitNervesEvent)({
|
|
139
|
+
level: "warn",
|
|
140
|
+
component: "daemon",
|
|
141
|
+
event: "daemon.bundle_migration_collision",
|
|
142
|
+
message: `migration collision: destination newer or equal, keeping destination`,
|
|
143
|
+
meta: { srcPath, destPath, srcMtime, destMtime },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
40
149
|
async function bundleMetaHook(ctx) {
|
|
41
150
|
(0, runtime_1.emitNervesEvent)({
|
|
42
151
|
component: "daemon",
|
|
@@ -56,9 +165,14 @@ async function bundleMetaHook(ctx) {
|
|
|
56
165
|
// Malformed JSON -- treat as missing, will overwrite with fresh
|
|
57
166
|
existing = undefined;
|
|
58
167
|
}
|
|
168
|
+
// Run schema-2 migration if needed
|
|
169
|
+
const currentSchema = existing?.bundleSchemaVersion ?? 1;
|
|
170
|
+
if (currentSchema < 2) {
|
|
171
|
+
migrateToSchema2(ctx.agentRoot);
|
|
172
|
+
}
|
|
59
173
|
const updated = {
|
|
60
174
|
runtimeVersion: ctx.currentVersion,
|
|
61
|
-
bundleSchemaVersion:
|
|
175
|
+
bundleSchemaVersion: currentSchema < 2 ? 2 : currentSchema,
|
|
62
176
|
lastUpdated: new Date().toISOString(),
|
|
63
177
|
};
|
|
64
178
|
// Save old runtimeVersion as previousRuntimeVersion (if there was one)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildInnerStatusOutput = buildInnerStatusOutput;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
function formatRelativeTime(elapsedMs) {
|
|
6
|
+
const minutes = Math.floor(elapsedMs / (60 * 1000));
|
|
7
|
+
if (minutes < 1)
|
|
8
|
+
return "just now";
|
|
9
|
+
if (minutes === 1)
|
|
10
|
+
return "1 minute ago";
|
|
11
|
+
if (minutes < 60)
|
|
12
|
+
return `${minutes} minutes ago`;
|
|
13
|
+
const hours = Math.floor(minutes / 60);
|
|
14
|
+
if (hours === 1)
|
|
15
|
+
return "1 hour ago";
|
|
16
|
+
return `${hours} hours ago`;
|
|
17
|
+
}
|
|
18
|
+
function formatCadence(cadenceMs) {
|
|
19
|
+
const minutes = Math.round(cadenceMs / (60 * 1000));
|
|
20
|
+
if (minutes >= 60) {
|
|
21
|
+
const hours = Math.round(minutes / 60);
|
|
22
|
+
return `${hours}h`;
|
|
23
|
+
}
|
|
24
|
+
return `${minutes}m`;
|
|
25
|
+
}
|
|
26
|
+
function buildInnerStatusOutput(input) {
|
|
27
|
+
const { agentName, runtimeState, journalFiles, heartbeat, attentionCount, now } = input;
|
|
28
|
+
const lines = [];
|
|
29
|
+
lines.push(`inner dialog status: ${agentName}`);
|
|
30
|
+
// Last turn
|
|
31
|
+
if (runtimeState?.lastCompletedAt) {
|
|
32
|
+
const lastMs = new Date(runtimeState.lastCompletedAt).getTime();
|
|
33
|
+
const elapsed = now - lastMs;
|
|
34
|
+
const relativeTime = formatRelativeTime(elapsed);
|
|
35
|
+
const reasonSuffix = runtimeState.reason ? ` (${runtimeState.reason})` : "";
|
|
36
|
+
lines.push(` last turn: ${relativeTime}${reasonSuffix}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
lines.push(" last turn: unknown");
|
|
40
|
+
}
|
|
41
|
+
// Status
|
|
42
|
+
if (runtimeState) {
|
|
43
|
+
const reasonSuffix = runtimeState.status === "running" && runtimeState.reason ? ` (${runtimeState.reason})` : "";
|
|
44
|
+
lines.push(` status: ${runtimeState.status}${reasonSuffix}`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
lines.push(" status: unknown");
|
|
48
|
+
}
|
|
49
|
+
// Heartbeat health
|
|
50
|
+
if (heartbeat && heartbeat.lastCompletedAt !== null) {
|
|
51
|
+
const elapsed = now - heartbeat.lastCompletedAt;
|
|
52
|
+
const threshold = heartbeat.cadenceMs * 1.5;
|
|
53
|
+
const health = elapsed < threshold ? "healthy" : "overdue";
|
|
54
|
+
const cadenceStr = formatCadence(heartbeat.cadenceMs);
|
|
55
|
+
const sinceStr = formatRelativeTime(elapsed);
|
|
56
|
+
lines.push(` heartbeat: ${health} (cadence ${cadenceStr}, ${sinceStr})`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
lines.push(" heartbeat: unknown");
|
|
60
|
+
}
|
|
61
|
+
// Journal
|
|
62
|
+
if (journalFiles.length === 0) {
|
|
63
|
+
lines.push(" journal: (empty)");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
lines.push(" journal:");
|
|
67
|
+
const sorted = [...journalFiles].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
68
|
+
for (const file of sorted) {
|
|
69
|
+
const elapsed = now - file.mtimeMs;
|
|
70
|
+
const relativeTime = formatRelativeTime(elapsed);
|
|
71
|
+
lines.push(` - ${file.name} (${relativeTime})`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Attention
|
|
75
|
+
const thoughtWord = attentionCount === 1 ? "thought" : "thoughts";
|
|
76
|
+
lines.push(` attention: ${attentionCount} held ${thoughtWord}`);
|
|
77
|
+
(0, runtime_1.emitNervesEvent)({
|
|
78
|
+
component: "daemon",
|
|
79
|
+
event: "daemon.inner_status_read",
|
|
80
|
+
message: "inner dialog status read",
|
|
81
|
+
meta: {
|
|
82
|
+
agentName,
|
|
83
|
+
status: runtimeState?.status ?? "unknown",
|
|
84
|
+
journalCount: journalFiles.length,
|
|
85
|
+
attentionCount,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.DAEMON_PLIST_LABEL = void 0;
|
|
37
37
|
exports.generateDaemonPlist = generateDaemonPlist;
|
|
38
|
+
exports.writeLaunchAgentPlist = writeLaunchAgentPlist;
|
|
38
39
|
exports.installLaunchAgent = installLaunchAgent;
|
|
39
40
|
exports.uninstallLaunchAgent = uninstallLaunchAgent;
|
|
40
41
|
exports.isDaemonInstalled = isDaemonInstalled;
|
|
@@ -44,6 +45,9 @@ exports.DAEMON_PLIST_LABEL = "bot.ouro.daemon";
|
|
|
44
45
|
function plistFilePath(homeDir) {
|
|
45
46
|
return path.join(homeDir, "Library", "LaunchAgents", `${exports.DAEMON_PLIST_LABEL}.plist`);
|
|
46
47
|
}
|
|
48
|
+
function userLaunchDomain(userUid) {
|
|
49
|
+
return `gui/${userUid}`;
|
|
50
|
+
}
|
|
47
51
|
function generateDaemonPlist(options) {
|
|
48
52
|
(0, runtime_1.emitNervesEvent)({
|
|
49
53
|
component: "daemon",
|
|
@@ -65,15 +69,37 @@ function generateDaemonPlist(options) {
|
|
|
65
69
|
` <string>--socket</string>`,
|
|
66
70
|
` <string>${options.socketPath}</string>`,
|
|
67
71
|
` </array>`,
|
|
72
|
+
` <key>RunAtLoad</key>`,
|
|
73
|
+
` <true/>`,
|
|
68
74
|
` <key>KeepAlive</key>`,
|
|
69
75
|
` <true/>`,
|
|
70
76
|
];
|
|
77
|
+
if (options.envPath) {
|
|
78
|
+
lines.push(` <key>EnvironmentVariables</key>`, ` <dict>`, ` <key>PATH</key>`, ` <string>${options.envPath}</string>`, ` </dict>`);
|
|
79
|
+
}
|
|
71
80
|
if (options.logDir) {
|
|
72
81
|
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
82
|
}
|
|
74
83
|
lines.push(`</dict>`, `</plist>`, ``);
|
|
75
84
|
return lines.join("\n");
|
|
76
85
|
}
|
|
86
|
+
function writeLaunchAgentPlist(deps, options) {
|
|
87
|
+
const launchAgentsDir = path.join(deps.homeDir, "Library", "LaunchAgents");
|
|
88
|
+
deps.mkdirp(launchAgentsDir);
|
|
89
|
+
if (options.logDir) {
|
|
90
|
+
deps.mkdirp(options.logDir);
|
|
91
|
+
}
|
|
92
|
+
const fullPath = plistFilePath(deps.homeDir);
|
|
93
|
+
const xml = generateDaemonPlist(options);
|
|
94
|
+
deps.writeFile(fullPath, xml);
|
|
95
|
+
(0, runtime_1.emitNervesEvent)({
|
|
96
|
+
component: "daemon",
|
|
97
|
+
event: "daemon.launchd_plist_written",
|
|
98
|
+
message: "daemon launch agent plist written",
|
|
99
|
+
meta: { plistPath: fullPath, entryPath: options.entryPath, socketPath: options.socketPath },
|
|
100
|
+
});
|
|
101
|
+
return fullPath;
|
|
102
|
+
}
|
|
77
103
|
function installLaunchAgent(deps, options) {
|
|
78
104
|
(0, runtime_1.emitNervesEvent)({
|
|
79
105
|
component: "daemon",
|
|
@@ -81,23 +107,27 @@ function installLaunchAgent(deps, options) {
|
|
|
81
107
|
message: "installing launch agent",
|
|
82
108
|
meta: { entryPath: options.entryPath, socketPath: options.socketPath },
|
|
83
109
|
});
|
|
84
|
-
const launchAgentsDir = path.join(deps.homeDir, "Library", "LaunchAgents");
|
|
85
|
-
deps.mkdirp(launchAgentsDir);
|
|
86
110
|
const fullPath = plistFilePath(deps.homeDir);
|
|
111
|
+
const domain = userLaunchDomain(deps.userUid);
|
|
87
112
|
// Unload existing (best effort) for idempotent re-install
|
|
88
113
|
if (deps.existsFile(fullPath)) {
|
|
89
114
|
try {
|
|
90
|
-
deps.exec(`launchctl
|
|
115
|
+
deps.exec(`launchctl bootout ${domain} "${fullPath}"`);
|
|
91
116
|
}
|
|
92
117
|
catch { /* best effort */ }
|
|
93
118
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
119
|
+
writeLaunchAgentPlist(deps, options);
|
|
120
|
+
// Bootstrap the plist so launchd manages crash recovery via KeepAlive.
|
|
121
|
+
// This is safe because ouro up calls this AFTER the daemon is already running,
|
|
122
|
+
// so launchd sees the existing process and just registers for KeepAlive.
|
|
123
|
+
try {
|
|
124
|
+
deps.exec(`launchctl bootstrap ${domain} "${fullPath}"`);
|
|
125
|
+
}
|
|
126
|
+
catch { /* already loaded */ }
|
|
97
127
|
(0, runtime_1.emitNervesEvent)({
|
|
98
128
|
component: "daemon",
|
|
99
129
|
event: "daemon.launchd_installed",
|
|
100
|
-
message: "launch agent installed",
|
|
130
|
+
message: "launch agent installed with KeepAlive",
|
|
101
131
|
meta: { plistPath: fullPath },
|
|
102
132
|
});
|
|
103
133
|
}
|
|
@@ -109,9 +139,10 @@ function uninstallLaunchAgent(deps) {
|
|
|
109
139
|
meta: {},
|
|
110
140
|
});
|
|
111
141
|
const fullPath = plistFilePath(deps.homeDir);
|
|
142
|
+
const domain = userLaunchDomain(deps.userUid);
|
|
112
143
|
if (deps.existsFile(fullPath)) {
|
|
113
144
|
try {
|
|
114
|
-
deps.exec(`launchctl
|
|
145
|
+
deps.exec(`launchctl bootout ${domain} "${fullPath}"`);
|
|
115
146
|
}
|
|
116
147
|
catch { /* best effort */ }
|
|
117
148
|
deps.removeFile(fullPath);
|
|
@@ -37,10 +37,10 @@ exports.discoverLogFiles = discoverLogFiles;
|
|
|
37
37
|
exports.readLastLines = readLastLines;
|
|
38
38
|
exports.formatLogLine = formatLogLine;
|
|
39
39
|
exports.tailLogs = tailLogs;
|
|
40
|
-
const os = __importStar(require("os"));
|
|
41
40
|
const path = __importStar(require("path"));
|
|
42
41
|
const nerves_1 = require("../../nerves");
|
|
43
42
|
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const identity_1 = require("../identity");
|
|
44
44
|
const LEVEL_COLORS = {
|
|
45
45
|
debug: "\x1b[2m",
|
|
46
46
|
info: "\x1b[36m",
|
|
@@ -49,11 +49,12 @@ const LEVEL_COLORS = {
|
|
|
49
49
|
};
|
|
50
50
|
function discoverLogFiles(options) {
|
|
51
51
|
/* v8 ignore start -- integration: default DI stubs for real OS @preserve */
|
|
52
|
-
const homeDir = options.homeDir ?? os.homedir();
|
|
53
52
|
const existsSync = options.existsSync ?? (() => false);
|
|
54
53
|
const readdirSync = options.readdirSync ?? (() => []);
|
|
55
54
|
/* v8 ignore stop */
|
|
56
|
-
const logDir =
|
|
55
|
+
const logDir = options.homeDir
|
|
56
|
+
? path.join(options.homeDir, "AgentBundles", `${options.agentName ?? "slugger"}.ouro`, "state", "daemon", "logs")
|
|
57
|
+
: (0, identity_1.getAgentDaemonLogsDir)(options.agentName);
|
|
57
58
|
const files = [];
|
|
58
59
|
if (existsSync(logDir)) {
|
|
59
60
|
for (const name of readdirSync(logDir)) {
|
|
@@ -35,9 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.FileMessageRouter = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
-
const os = __importStar(require("os"));
|
|
39
38
|
const path = __importStar(require("path"));
|
|
40
39
|
const runtime_1 = require("../../nerves/runtime");
|
|
40
|
+
const identity_1 = require("../identity");
|
|
41
41
|
function messageId(nowIso) {
|
|
42
42
|
return `msg-${nowIso.replace(/[^0-9]/g, "")}`;
|
|
43
43
|
}
|
|
@@ -45,7 +45,7 @@ class FileMessageRouter {
|
|
|
45
45
|
baseDir;
|
|
46
46
|
now;
|
|
47
47
|
constructor(options = {}) {
|
|
48
|
-
this.baseDir = options.baseDir ??
|
|
48
|
+
this.baseDir = options.baseDir ?? (0, identity_1.getAgentMessagesRoot)();
|
|
49
49
|
this.now = options.now ?? (() => new Date().toISOString());
|
|
50
50
|
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
51
51
|
}
|
|
@@ -77,12 +77,21 @@ class FileMessageRouter {
|
|
|
77
77
|
if (!fs.existsSync(inboxPath))
|
|
78
78
|
return [];
|
|
79
79
|
const raw = fs.readFileSync(inboxPath, "utf-8");
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
const messages = [];
|
|
81
|
+
const unparsed = [];
|
|
82
|
+
for (const line of raw.split("\n")) {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
if (!trimmed)
|
|
85
|
+
continue;
|
|
86
|
+
try {
|
|
87
|
+
messages.push(JSON.parse(trimmed));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
unparsed.push(trimmed);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Only clear inbox after parsing; preserve lines that failed to parse.
|
|
94
|
+
fs.writeFileSync(inboxPath, unparsed.length > 0 ? unparsed.map((l) => `${l}\n`).join("") : "", "utf-8");
|
|
86
95
|
(0, runtime_1.emitNervesEvent)({
|
|
87
96
|
component: "daemon",
|
|
88
97
|
event: "daemon.message_polled",
|
|
@@ -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.createRealOsCronDeps = createRealOsCronDeps;
|
|
37
|
+
exports.createRealCrontabDeps = createRealCrontabDeps;
|
|
38
|
+
exports.resolveOuroBinaryPath = resolveOuroBinaryPath;
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
44
|
+
function createRealOsCronDeps() {
|
|
45
|
+
(0, runtime_1.emitNervesEvent)({
|
|
46
|
+
component: "daemon",
|
|
47
|
+
event: "daemon.os_cron_deps_created",
|
|
48
|
+
message: "created real OS cron deps",
|
|
49
|
+
meta: { platform: process.platform },
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
exec: (cmd) => {
|
|
53
|
+
try {
|
|
54
|
+
(0, child_process_1.execSync)(cmd, { stdio: "ignore" });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
/* best effort */
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
writeFile: (p, c) => fs.writeFileSync(p, c, "utf-8"),
|
|
61
|
+
removeFile: (p) => {
|
|
62
|
+
try {
|
|
63
|
+
fs.unlinkSync(p);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* best effort — file may already be gone */
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
existsFile: (p) => fs.existsSync(p),
|
|
70
|
+
listDir: (dir) => {
|
|
71
|
+
try {
|
|
72
|
+
return fs.readdirSync(dir);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
mkdirp: (dir) => fs.mkdirSync(dir, { recursive: true }),
|
|
79
|
+
homeDir: os.homedir(),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function createRealCrontabDeps() {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "daemon",
|
|
85
|
+
event: "daemon.crontab_deps_created",
|
|
86
|
+
message: "created real crontab deps",
|
|
87
|
+
meta: {},
|
|
88
|
+
});
|
|
89
|
+
/* v8 ignore start -- crontab exec closures: calling these in tests would modify the real system crontab @preserve */
|
|
90
|
+
return {
|
|
91
|
+
execOutput: (cmd) => (0, child_process_1.execSync)(cmd, { encoding: "utf-8" }),
|
|
92
|
+
execWrite: (cmd, stdin) => {
|
|
93
|
+
(0, child_process_1.execSync)(cmd, { input: stdin, stdio: ["pipe", "ignore", "ignore"] });
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
/* v8 ignore stop */
|
|
97
|
+
}
|
|
98
|
+
/* v8 ignore start -- ouro path resolution: probes process.argv, filesystem layout, and PATH; branches depend on install method and runtime environment @preserve */
|
|
99
|
+
function resolveOuroBinaryPath() {
|
|
100
|
+
// Try to resolve from process.argv[1] — the script being run
|
|
101
|
+
const scriptPath = process.argv[1];
|
|
102
|
+
if (scriptPath) {
|
|
103
|
+
// If running via node dist/heart/daemon/daemon-entry.js, resolve the ouro wrapper
|
|
104
|
+
// The ouro binary is typically at the package root's bin
|
|
105
|
+
const distDir = path.resolve(path.dirname(scriptPath));
|
|
106
|
+
const packageBin = path.resolve(distDir, "..", "..", "..", "node_modules", ".bin", "ouro");
|
|
107
|
+
if (fs.existsSync(packageBin)) {
|
|
108
|
+
return packageBin;
|
|
109
|
+
}
|
|
110
|
+
// Try the repo-local scripts/ouro.sh
|
|
111
|
+
const repoOuro = path.resolve(distDir, "..", "..", "..", "scripts", "ouro.sh");
|
|
112
|
+
if (fs.existsSync(repoOuro)) {
|
|
113
|
+
return repoOuro;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Try which ouro
|
|
117
|
+
try {
|
|
118
|
+
const result = (0, child_process_1.execSync)("which ouro", { encoding: "utf-8" }).trim();
|
|
119
|
+
if (result.length > 0)
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
/* not on PATH */
|
|
124
|
+
}
|
|
125
|
+
// Fallback: use "ouro" and rely on PATH
|
|
126
|
+
(0, runtime_1.emitNervesEvent)({
|
|
127
|
+
component: "daemon",
|
|
128
|
+
event: "daemon.ouro_path_fallback",
|
|
129
|
+
message: "could not resolve full ouro binary path, falling back to 'ouro'",
|
|
130
|
+
meta: {},
|
|
131
|
+
});
|
|
132
|
+
return "ouro";
|
|
133
|
+
}
|
|
134
|
+
/* v8 ignore stop */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const runtime_1 = require("../../nerves/runtime");
|
|
5
5
|
const runtime_logging_1 = require("./runtime-logging");
|
|
6
|
-
const ouro_bot_wrapper_1 = require("
|
|
6
|
+
const ouro_bot_wrapper_1 = require("../versioning/ouro-bot-wrapper");
|
|
7
7
|
(0, runtime_logging_1.configureDaemonRuntimeLogger)("ouro-bot");
|
|
8
8
|
(0, runtime_1.emitNervesEvent)({
|
|
9
9
|
component: "daemon",
|