mixdog 0.8.0 → 0.9.0
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 +47 -23
- package/package.json +33 -27
- package/scripts/_test-folder-dialog.mjs +30 -0
- package/scripts/agent-parallel-smoke.mjs +388 -0
- package/scripts/agent-tag-reuse-smoke.mjs +183 -0
- package/scripts/background-task-meta-smoke.mjs +38 -0
- package/scripts/boot-smoke.mjs +52 -9
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/compact-active-turn-test.mjs +68 -0
- package/scripts/compact-smoke.mjs +859 -129
- package/scripts/compact-trigger-migration-smoke.mjs +187 -0
- package/scripts/fix-brief-fn.mjs +35 -0
- package/scripts/fix-format-tool-surface.mjs +24 -0
- package/scripts/fix-tool-exec-visible.mjs +42 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/hook-bus-test.mjs +330 -0
- package/scripts/lead-workflow-smoke.mjs +33 -39
- package/scripts/live-worker-smoke.mjs +43 -37
- package/scripts/llm-trace-summary.mjs +315 -0
- package/scripts/memory-meta-concurrency-test.mjs +20 -0
- package/scripts/output-style-smoke.mjs +56 -15
- package/scripts/parent-abort-link-test.mjs +44 -0
- package/scripts/patch-agent-brief.mjs +48 -0
- package/scripts/patch-app.mjs +21 -0
- package/scripts/patch-app2.mjs +18 -0
- package/scripts/patch-dist-brief.mjs +96 -0
- package/scripts/patch-tool-exec.mjs +70 -0
- package/scripts/pretool-ask-runtime-test.mjs +54 -0
- package/scripts/provider-toolcall-test.mjs +376 -0
- package/scripts/reactive-compact-persist-smoke.mjs +124 -0
- package/scripts/sanitize-tool-pairs-test.mjs +260 -0
- package/scripts/session-context-bench.mjs +344 -0
- package/scripts/session-ingest-smoke.mjs +177 -0
- package/scripts/set-effort-config-test.mjs +41 -0
- package/scripts/smoke-runtime-negative.ps1 +106 -0
- package/scripts/smoke-runtime-negative.sh +97 -0
- package/scripts/smoke.mjs +25 -0
- package/scripts/tool-result-hook-test.mjs +48 -0
- package/scripts/tool-smoke.mjs +1223 -95
- package/scripts/toolcall-args-test.mjs +150 -0
- package/scripts/tui-background-failure-smoke.mjs +73 -0
- package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
- package/src/agents/debugger/AGENT.md +8 -0
- package/src/agents/explore/AGENT.md +4 -0
- package/src/agents/heavy-worker/AGENT.md +9 -3
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +8 -0
- package/src/agents/scheduler-task/AGENT.md +12 -0
- package/src/agents/scheduler-task/agent.json +6 -0
- package/src/agents/webhook-handler/AGENT.md +12 -0
- package/src/agents/webhook-handler/agent.json +6 -0
- package/src/agents/worker/AGENT.md +9 -3
- package/src/app.mjs +77 -3
- package/src/defaults/hidden-roles.json +17 -12
- package/src/headless-role.mjs +117 -0
- package/src/help.mjs +30 -0
- package/src/hooks/lib/permission-evaluator.cjs +11 -475
- package/src/lib/keychain-cjs.cjs +9 -1
- package/src/lib/mixdog-debug.cjs +0 -7
- package/src/lib/rules-builder.cjs +240 -96
- package/src/lib/text-utils.cjs +1 -1
- package/src/mixdog-session-runtime.mjs +2325 -450
- package/src/output-styles/default.md +12 -28
- package/src/output-styles/extreme-simple.md +9 -6
- package/src/output-styles/simple.md +22 -9
- package/src/repl.mjs +118 -59
- package/src/rules/agent/00-common.md +15 -0
- package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
- package/src/rules/agent/30-explorer.md +22 -0
- package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
- package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
- package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
- package/src/rules/lead/01-general.md +9 -5
- package/src/rules/lead/04-workflow.md +51 -12
- package/src/rules/lead/lead-tool.md +6 -0
- package/src/rules/shared/01-tool.md +12 -1
- package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
- package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
- package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
- package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
- package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
- package/src/runtime/agent/orchestrator/config.mjs +174 -55
- package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
- package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
- package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
- package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
- package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
- package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
- package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
- package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
- package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
- package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
- package/src/runtime/channels/index.mjs +152 -24
- package/src/runtime/channels/lib/scheduler.mjs +18 -14
- package/src/runtime/channels/lib/session-discovery.mjs +3 -2
- package/src/runtime/channels/lib/tool-format.mjs +0 -2
- package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
- package/src/runtime/channels/lib/webhook.mjs +1 -1
- package/src/runtime/channels/tool-defs.mjs +29 -29
- package/src/runtime/memory/index.mjs +635 -107
- package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
- package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
- package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
- package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
- package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
- package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
- package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
- package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
- package/src/runtime/memory/lib/memory.mjs +88 -9
- package/src/runtime/memory/lib/model-profile.mjs +1 -1
- package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
- package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
- package/src/runtime/memory/lib/session-ingest.mjs +194 -0
- package/src/runtime/memory/lib/trace-store.mjs +96 -51
- package/src/runtime/memory/tool-defs.mjs +46 -37
- package/src/runtime/search/index.mjs +102 -466
- package/src/runtime/search/lib/web-tools.mjs +45 -25
- package/src/runtime/search/tool-defs.mjs +16 -23
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +4 -3
- package/src/runtime/shared/background-tasks.mjs +122 -11
- package/src/runtime/shared/child-spawn-gate.mjs +145 -0
- package/src/runtime/shared/config.mjs +7 -4
- package/src/runtime/shared/err-text.mjs +131 -4
- package/src/runtime/shared/llm/cost.mjs +2 -2
- package/src/runtime/shared/llm/http-agent.mjs +23 -7
- package/src/runtime/shared/llm/index.mjs +34 -11
- package/src/runtime/shared/llm/usage-log.mjs +4 -4
- package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
- package/src/runtime/shared/singleton-owner.mjs +104 -0
- package/src/runtime/shared/tool-execution-contract.mjs +199 -20
- package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
- package/src/runtime/shared/tool-surface.mjs +624 -98
- package/src/runtime/shared/user-data-guard.mjs +0 -2
- package/src/standalone/agent-task-status.mjs +203 -0
- package/src/standalone/agent-task-status.test.mjs +76 -0
- package/src/standalone/agent-tool.mjs +1913 -0
- package/src/standalone/channel-worker.mjs +370 -14
- package/src/standalone/explore-tool.mjs +165 -70
- package/src/standalone/folder-dialog.mjs +314 -0
- package/src/standalone/hook-bus.mjs +898 -22
- package/src/standalone/memory-runtime-proxy.mjs +320 -0
- package/src/standalone/projects.mjs +226 -0
- package/src/standalone/provider-admin.mjs +41 -24
- package/src/standalone/seeds.mjs +2 -69
- package/src/standalone/usage-dashboard.mjs +96 -8
- package/src/tui/App.jsx +4798 -2153
- package/src/tui/components/AnsiText.jsx +39 -28
- package/src/tui/components/ContextPanel.jsx +87 -29
- package/src/tui/components/Markdown.jsx +43 -77
- package/src/tui/components/MarkdownTable.jsx +9 -184
- package/src/tui/components/Message.jsx +28 -11
- package/src/tui/components/Picker.jsx +95 -56
- package/src/tui/components/PromptInput.jsx +367 -239
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +27 -21
- package/src/tui/components/Spinner.jsx +67 -38
- package/src/tui/components/StatusLine.jsx +606 -38
- package/src/tui/components/TextEntryPanel.jsx +128 -9
- package/src/tui/components/ToolExecution.jsx +617 -368
- package/src/tui/components/TurnDone.jsx +3 -3
- package/src/tui/components/UsagePanel.jsx +3 -5
- package/src/tui/components/tool-output-format.mjs +365 -0
- package/src/tui/components/tool-output-format.test.mjs +220 -0
- package/src/tui/dist/index.mjs +8915 -2418
- package/src/tui/engine-runtime-notification.test.mjs +115 -0
- package/src/tui/engine-tool-result-text.test.mjs +75 -0
- package/src/tui/engine.mjs +1455 -279
- package/src/tui/figures.mjs +21 -40
- package/src/tui/index.jsx +75 -31
- package/src/tui/input-editing.mjs +25 -0
- package/src/tui/markdown/format-token.mjs +511 -68
- package/src/tui/markdown/format-token.test.mjs +216 -0
- package/src/tui/markdown/render-ansi.mjs +94 -0
- package/src/tui/markdown/render-ansi.test.mjs +108 -0
- package/src/tui/markdown/stream-fence.mjs +34 -0
- package/src/tui/markdown/stream-fence.test.mjs +26 -0
- package/src/tui/markdown/table-layout.mjs +250 -0
- package/src/tui/paste-attachments.mjs +0 -7
- package/src/tui/spinner-verbs.mjs +1 -2
- package/src/tui/statusline-ansi-bridge.mjs +172 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
- package/src/tui/theme.mjs +746 -24
- package/src/tui/time-format.mjs +1 -1
- package/src/tui/transcript-tool-failures.mjs +67 -0
- package/src/tui/transcript-tool-failures.test.mjs +111 -0
- package/src/ui/ansi.mjs +1 -2
- package/src/ui/markdown.mjs +85 -26
- package/src/ui/markdown.test.mjs +70 -0
- package/src/ui/model-display.mjs +121 -0
- package/src/ui/session-stats.mjs +44 -0
- package/src/ui/statusline-context-label.test.mjs +15 -0
- package/src/ui/statusline.mjs +386 -178
- package/src/ui/tool-card.mjs +3 -16
- package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
- package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
- package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
- package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
- package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
- package/src/workflows/default/WORKFLOW.md +12 -5
- package/src/workflows/default/workflow.json +0 -1
- package/src/workflows/solo/WORKFLOW.md +15 -0
- package/src/workflows/solo/workflow.json +7 -0
- package/vendor/ink/build/output.js +6 -1
- package/src/agents/scheduler-task.md +0 -3
- package/src/agents/web-researcher/AGENT.md +0 -3
- package/src/agents/web-researcher/agent.json +0 -6
- package/src/agents/webhook-handler.md +0 -3
- package/src/rules/bridge/00-common.md +0 -5
- package/src/rules/bridge/30-explorer.md +0 -4
- package/src/rules/lead/00-tool-lead.md +0 -5
- package/src/rules/shared/00-language.md +0 -3
- package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
- package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
- package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
- package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
- package/src/standalone/bridge-tool.mjs +0 -1414
- package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { execFile, fork, spawnSync } from 'node:child_process';
|
|
2
|
-
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
3
|
-
import
|
|
2
|
+
import { appendFileSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import http from 'node:http';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
6
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
7
|
import { startChildGuardian } from '../runtime/shared/child-guardian.mjs';
|
|
8
|
+
import { claimSingletonOwner, readSingletonOwner, releaseSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
|
|
6
9
|
|
|
7
10
|
const CHANNEL_TOOLS = new Set([
|
|
8
11
|
'reply',
|
|
@@ -29,6 +32,91 @@ function logLine(path, line) {
|
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
const CHANNEL_WORKER_EXIT_CLEANUPS = new Set();
|
|
36
|
+
let channelWorkerExitHookInstalled = false;
|
|
37
|
+
|
|
38
|
+
function registerChannelWorkerExitCleanup(cleanup) {
|
|
39
|
+
if (typeof cleanup !== 'function') return () => {};
|
|
40
|
+
CHANNEL_WORKER_EXIT_CLEANUPS.add(cleanup);
|
|
41
|
+
if (!channelWorkerExitHookInstalled) {
|
|
42
|
+
channelWorkerExitHookInstalled = true;
|
|
43
|
+
process.once('exit', () => {
|
|
44
|
+
for (const fn of Array.from(CHANNEL_WORKER_EXIT_CLEANUPS)) {
|
|
45
|
+
try { fn(); } catch {}
|
|
46
|
+
}
|
|
47
|
+
CHANNEL_WORKER_EXIT_CLEANUPS.clear();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return () => {
|
|
51
|
+
CHANNEL_WORKER_EXIT_CLEANUPS.delete(cleanup);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function runtimeRoot() {
|
|
56
|
+
return process.env.MIXDOG_RUNTIME_ROOT ? resolve(process.env.MIXDOG_RUNTIME_ROOT) : join(tmpdir(), 'mixdog');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readJson(path) {
|
|
60
|
+
try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function parsePort(value) {
|
|
64
|
+
const port = Number(value);
|
|
65
|
+
return Number.isInteger(port) && port > 0 && port < 65536 ? port : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readActiveInstance() {
|
|
69
|
+
return readJson(join(runtimeRoot(), 'active-instance.json'));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readOwnerSecret(instanceId) {
|
|
73
|
+
const parsed = readJson(join(runtimeRoot(), `owner-secret-${String(instanceId)}.json`));
|
|
74
|
+
return typeof parsed?.secret === 'string' ? parsed.secret : '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function requestJson({ port, method = 'GET', path = '/', body = null, headers = {}, timeoutMs = 120_000 }) {
|
|
78
|
+
return new Promise((resolvePromise, reject) => {
|
|
79
|
+
const payload = body == null ? null : JSON.stringify(body);
|
|
80
|
+
const req = http.request({
|
|
81
|
+
hostname: '127.0.0.1',
|
|
82
|
+
port,
|
|
83
|
+
path,
|
|
84
|
+
method,
|
|
85
|
+
headers: {
|
|
86
|
+
...headers,
|
|
87
|
+
...(payload
|
|
88
|
+
? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }
|
|
89
|
+
: {}),
|
|
90
|
+
},
|
|
91
|
+
timeout: timeoutMs,
|
|
92
|
+
}, (res) => {
|
|
93
|
+
let data = '';
|
|
94
|
+
res.setEncoding('utf8');
|
|
95
|
+
res.on('data', chunk => { data += chunk; });
|
|
96
|
+
res.on('end', () => {
|
|
97
|
+
let parsed = null;
|
|
98
|
+
try { parsed = data ? JSON.parse(data) : null; } catch {}
|
|
99
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
100
|
+
reject(new Error(parsed?.error || data || `HTTP ${res.statusCode}`));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
resolvePromise(parsed ?? { raw: data });
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
req.on('error', reject);
|
|
107
|
+
req.on('timeout', () => {
|
|
108
|
+
req.destroy();
|
|
109
|
+
reject(new Error(`channel owner request timed out: ${method} ${path}`));
|
|
110
|
+
});
|
|
111
|
+
if (payload) req.write(payload);
|
|
112
|
+
req.end();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function textResult(text, isError = false) {
|
|
117
|
+
return { content: [{ type: 'text', text: String(text ?? '') }], ...(isError ? { isError: true } : {}) };
|
|
118
|
+
}
|
|
119
|
+
|
|
32
120
|
export function createStandaloneChannelWorker({
|
|
33
121
|
entry,
|
|
34
122
|
rootDir,
|
|
@@ -48,13 +136,106 @@ export function createStandaloneChannelWorker({
|
|
|
48
136
|
let inProcessMod = null;
|
|
49
137
|
let inProcessStartPromise = null;
|
|
50
138
|
let nextCallId = 1;
|
|
51
|
-
let
|
|
139
|
+
let parentExitCleanup = null;
|
|
52
140
|
const pending = new Map();
|
|
53
141
|
const ownedChildPids = new Set();
|
|
54
142
|
const logPath = join(dataDir, 'channels-worker-standalone.log');
|
|
55
143
|
const useProcessWorker = process.env.MIXDOG_CHANNEL_WORKER_PROCESS !== '0';
|
|
144
|
+
const singletonEnabled = process.env.MIXDOG_CHANNEL_SINGLETON !== '0';
|
|
145
|
+
const daemonEnabled = process.env.MIXDOG_CHANNEL_DAEMON !== '0';
|
|
146
|
+
const ownerPath = join(dataDir, 'channels-worker-owner.json');
|
|
147
|
+
const notifyBusPath = join(runtimeRoot(), 'channel-notifications.jsonl');
|
|
148
|
+
const clientDir = join(runtimeRoot(), 'channel-clients');
|
|
149
|
+
const clientPath = join(clientDir, `${process.pid}.json`);
|
|
150
|
+
let ownerClaim = null;
|
|
151
|
+
let ownerPid = process.pid;
|
|
152
|
+
let notifyBusTimer = null;
|
|
153
|
+
let notifyBusOffset = 0;
|
|
154
|
+
let clientHeartbeatTimer = null;
|
|
155
|
+
let clientHeartbeatExitCleanup = null;
|
|
156
|
+
|
|
157
|
+
function writeClientHeartbeat() {
|
|
158
|
+
try {
|
|
159
|
+
mkdirSync(clientDir, { recursive: true });
|
|
160
|
+
writeFileSync(clientPath, JSON.stringify({
|
|
161
|
+
pid: process.pid,
|
|
162
|
+
cwd,
|
|
163
|
+
updatedAt: Date.now(),
|
|
164
|
+
}));
|
|
165
|
+
} catch {}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function startClientHeartbeat() {
|
|
169
|
+
if (clientHeartbeatTimer) return;
|
|
170
|
+
writeClientHeartbeat();
|
|
171
|
+
clientHeartbeatTimer = setInterval(writeClientHeartbeat, 5000);
|
|
172
|
+
clientHeartbeatTimer.unref?.();
|
|
173
|
+
clientHeartbeatExitCleanup ||= registerChannelWorkerExitCleanup(stopClientHeartbeat);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function stopClientHeartbeat() {
|
|
177
|
+
if (clientHeartbeatExitCleanup) {
|
|
178
|
+
const unregister = clientHeartbeatExitCleanup;
|
|
179
|
+
clientHeartbeatExitCleanup = null;
|
|
180
|
+
unregister();
|
|
181
|
+
}
|
|
182
|
+
if (clientHeartbeatTimer) {
|
|
183
|
+
clearInterval(clientHeartbeatTimer);
|
|
184
|
+
clientHeartbeatTimer = null;
|
|
185
|
+
}
|
|
186
|
+
try { rmSync(clientPath, { force: true }); } catch {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function startNotifyBus() {
|
|
190
|
+
if (!onNotify || notifyBusTimer) return;
|
|
191
|
+
try { notifyBusOffset = statSync(notifyBusPath).size; } catch { notifyBusOffset = 0; }
|
|
192
|
+
notifyBusTimer = setInterval(pollNotifyBus, 500);
|
|
193
|
+
notifyBusTimer.unref?.();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function stopNotifyBus() {
|
|
197
|
+
if (!notifyBusTimer) return;
|
|
198
|
+
clearInterval(notifyBusTimer);
|
|
199
|
+
notifyBusTimer = null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function pollNotifyBus() {
|
|
203
|
+
let size = 0;
|
|
204
|
+
try { size = statSync(notifyBusPath).size; } catch { return; }
|
|
205
|
+
if (size < notifyBusOffset) notifyBusOffset = 0;
|
|
206
|
+
if (size === notifyBusOffset) return;
|
|
207
|
+
let chunk;
|
|
208
|
+
try {
|
|
209
|
+
const data = readFileSync(notifyBusPath);
|
|
210
|
+
chunk = data.subarray(notifyBusOffset, size).toString('utf8');
|
|
211
|
+
notifyBusOffset = size;
|
|
212
|
+
} catch {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
for (const line of chunk.split(/\r?\n/)) {
|
|
216
|
+
if (!line.trim()) continue;
|
|
217
|
+
try {
|
|
218
|
+
const msg = JSON.parse(line);
|
|
219
|
+
if (!msg || typeof msg !== 'object') continue;
|
|
220
|
+
onNotify({ type: 'notify', method: msg.method, params: msg.params, busId: msg.id, busPid: msg.pid });
|
|
221
|
+
} catch {}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
startNotifyBus();
|
|
226
|
+
startClientHeartbeat();
|
|
56
227
|
|
|
57
228
|
function status() {
|
|
229
|
+
const external = externalOwner();
|
|
230
|
+
if (external) {
|
|
231
|
+
return {
|
|
232
|
+
running: false,
|
|
233
|
+
pid: null,
|
|
234
|
+
pending: pending.size,
|
|
235
|
+
mode: 'external-owner',
|
|
236
|
+
ownerPid: Number(external.pid) || null,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
58
239
|
if (!useProcessWorker) {
|
|
59
240
|
return {
|
|
60
241
|
running: Boolean(inProcessMod),
|
|
@@ -71,6 +252,36 @@ export function createStandaloneChannelWorker({
|
|
|
71
252
|
};
|
|
72
253
|
}
|
|
73
254
|
|
|
255
|
+
function externalOwner() {
|
|
256
|
+
if (!singletonEnabled || ownerClaim?.owned) return null;
|
|
257
|
+
const current = readSingletonOwner(ownerPath);
|
|
258
|
+
if (current.alive && current.owner?.pid && Number(current.owner.pid) !== process.pid) return current.owner;
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function ensureOwner() {
|
|
263
|
+
if (!singletonEnabled || ownerClaim?.owned) return true;
|
|
264
|
+
ownerClaim = claimSingletonOwner(ownerPath, {
|
|
265
|
+
kind: 'channel-worker-host',
|
|
266
|
+
pid: process.pid,
|
|
267
|
+
meta: { cwd },
|
|
268
|
+
});
|
|
269
|
+
if (!ownerClaim.owned) {
|
|
270
|
+
logLine(logPath, `runtime owned by pid ${ownerClaim.owner?.pid || 'unknown'}; skipping local channel worker`);
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
installParentExitHook();
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function releaseOwner() {
|
|
278
|
+
if (!singletonEnabled || !ownerClaim?.owned) return;
|
|
279
|
+
releaseSingletonOwner(ownerPath, ownerPid);
|
|
280
|
+
ownerClaim = null;
|
|
281
|
+
ownerPid = process.pid;
|
|
282
|
+
uninstallParentExitHook();
|
|
283
|
+
}
|
|
284
|
+
|
|
74
285
|
function rejectPending(error) {
|
|
75
286
|
for (const [, item] of pending) {
|
|
76
287
|
clearTimeout(item.timer);
|
|
@@ -81,6 +292,7 @@ export function createStandaloneChannelWorker({
|
|
|
81
292
|
|
|
82
293
|
function start() {
|
|
83
294
|
if (!useProcessWorker) return startInProcess();
|
|
295
|
+
if (!ensureOwner()) return Promise.resolve(status());
|
|
84
296
|
if (stopPromise) {
|
|
85
297
|
return stopPromise.then(() => start());
|
|
86
298
|
}
|
|
@@ -94,13 +306,15 @@ export function createStandaloneChannelWorker({
|
|
|
94
306
|
cwd,
|
|
95
307
|
execArgv: ['--require', WORKER_PRELOAD],
|
|
96
308
|
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
309
|
+
detached: daemonEnabled,
|
|
97
310
|
env: {
|
|
98
311
|
...process.env,
|
|
99
312
|
MIXDOG_ROOT: rootDir,
|
|
100
313
|
MIXDOG_DATA_DIR: dataDir,
|
|
101
314
|
MIXDOG_STANDALONE: '1',
|
|
102
315
|
MIXDOG_WORKER_MODE: '1',
|
|
103
|
-
MIXDOG_CLI_OWNED: '1',
|
|
316
|
+
MIXDOG_CLI_OWNED: daemonEnabled ? '0' : '1',
|
|
317
|
+
MIXDOG_CHANNEL_DAEMON: daemonEnabled ? '1' : '0',
|
|
104
318
|
MIXDOG_CHANNELS_AUTO_BOOT: '0',
|
|
105
319
|
MIXDOG_CHANNEL_FLAG: '1',
|
|
106
320
|
MIXDOG_QUIET_SESSION_LOG: process.env.MIXDOG_QUIET_SESSION_LOG ?? '1',
|
|
@@ -108,8 +322,19 @@ export function createStandaloneChannelWorker({
|
|
|
108
322
|
windowsHide: true,
|
|
109
323
|
});
|
|
110
324
|
const spawnedPid = child.pid;
|
|
111
|
-
|
|
112
|
-
|
|
325
|
+
if (daemonEnabled && spawnedPid && singletonEnabled) {
|
|
326
|
+
releaseSingletonOwner(ownerPath, process.pid);
|
|
327
|
+
ownerClaim = claimSingletonOwner(ownerPath, {
|
|
328
|
+
kind: 'channel-worker-daemon',
|
|
329
|
+
pid: spawnedPid,
|
|
330
|
+
meta: { cwd, launcherPid: process.pid },
|
|
331
|
+
});
|
|
332
|
+
ownerPid = spawnedPid;
|
|
333
|
+
}
|
|
334
|
+
if (!daemonEnabled) {
|
|
335
|
+
startChildGuardian({ childPid: spawnedPid, label: 'channel-worker', orphanGraceMs: 8000, forceGraceMs: 3000 });
|
|
336
|
+
if (spawnedPid) ownedChildPids.add(spawnedPid);
|
|
337
|
+
}
|
|
113
338
|
installParentExitHook();
|
|
114
339
|
|
|
115
340
|
child.stderr?.on('data', (chunk) => {
|
|
@@ -135,12 +360,19 @@ export function createStandaloneChannelWorker({
|
|
|
135
360
|
return;
|
|
136
361
|
}
|
|
137
362
|
if (msg.type === 'notify') {
|
|
138
|
-
|
|
363
|
+
if (!daemonEnabled) {
|
|
364
|
+
try { onNotify?.(msg); } catch {}
|
|
365
|
+
}
|
|
139
366
|
}
|
|
140
367
|
});
|
|
141
368
|
|
|
142
369
|
child.on('exit', (code, signal) => {
|
|
143
370
|
if (spawnedPid) ownedChildPids.delete(spawnedPid);
|
|
371
|
+
if (daemonEnabled && spawnedPid) releaseSingletonOwner(ownerPath, spawnedPid);
|
|
372
|
+
if (daemonEnabled && ownerPid === spawnedPid) {
|
|
373
|
+
ownerClaim = null;
|
|
374
|
+
ownerPid = process.pid;
|
|
375
|
+
}
|
|
144
376
|
const error = new Error(`channels runtime exited (${signal || (code ?? 'unknown')})`);
|
|
145
377
|
if (readyReject) readyReject(error);
|
|
146
378
|
readyResolve = null;
|
|
@@ -162,6 +394,7 @@ export function createStandaloneChannelWorker({
|
|
|
162
394
|
}
|
|
163
395
|
|
|
164
396
|
async function startInProcess() {
|
|
397
|
+
if (!ensureOwner()) return status();
|
|
165
398
|
if (inProcessMod) return status();
|
|
166
399
|
if (inProcessStartPromise) return inProcessStartPromise;
|
|
167
400
|
inProcessStartPromise = (async () => {
|
|
@@ -184,6 +417,9 @@ export function createStandaloneChannelWorker({
|
|
|
184
417
|
|
|
185
418
|
async function execute(name, args = {}, { timeoutMs = 120_000 } = {}) {
|
|
186
419
|
if (!CHANNEL_TOOLS.has(name)) throw new Error(`unknown channel tool: ${name}`);
|
|
420
|
+
if (!ensureOwner()) {
|
|
421
|
+
return await executeViaOwnerHttp(name, args || {}, timeoutMs);
|
|
422
|
+
}
|
|
187
423
|
await start();
|
|
188
424
|
if (!useProcessWorker) {
|
|
189
425
|
const call = inProcessMod?.handleToolCallWithBridgeRetry || inProcessMod?.handleToolCall;
|
|
@@ -217,6 +453,91 @@ export function createStandaloneChannelWorker({
|
|
|
217
453
|
});
|
|
218
454
|
}
|
|
219
455
|
|
|
456
|
+
async function executeViaOwnerHttp(name, args, timeoutMs) {
|
|
457
|
+
const active = readActiveInstance();
|
|
458
|
+
const port = parsePort(active?.httpPort);
|
|
459
|
+
const instanceId = active?.instanceId;
|
|
460
|
+
const token = readOwnerSecret(instanceId);
|
|
461
|
+
if (!port || !token) {
|
|
462
|
+
const owner = externalOwner();
|
|
463
|
+
throw new Error(`channels runtime is owned by pid ${owner?.pid || 'unknown'} but owner HTTP is unavailable`);
|
|
464
|
+
}
|
|
465
|
+
const headers = {
|
|
466
|
+
'x-owner-token': token,
|
|
467
|
+
'x-owner-instance': String(instanceId || ''),
|
|
468
|
+
};
|
|
469
|
+
const post = (path, body) => requestJson({ port, method: 'POST', path, body, headers, timeoutMs });
|
|
470
|
+
const get = (path) => requestJson({ port, method: 'GET', path, headers, timeoutMs });
|
|
471
|
+
|
|
472
|
+
switch (name) {
|
|
473
|
+
case 'reply': {
|
|
474
|
+
const result = await post('/send', {
|
|
475
|
+
chatId: args.chat_id,
|
|
476
|
+
text: args.text,
|
|
477
|
+
opts: {
|
|
478
|
+
replyTo: args.reply_to,
|
|
479
|
+
files: args.files ?? [],
|
|
480
|
+
embeds: args.embeds ?? [],
|
|
481
|
+
components: args.components ?? [],
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
const ids = Array.isArray(result?.sentIds) ? result.sentIds : [];
|
|
485
|
+
return textResult(ids.length === 1 ? `sent (id: ${ids[0]})` : `sent ${ids.length} parts (ids: ${ids.join(', ')})`);
|
|
486
|
+
}
|
|
487
|
+
case 'fetch': {
|
|
488
|
+
const channel = encodeURIComponent(String(args.channel || ''));
|
|
489
|
+
const limit = Math.max(1, Number(args.limit) || 20);
|
|
490
|
+
const result = await get(`/fetch?channel=${channel}&limit=${limit}`);
|
|
491
|
+
const msgs = Array.isArray(result?.messages) ? result.messages : [];
|
|
492
|
+
const text = msgs.length === 0 ? '(no messages)' : msgs.map((m) => {
|
|
493
|
+
const atts = Number(m.attachmentCount) > 0 ? ` +${m.attachmentCount}att` : '';
|
|
494
|
+
return `[${m.ts}] ${m.user}: ${m.text} (id: ${m.id}${atts})`;
|
|
495
|
+
}).join('\n');
|
|
496
|
+
return textResult(text);
|
|
497
|
+
}
|
|
498
|
+
case 'react':
|
|
499
|
+
await post('/react', { chatId: args.chat_id, messageId: args.message_id, emoji: args.emoji });
|
|
500
|
+
return textResult('reacted');
|
|
501
|
+
case 'edit_message': {
|
|
502
|
+
const result = await post('/edit', {
|
|
503
|
+
chatId: args.chat_id,
|
|
504
|
+
messageId: args.message_id,
|
|
505
|
+
text: args.text,
|
|
506
|
+
opts: { embeds: args.embeds ?? [], components: args.components ?? [] },
|
|
507
|
+
});
|
|
508
|
+
return textResult(`edited (id: ${result?.id ?? ''})`);
|
|
509
|
+
}
|
|
510
|
+
case 'download_attachment': {
|
|
511
|
+
const result = await post('/download', { chatId: args.chat_id, messageId: args.message_id });
|
|
512
|
+
const files = Array.isArray(result?.files) ? result.files : [];
|
|
513
|
+
if (files.length === 0) return textResult('message has no attachments');
|
|
514
|
+
return textResult(`downloaded ${files.length} attachment(s):\n${files.map((f) => ` ${f.path} (${f.name}, ${f.contentType}, ${(Number(f.size || 0) / 1024).toFixed(0)}KB)`).join('\n')}`);
|
|
515
|
+
}
|
|
516
|
+
case 'schedule_status': {
|
|
517
|
+
const result = await get('/schedule-status');
|
|
518
|
+
return result?.result ?? textResult('no schedules configured');
|
|
519
|
+
}
|
|
520
|
+
case 'trigger_schedule': {
|
|
521
|
+
const result = await post('/trigger-schedule', { name: args.name });
|
|
522
|
+
return textResult(result?.result == null ? '' : String(result.result));
|
|
523
|
+
}
|
|
524
|
+
case 'schedule_control': {
|
|
525
|
+
const result = await post('/schedule-control', { name: args.name, action: args.action, minutes: args.minutes });
|
|
526
|
+
return result?.result ?? textResult(`unknown action: ${args.action}`, true);
|
|
527
|
+
}
|
|
528
|
+
case 'activate_channel_bridge':
|
|
529
|
+
await post('/bridge/activate', { active: args.active === true });
|
|
530
|
+
return textResult(`channel bridge ${args.active ? 'activated' : 'deactivated'}`);
|
|
531
|
+
case 'inject_command':
|
|
532
|
+
await post('/inject', { content: `/${String(args.command || '').trim()}`, source: 'mixdog-agent', type: 'command' });
|
|
533
|
+
return textResult(`queued /${String(args.command || '').trim()} via channel owner`);
|
|
534
|
+
case 'reload_config':
|
|
535
|
+
return textResult('reload_config must run in the channel owner process', true);
|
|
536
|
+
default:
|
|
537
|
+
return textResult(`unknown channel tool: ${name}`, true);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
220
541
|
function forceKillTree(pid) {
|
|
221
542
|
if (!pid) return;
|
|
222
543
|
if (process.platform === 'win32') {
|
|
@@ -241,16 +562,26 @@ export function createStandaloneChannelWorker({
|
|
|
241
562
|
}
|
|
242
563
|
|
|
243
564
|
function installParentExitHook() {
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
565
|
+
if (parentExitCleanup) return;
|
|
566
|
+
parentExitCleanup = registerChannelWorkerExitCleanup(() => {
|
|
567
|
+
parentExitCleanup = null;
|
|
568
|
+
if (!daemonEnabled) {
|
|
569
|
+
for (const pid of Array.from(ownedChildPids)) {
|
|
570
|
+
forceKillTreeSync(pid);
|
|
571
|
+
}
|
|
572
|
+
releaseOwner();
|
|
249
573
|
}
|
|
250
574
|
ownedChildPids.clear();
|
|
251
575
|
});
|
|
252
576
|
}
|
|
253
577
|
|
|
578
|
+
function uninstallParentExitHook() {
|
|
579
|
+
if (!parentExitCleanup) return;
|
|
580
|
+
const unregister = parentExitCleanup;
|
|
581
|
+
parentExitCleanup = null;
|
|
582
|
+
unregister();
|
|
583
|
+
}
|
|
584
|
+
|
|
254
585
|
function unrefChildHandles(target) {
|
|
255
586
|
try { target?.unref?.(); } catch {}
|
|
256
587
|
try { target?.stderr?.unref?.(); } catch {}
|
|
@@ -261,14 +592,20 @@ export function createStandaloneChannelWorker({
|
|
|
261
592
|
|
|
262
593
|
function stop(reason = 'standalone shutdown', options = {}) {
|
|
263
594
|
const waitForExit = options?.waitForExit !== false;
|
|
595
|
+
stopNotifyBus();
|
|
596
|
+
stopClientHeartbeat();
|
|
264
597
|
if (stopPromise) return stopPromise;
|
|
265
598
|
if (!useProcessWorker) {
|
|
266
|
-
if (!inProcessMod && !inProcessStartPromise)
|
|
599
|
+
if (!inProcessMod && !inProcessStartPromise) {
|
|
600
|
+
releaseOwner();
|
|
601
|
+
return Promise.resolve(false);
|
|
602
|
+
}
|
|
267
603
|
stopPromise = Promise.resolve(inProcessStartPromise)
|
|
268
604
|
.catch(() => null)
|
|
269
605
|
.then(async () => {
|
|
270
606
|
try { await inProcessMod?.stop?.(reason); } catch {}
|
|
271
607
|
inProcessMod = null;
|
|
608
|
+
releaseOwner();
|
|
272
609
|
return true;
|
|
273
610
|
})
|
|
274
611
|
.finally(() => {
|
|
@@ -276,10 +613,24 @@ export function createStandaloneChannelWorker({
|
|
|
276
613
|
});
|
|
277
614
|
return stopPromise;
|
|
278
615
|
}
|
|
279
|
-
if (!child)
|
|
616
|
+
if (!child) {
|
|
617
|
+
stopNotifyBus();
|
|
618
|
+
releaseOwner();
|
|
619
|
+
return Promise.resolve(false);
|
|
620
|
+
}
|
|
280
621
|
const target = child;
|
|
281
622
|
const targetPid = target.pid;
|
|
282
623
|
child = null;
|
|
624
|
+
if (daemonEnabled && options?.force !== true) {
|
|
625
|
+
rejectPending(new Error(`channels runtime detached (${reason})`));
|
|
626
|
+
if (targetPid) ownedChildPids.delete(targetPid);
|
|
627
|
+
try { target.disconnect?.(); } catch {}
|
|
628
|
+
unrefChildHandles(target);
|
|
629
|
+
ownerClaim = null;
|
|
630
|
+
ownerPid = process.pid;
|
|
631
|
+
uninstallParentExitHook();
|
|
632
|
+
return Promise.resolve(true);
|
|
633
|
+
}
|
|
283
634
|
if (!waitForExit) {
|
|
284
635
|
rejectPending(new Error(`channels runtime shutdown requested (${reason})`));
|
|
285
636
|
if (targetPid) ownedChildPids.delete(targetPid);
|
|
@@ -291,6 +642,8 @@ export function createStandaloneChannelWorker({
|
|
|
291
642
|
clearTimeout(sendTimer);
|
|
292
643
|
stopPromise = null;
|
|
293
644
|
unrefChildHandles(target);
|
|
645
|
+
releaseOwner();
|
|
646
|
+
uninstallParentExitHook();
|
|
294
647
|
resolve(ok);
|
|
295
648
|
};
|
|
296
649
|
const sendTimer = setTimeout(() => finish(false), 250);
|
|
@@ -314,6 +667,9 @@ export function createStandaloneChannelWorker({
|
|
|
314
667
|
clearTimeout(termTimer);
|
|
315
668
|
clearTimeout(killTimer);
|
|
316
669
|
stopPromise = null;
|
|
670
|
+
stopNotifyBus();
|
|
671
|
+
releaseOwner();
|
|
672
|
+
uninstallParentExitHook();
|
|
317
673
|
resolve(ok);
|
|
318
674
|
};
|
|
319
675
|
const termTimer = setTimeout(() => {
|