@adhdev/daemon-core 0.9.82-rc.321 → 0.9.82-rc.323
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/router.d.ts +13 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -6
- package/dist/index.mjs.map +1 -1
- package/dist/shared-types.d.ts +9 -0
- package/package.json +2 -2
- package/src/commands/router.ts +22 -2
- package/src/config/mesh-config.ts +60 -1
- package/src/index.ts +1 -1
- package/src/shared-types.d.ts +8 -0
- package/src/shared-types.ts +9 -0
package/dist/shared-types.d.ts
CHANGED
|
@@ -302,6 +302,15 @@ export interface SessionEntry {
|
|
|
302
302
|
seenCompletionMarker?: string;
|
|
303
303
|
surfaceHidden?: boolean;
|
|
304
304
|
settings?: Record<string, any>;
|
|
305
|
+
/**
|
|
306
|
+
* True owning-daemon id for a session a coordinator synthesises into its own
|
|
307
|
+
* status snapshot (mesh delegated sessions). The dashboard attributes the session
|
|
308
|
+
* to this daemon instead of the snapshot daemon so the worker node — not the
|
|
309
|
+
* coordinator — is shown as its machine.
|
|
310
|
+
*/
|
|
311
|
+
ownerDaemonId?: string;
|
|
312
|
+
/** True owning-machine display name fallback when the owning daemon is not aggregated. */
|
|
313
|
+
ownerMachineName?: string;
|
|
305
314
|
/** Set when this session is acting as a mesh coordinator for the given mesh. */
|
|
306
315
|
coordinator?: {
|
|
307
316
|
meshId: string;
|
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.323",
|
|
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.323",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
package/src/commands/router.ts
CHANGED
|
@@ -85,6 +85,7 @@ import { basename as pathBasename, join as pathJoin, resolve as pathResolve } fr
|
|
|
85
85
|
import * as fs from 'fs';
|
|
86
86
|
import { execFileSync } from 'node:child_process';
|
|
87
87
|
import { normalizeInteractivePromptResponse } from '../providers/types/interactive-prompt.js';
|
|
88
|
+
import { workingDirBasename } from '../providers/working-dir.js';
|
|
88
89
|
|
|
89
90
|
type ReleaseChannel = 'stable' | 'preview';
|
|
90
91
|
const CHANNEL_NPM_TAG: Record<ReleaseChannel, 'latest' | 'next'> = { stable: 'latest', preview: 'next' };
|
|
@@ -223,11 +224,14 @@ function logRepoMeshStatusDebug(event: string, fields: Record<string, unknown>):
|
|
|
223
224
|
// joinRepoPath + readGitSubmodules moved to @adhdev/mesh-shared (readGitSubmodules)
|
|
224
225
|
// — used via sharedNormalizeGitStatus / sharedPickBestTransitGitStatus below.
|
|
225
226
|
|
|
226
|
-
function buildMeshNodeDisplayLabel(node: Record<string, unknown>, nodeId: string, providerPriority: string[]): string {
|
|
227
|
+
export function buildMeshNodeDisplayLabel(node: Record<string, unknown>, nodeId: string, providerPriority: string[]): string {
|
|
227
228
|
const explicit = readStringValue(node.machineLabel, node.machine_label, node.machineNickname, node.machine_nickname, node.alias);
|
|
228
229
|
if (explicit) return explicit;
|
|
229
230
|
const workspace = readStringValue(node.workspace, node.repoRoot, node.repo_root);
|
|
230
|
-
|
|
231
|
+
// Use the OS-agnostic basename: a workspace reported by a Windows node
|
|
232
|
+
// (`D:\gh\adhdev-cloud`) must still collapse to its trailing segment even when
|
|
233
|
+
// this coordinator's own `path.basename` is POSIX-only and would not split `\`.
|
|
234
|
+
const workspaceName = workspace ? workingDirBasename(workspace) : undefined;
|
|
231
235
|
const host = readStringValue(node.machineName, node.machine_name, node.hostname, node.host, node.daemonId, node.daemon_id, node.machineId, node.machine_id);
|
|
232
236
|
const provider = providerPriority[0] || (Array.isArray(node.providers) ? readStringValue(...node.providers) : undefined);
|
|
233
237
|
const parts = [workspaceName, host, provider].filter(Boolean);
|
|
@@ -873,6 +877,22 @@ function readCachedInlineMeshActiveSessions(node: any): string[] {
|
|
|
873
877
|
return sessionId ? [sessionId] : [];
|
|
874
878
|
}
|
|
875
879
|
|
|
880
|
+
/**
|
|
881
|
+
* Resolve the owning-node attribution for a mesh node record so a coordinator can
|
|
882
|
+
* stamp the TRUE owner onto a synthetic session entry instead of letting the
|
|
883
|
+
* dashboard fall back to the coordinator's own daemonId. Returns whichever of the
|
|
884
|
+
* owning node's `daemonId` / display machine name could be read from the node's
|
|
885
|
+
* (possibly multi-serialization-path) shape; both may be undefined for a node that
|
|
886
|
+
* never carried machine identity.
|
|
887
|
+
*/
|
|
888
|
+
export function resolveMeshNodeAttribution(node: unknown): { daemonId?: string; machineName?: string } {
|
|
889
|
+
const record = readObjectRecord(node);
|
|
890
|
+
return {
|
|
891
|
+
daemonId: readMeshNodeDaemonId(record),
|
|
892
|
+
machineName: readMeshNodeDisplayMachineName(record),
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
|
|
876
896
|
export function readCachedInlineMeshActiveSessionDetails(node: any): Array<Record<string, unknown>> {
|
|
877
897
|
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
878
898
|
const activeSession = readObjectRecord(cachedStatus.activeSession);
|
|
@@ -36,12 +36,71 @@ function loadMeshConfig(): LocalMeshConfig {
|
|
|
36
36
|
try {
|
|
37
37
|
const raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
38
38
|
if (!raw || !Array.isArray(raw.meshes)) return { meshes: [] };
|
|
39
|
-
|
|
39
|
+
const config = raw as LocalMeshConfig;
|
|
40
|
+
const migrated = migrateLoadedMeshConfig(config);
|
|
41
|
+
// Persist eagerly when the on-load migration changed anything, so the
|
|
42
|
+
// dead field is gone from disk even on a pure-read path (mesh_status /
|
|
43
|
+
// mesh_list_nodes) that never otherwise mutates the config. Best-effort:
|
|
44
|
+
// a write failure (e.g. read-only fs) must not break reads, so swallow.
|
|
45
|
+
if (migrated) {
|
|
46
|
+
try {
|
|
47
|
+
saveMeshConfig(config);
|
|
48
|
+
} catch {
|
|
49
|
+
// keep the in-memory strip; disk converges on the next mutating op
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return config;
|
|
40
53
|
} catch {
|
|
41
54
|
return { meshes: [] };
|
|
42
55
|
}
|
|
43
56
|
}
|
|
44
57
|
|
|
58
|
+
/**
|
|
59
|
+
* In-place migration applied to every loaded meshes.json. Strips data that
|
|
60
|
+
* outlived the feature that wrote it so the persisted config converges on the
|
|
61
|
+
* current schema the next time it is saved.
|
|
62
|
+
*
|
|
63
|
+
* Currently: drops the dead `role` field from each node policy's providerRoles
|
|
64
|
+
* entries. providerRoles is retained for its `maxParallel` per-(node, provider)
|
|
65
|
+
* cap, but the routing `role` was removed (routing is governed solely by
|
|
66
|
+
* required_tags). A meshes.json written before the removal still carries
|
|
67
|
+
* `role: "validator"` etc.; this drops it on load so mesh_status / mesh_list_nodes
|
|
68
|
+
* never surface the dead field and the next saveMeshConfig() persists it gone.
|
|
69
|
+
*
|
|
70
|
+
* Returns true when the config was mutated (caller may persist eagerly).
|
|
71
|
+
*/
|
|
72
|
+
function migrateLoadedMeshConfig(config: LocalMeshConfig): boolean {
|
|
73
|
+
let changed = false;
|
|
74
|
+
for (const mesh of config.meshes) {
|
|
75
|
+
if (!mesh || !Array.isArray(mesh.nodes)) continue;
|
|
76
|
+
for (const node of mesh.nodes) {
|
|
77
|
+
if (stripDeadRoleFromProviderRoles(node?.policy)) changed = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return changed;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Drop the legacy `role` field from each providerRoles entry of a node policy,
|
|
85
|
+
* in place. Keeps providerType + maxParallel (the still-meaningful per-(node,
|
|
86
|
+
* provider) cap). Defensive against malformed entries — non-object items are
|
|
87
|
+
* left untouched. Returns true when at least one `role` field was removed.
|
|
88
|
+
*/
|
|
89
|
+
function stripDeadRoleFromProviderRoles(policy: unknown): boolean {
|
|
90
|
+
if (!policy || typeof policy !== 'object') return false;
|
|
91
|
+
const roles = (policy as { providerRoles?: unknown }).providerRoles;
|
|
92
|
+
if (!Array.isArray(roles)) return false;
|
|
93
|
+
let changed = false;
|
|
94
|
+
for (const entry of roles) {
|
|
95
|
+
if (entry && typeof entry === 'object' && !Array.isArray(entry)
|
|
96
|
+
&& Object.prototype.hasOwnProperty.call(entry, 'role')) {
|
|
97
|
+
delete (entry as Record<string, unknown>).role;
|
|
98
|
+
changed = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return changed;
|
|
102
|
+
}
|
|
103
|
+
|
|
45
104
|
function normalizeCapabilityTags(value: unknown): string[] | undefined {
|
|
46
105
|
if (!Array.isArray(value)) return undefined;
|
|
47
106
|
const seen = new Set<string>();
|
package/src/index.ts
CHANGED
|
@@ -300,7 +300,7 @@ export type { CdpInitializerConfig } from './cdp/initializer.js';
|
|
|
300
300
|
// ── Commands ──
|
|
301
301
|
export { DaemonCommandHandler } from './commands/handler.js';
|
|
302
302
|
export type { CommandResult, CommandContext } from './commands/handler.js';
|
|
303
|
-
export { DaemonCommandRouter, readCachedInlineMeshActiveSessionDetails } from './commands/router.js';
|
|
303
|
+
export { DaemonCommandRouter, readCachedInlineMeshActiveSessionDetails, resolveMeshNodeAttribution } from './commands/router.js';
|
|
304
304
|
export type { CommandRouterDeps, CommandRouterResult } from './commands/router.js';
|
|
305
305
|
export {
|
|
306
306
|
maybeRunDaemonUpgradeHelperFromEnv,
|
package/src/shared-types.d.ts
CHANGED
|
@@ -206,6 +206,14 @@ export interface SessionEntry {
|
|
|
206
206
|
completionMarker?: string;
|
|
207
207
|
seenCompletionMarker?: string;
|
|
208
208
|
surfaceHidden?: boolean;
|
|
209
|
+
/**
|
|
210
|
+
* True owning-daemon id for a session a coordinator synthesises into its own
|
|
211
|
+
* status snapshot (mesh delegated sessions), so the dashboard attributes it to the
|
|
212
|
+
* worker node instead of the coordinator daemon hosting the snapshot.
|
|
213
|
+
*/
|
|
214
|
+
ownerDaemonId?: string;
|
|
215
|
+
/** True owning-machine display name fallback when the owning daemon is not aggregated. */
|
|
216
|
+
ownerMachineName?: string;
|
|
209
217
|
}
|
|
210
218
|
/**
|
|
211
219
|
* Compact session metadata stored in UserSessionDO and reused by server-side
|
package/src/shared-types.ts
CHANGED
|
@@ -399,6 +399,15 @@ export interface SessionEntry {
|
|
|
399
399
|
seenCompletionMarker?: string;
|
|
400
400
|
surfaceHidden?: boolean;
|
|
401
401
|
settings?: Record<string, any>;
|
|
402
|
+
/**
|
|
403
|
+
* True owning-daemon id for a session a coordinator synthesises into its own
|
|
404
|
+
* status snapshot (mesh delegated sessions). The dashboard attributes the session
|
|
405
|
+
* to this daemon instead of the snapshot daemon so the worker node — not the
|
|
406
|
+
* coordinator — is shown as its machine.
|
|
407
|
+
*/
|
|
408
|
+
ownerDaemonId?: string;
|
|
409
|
+
/** True owning-machine display name fallback when the owning daemon is not aggregated. */
|
|
410
|
+
ownerMachineName?: string;
|
|
402
411
|
/** Set when this session is acting as a mesh coordinator for the given mesh. */
|
|
403
412
|
coordinator?: { meshId: string; role: 'coordinator' };
|
|
404
413
|
meshQueueStats?: {
|