@adhdev/daemon-core 0.9.82-rc.370 → 0.9.82-rc.371
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/dist/commands/med-family/mesh-restart.d.ts +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +56 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +54 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/commands/med-family/index.ts +2 -0
- package/src/commands/med-family/mesh-restart.ts +92 -0
- package/src/index.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.371",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.371",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -19,6 +19,7 @@ import { meshCrudHandlers } from './mesh-crud.js';
|
|
|
19
19
|
import { meshHostPairingHandlers } from './mesh-host-pairing.js';
|
|
20
20
|
import { meshQueueHandlers } from './mesh-queue.js';
|
|
21
21
|
import { fastForwardHandlers } from './fast-forward.js';
|
|
22
|
+
import { meshRestartHandlers } from './mesh-restart.js';
|
|
22
23
|
import type { MedFamilyRegistry } from './types.js';
|
|
23
24
|
|
|
24
25
|
export type { MedFamilyContext, MedFamilyHandler, MedFamilyRegistry } from './types.js';
|
|
@@ -31,5 +32,6 @@ export const medFamilyRegistry: MedFamilyRegistry = new Map(
|
|
|
31
32
|
...meshHostPairingHandlers,
|
|
32
33
|
...meshQueueHandlers,
|
|
33
34
|
...fastForwardHandlers,
|
|
35
|
+
...meshRestartHandlers,
|
|
34
36
|
}),
|
|
35
37
|
);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RF-ROUTER MED family — coordinator-triggered daemon restart.
|
|
3
|
+
*
|
|
4
|
+
* restart_daemon_node exposes the existing dashboard "preview update" path
|
|
5
|
+
* (low-family daemon_upgrade: update-to-latest-on-channel + detached restart) as
|
|
6
|
+
* a mesh command, so a coordinator can roll a worker daemon onto a freshly
|
|
7
|
+
* deployed version without a manual restart round-trip. It mirrors
|
|
8
|
+
* fast_forward_mesh_node's remote-forward shape — resolve the target node, and
|
|
9
|
+
* if it belongs to a remote daemon forward the command there so the owning
|
|
10
|
+
* daemon (not the coordinator) restarts itself — and adds an idle-gate: a node
|
|
11
|
+
* with a generating / waiting_approval / starting session is refused so an
|
|
12
|
+
* in-flight turn is never killed mid-restart.
|
|
13
|
+
*
|
|
14
|
+
* v1 reuses daemon_upgrade verbatim rather than adding a restart-only path:
|
|
15
|
+
* the goal is "pick up a just-deployed version", which inherently needs the
|
|
16
|
+
* npm reinstall the upgrade helper already performs. Already-latest is a no-op
|
|
17
|
+
* (no restart), matching the dashboard button.
|
|
18
|
+
*/
|
|
19
|
+
import { daemonIdsEquivalent, meshNodeIdMatches } from '@adhdev/mesh-shared';
|
|
20
|
+
import { daemonLifecycleHandlers } from '../low-family/daemon-lifecycle.js';
|
|
21
|
+
import type { CommandRouterResult } from '../router.js';
|
|
22
|
+
import type { MedFamilyContext, MedFamilyHandler } from './types.js';
|
|
23
|
+
|
|
24
|
+
// Session states that must block a restart: an in-flight turn or a pending
|
|
25
|
+
// approval would be lost when the daemon exits to re-spawn. Mirrors the
|
|
26
|
+
// daemon-cloud mandatory-update idle-gate (hasBlockingSessionsForMandatoryUpdate).
|
|
27
|
+
const RESTART_BLOCKING_STATES = new Set(['generating', 'waiting_approval', 'starting']);
|
|
28
|
+
|
|
29
|
+
function hasBlockingSessions(ctx: MedFamilyContext): boolean {
|
|
30
|
+
const states = ctx.deps.instanceManager.collectAllStates();
|
|
31
|
+
for (const state of states) {
|
|
32
|
+
if (RESTART_BLOCKING_STATES.has(String(state.status || ''))) return true;
|
|
33
|
+
const childStates = 'extensions' in state && Array.isArray((state as any).extensions)
|
|
34
|
+
? (state as any).extensions
|
|
35
|
+
: [];
|
|
36
|
+
for (const child of childStates) {
|
|
37
|
+
if (RESTART_BLOCKING_STATES.has(String(child?.status || ''))) return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const meshRestartHandlers: Record<string, MedFamilyHandler> = {
|
|
44
|
+
restart_daemon_node: async (ctx: MedFamilyContext, args: any): Promise<CommandRouterResult> => {
|
|
45
|
+
const meshId = typeof args?.meshId === 'string' ? args.meshId.trim() : '';
|
|
46
|
+
const nodeId = typeof args?.nodeId === 'string' ? args.nodeId.trim() : '';
|
|
47
|
+
|
|
48
|
+
// Resolve the target node's owning daemon so a command that lands on a
|
|
49
|
+
// non-owner daemon is forwarded rather than restarting the wrong daemon.
|
|
50
|
+
// preferInline so inline-cache-only worktree nodes still resolve.
|
|
51
|
+
let nodeDaemonId: string | undefined;
|
|
52
|
+
if (meshId && nodeId) {
|
|
53
|
+
const meshRecord = await ctx.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
54
|
+
const node = meshRecord?.mesh?.nodes?.find((n: any) => meshNodeIdMatches(n, nodeId));
|
|
55
|
+
nodeDaemonId = typeof node?.daemonId === 'string' ? node.daemonId.trim() : undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const selfDaemonId = ctx.deps.statusInstanceId;
|
|
59
|
+
// daemonIdsEquivalent: a legacy-form daemonId resolving to this machine's
|
|
60
|
+
// core is local — execute here instead of forwarding (and P2P self-dial).
|
|
61
|
+
// Equivalent → local. _meshDirectDispatch prevents re-forwarding once the
|
|
62
|
+
// call has landed on the owning daemon.
|
|
63
|
+
const isRemote = nodeDaemonId && selfDaemonId && !daemonIdsEquivalent(nodeDaemonId, selfDaemonId);
|
|
64
|
+
if (isRemote && ctx.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
|
|
65
|
+
const forwarded = await ctx.deps.dispatchMeshCommand(nodeDaemonId!, 'restart_daemon_node', {
|
|
66
|
+
...(typeof args === 'object' && args !== null ? args as Record<string, unknown> : {}),
|
|
67
|
+
_meshDirectDispatch: true,
|
|
68
|
+
});
|
|
69
|
+
return (forwarded ?? { success: false, error: 'no response from remote node' }) as CommandRouterResult;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Idle-gate: refuse if any session on THIS daemon is mid-turn / awaiting
|
|
73
|
+
// approval / starting. The coordinator restarts other (idle) nodes freely;
|
|
74
|
+
// restarting the coordinator's OWN daemon is naturally refused while its
|
|
75
|
+
// calling turn is 'generating' (accepted v1 limitation — call other nodes
|
|
76
|
+
// first, the coordinator last when it has gone idle).
|
|
77
|
+
if (hasBlockingSessions(ctx)) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
restarted: false,
|
|
81
|
+
code: 'blocking_sessions',
|
|
82
|
+
reason: 'Daemon has an active session (generating / waiting_approval / starting); restart refused to avoid interrupting in-flight work. Retry when the node is idle.',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Reuse the battle-tested dashboard "preview update" path: update to the
|
|
87
|
+
// latest published version on the resolved channel, then detached-restart.
|
|
88
|
+
// Already-latest is a no-op (no restart), matching the dashboard button.
|
|
89
|
+
const result = await daemonLifecycleHandlers.daemon_upgrade({ deps: ctx.deps }, args);
|
|
90
|
+
return { ...result, restarted: (result as any)?.restarting === true };
|
|
91
|
+
},
|
|
92
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -312,7 +312,7 @@ export type { CdpInitializerConfig } from './cdp/initializer.js';
|
|
|
312
312
|
// ── Commands ──
|
|
313
313
|
export { DaemonCommandHandler } from './commands/handler.js';
|
|
314
314
|
export type { CommandResult, CommandContext } from './commands/handler.js';
|
|
315
|
-
export { DaemonCommandRouter, readCachedInlineMeshActiveSessionDetails, resolveMeshNodeAttribution } from './commands/router.js';
|
|
315
|
+
export { DaemonCommandRouter, readCachedInlineMeshActiveSessionDetails, resolveMeshNodeAttribution, buildMeshNodeDataFreshness, MESH_NODE_LIVE_TRUTH_MARKER } from './commands/router.js';
|
|
316
316
|
export type { CommandRouterDeps, CommandRouterResult } from './commands/router.js';
|
|
317
317
|
export {
|
|
318
318
|
maybeRunDaemonUpgradeHelperFromEnv,
|