@adhdev/daemon-core 0.9.76-rc.37 → 0.9.76-rc.39
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 +7 -0
- package/dist/index.js +164 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +164 -5
- package/dist/index.mjs.map +1 -1
- package/dist/repo-mesh-types.d.ts +7 -0
- package/package.json +1 -1
- package/src/commands/router.d.ts +1 -0
- package/src/commands/router.ts +181 -2
- package/src/config/mesh-config.ts +17 -2
- package/src/repo-mesh-types.ts +9 -0
|
@@ -21,6 +21,9 @@ export interface SessionHostControlPlane {
|
|
|
21
21
|
}): Promise<any>;
|
|
22
22
|
listSessions(): Promise<any[]>;
|
|
23
23
|
stopSession(sessionId: string): Promise<any>;
|
|
24
|
+
deleteSession(sessionId: string, opts?: {
|
|
25
|
+
force?: boolean;
|
|
26
|
+
}): Promise<any>;
|
|
24
27
|
resumeSession(sessionId: string): Promise<any>;
|
|
25
28
|
restartSession(sessionId: string): Promise<any>;
|
|
26
29
|
sendSignal(sessionId: string, signal: string): Promise<any>;
|
|
@@ -85,6 +88,10 @@ export declare class DaemonCommandRouter {
|
|
|
85
88
|
private getMeshForCommand;
|
|
86
89
|
private updateInlineMeshNode;
|
|
87
90
|
private removeInlineMeshNode;
|
|
91
|
+
private normalizeMeshSessionCleanupMode;
|
|
92
|
+
private sessionMatchesMeshNode;
|
|
93
|
+
private isCompletedHostedSession;
|
|
94
|
+
private cleanupMeshSessions;
|
|
88
95
|
private traceSessionHostAction;
|
|
89
96
|
/**
|
|
90
97
|
* Unified command routing.
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,8 @@ var init_repo_mesh_types = __esm({
|
|
|
41
41
|
requireApprovalForPush: true,
|
|
42
42
|
requireApprovalForDestructiveGit: true,
|
|
43
43
|
dirtyWorkspaceBehavior: "warn",
|
|
44
|
-
maxParallelTasks: 2
|
|
44
|
+
maxParallelTasks: 2,
|
|
45
|
+
sessionCleanupOnNodeRemove: "preserve"
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
});
|
|
@@ -460,6 +461,18 @@ function normalizeRepoIdentity(remoteUrl) {
|
|
|
460
461
|
if (sshMatch) return `${sshMatch[1]}/${sshMatch[2]}`;
|
|
461
462
|
return identity;
|
|
462
463
|
}
|
|
464
|
+
function mergeMeshPolicy(base, patch) {
|
|
465
|
+
const policy = { ...DEFAULT_MESH_POLICY, ...base || {}, ...patch || {} };
|
|
466
|
+
if (!["block", "warn", "checkpoint_then_continue"].includes(policy.dirtyWorkspaceBehavior)) {
|
|
467
|
+
policy.dirtyWorkspaceBehavior = "warn";
|
|
468
|
+
}
|
|
469
|
+
const maxParallelTasks = Number(policy.maxParallelTasks);
|
|
470
|
+
policy.maxParallelTasks = Number.isFinite(maxParallelTasks) ? Math.max(1, Math.min(8, Math.floor(maxParallelTasks))) : 2;
|
|
471
|
+
if (!SESSION_CLEANUP_MODES.has(String(policy.sessionCleanupOnNodeRemove))) {
|
|
472
|
+
policy.sessionCleanupOnNodeRemove = "preserve";
|
|
473
|
+
}
|
|
474
|
+
return policy;
|
|
475
|
+
}
|
|
463
476
|
function listMeshes() {
|
|
464
477
|
return loadMeshConfig().meshes;
|
|
465
478
|
}
|
|
@@ -483,7 +496,7 @@ function createMesh(opts) {
|
|
|
483
496
|
repoIdentity,
|
|
484
497
|
repoRemoteUrl: opts.repoRemoteUrl,
|
|
485
498
|
defaultBranch: opts.defaultBranch,
|
|
486
|
-
policy:
|
|
499
|
+
policy: mergeMeshPolicy(void 0, opts.policy),
|
|
487
500
|
coordinator: opts.coordinator || {},
|
|
488
501
|
nodes: [],
|
|
489
502
|
createdAt: now,
|
|
@@ -499,7 +512,7 @@ function updateMesh(meshId, opts) {
|
|
|
499
512
|
if (!mesh) return void 0;
|
|
500
513
|
if (opts.name !== void 0) mesh.name = opts.name.trim().slice(0, 100);
|
|
501
514
|
if (opts.defaultBranch !== void 0) mesh.defaultBranch = opts.defaultBranch;
|
|
502
|
-
if (opts.policy) mesh.policy =
|
|
515
|
+
if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
|
|
503
516
|
if (opts.coordinator) mesh.coordinator = opts.coordinator;
|
|
504
517
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
505
518
|
saveMeshConfig(config);
|
|
@@ -562,7 +575,7 @@ function updateNode(meshId, nodeId, opts) {
|
|
|
562
575
|
saveMeshConfig(config);
|
|
563
576
|
return node;
|
|
564
577
|
}
|
|
565
|
-
var import_fs2, import_path2, import_crypto3;
|
|
578
|
+
var import_fs2, import_path2, import_crypto3, SESSION_CLEANUP_MODES;
|
|
566
579
|
var init_mesh_config = __esm({
|
|
567
580
|
"src/config/mesh-config.ts"() {
|
|
568
581
|
"use strict";
|
|
@@ -571,6 +584,7 @@ var init_mesh_config = __esm({
|
|
|
571
584
|
import_crypto3 = require("crypto");
|
|
572
585
|
init_config();
|
|
573
586
|
init_repo_mesh_types();
|
|
587
|
+
SESSION_CLEANUP_MODES = /* @__PURE__ */ new Set(["preserve", "stop", "delete_stopped", "stop_and_delete"]);
|
|
574
588
|
}
|
|
575
589
|
});
|
|
576
590
|
|
|
@@ -21491,6 +21505,75 @@ var DaemonCommandRouter = class {
|
|
|
21491
21505
|
this.inlineMeshCache.set(meshId, mesh);
|
|
21492
21506
|
return true;
|
|
21493
21507
|
}
|
|
21508
|
+
normalizeMeshSessionCleanupMode(value) {
|
|
21509
|
+
return value === "stop" || value === "delete_stopped" || value === "stop_and_delete" || value === "preserve" ? value : "preserve";
|
|
21510
|
+
}
|
|
21511
|
+
sessionMatchesMeshNode(record, node, nodeId, sessionIds) {
|
|
21512
|
+
const sessionId = typeof record?.sessionId === "string" ? record.sessionId : "";
|
|
21513
|
+
if (!sessionId) return false;
|
|
21514
|
+
if (sessionIds?.size) return sessionIds.has(sessionId);
|
|
21515
|
+
const workspace = typeof node?.workspace === "string" ? node.workspace : "";
|
|
21516
|
+
if (workspace && record?.workspace === workspace) return true;
|
|
21517
|
+
if (record?.meta?.meshNodeId === nodeId) return true;
|
|
21518
|
+
return false;
|
|
21519
|
+
}
|
|
21520
|
+
isCompletedHostedSession(record) {
|
|
21521
|
+
return record?.lifecycle === "stopped" || record?.lifecycle === "failed" || record?.lifecycle === "interrupted";
|
|
21522
|
+
}
|
|
21523
|
+
async cleanupMeshSessions(args) {
|
|
21524
|
+
if (args.mode === "preserve") {
|
|
21525
|
+
return { success: true, mode: "preserve", matchedCount: 0, stoppedSessionIds: [], deletedSessionIds: [], skippedSessionIds: [] };
|
|
21526
|
+
}
|
|
21527
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
21528
|
+
const requestedSessionIds = Array.isArray(args.sessionIds) ? new Set(args.sessionIds.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean)) : void 0;
|
|
21529
|
+
const sessions = await this.deps.sessionHostControl.listSessions();
|
|
21530
|
+
const matched = sessions.filter((record) => this.sessionMatchesMeshNode(record, args.node, args.nodeId, requestedSessionIds));
|
|
21531
|
+
const stoppedSessionIds = [];
|
|
21532
|
+
const deletedSessionIds = [];
|
|
21533
|
+
const skippedSessionIds = [];
|
|
21534
|
+
const errors = [];
|
|
21535
|
+
for (const record of matched) {
|
|
21536
|
+
const sessionId = String(record.sessionId);
|
|
21537
|
+
const completed = this.isCompletedHostedSession(record);
|
|
21538
|
+
try {
|
|
21539
|
+
if (args.mode === "stop") {
|
|
21540
|
+
if (!completed) {
|
|
21541
|
+
if (!args.dryRun) await this.deps.sessionHostControl.stopSession(sessionId);
|
|
21542
|
+
stoppedSessionIds.push(sessionId);
|
|
21543
|
+
} else {
|
|
21544
|
+
skippedSessionIds.push(sessionId);
|
|
21545
|
+
}
|
|
21546
|
+
continue;
|
|
21547
|
+
}
|
|
21548
|
+
if (args.mode === "delete_stopped") {
|
|
21549
|
+
if (completed) {
|
|
21550
|
+
if (!args.dryRun) await this.deps.sessionHostControl.deleteSession(sessionId, { force: false });
|
|
21551
|
+
deletedSessionIds.push(sessionId);
|
|
21552
|
+
} else {
|
|
21553
|
+
skippedSessionIds.push(sessionId);
|
|
21554
|
+
}
|
|
21555
|
+
continue;
|
|
21556
|
+
}
|
|
21557
|
+
if (args.mode === "stop_and_delete") {
|
|
21558
|
+
if (!args.dryRun) await this.deps.sessionHostControl.deleteSession(sessionId, { force: true });
|
|
21559
|
+
deletedSessionIds.push(sessionId);
|
|
21560
|
+
continue;
|
|
21561
|
+
}
|
|
21562
|
+
} catch (e) {
|
|
21563
|
+
errors.push({ sessionId, error: e?.message || String(e) });
|
|
21564
|
+
}
|
|
21565
|
+
}
|
|
21566
|
+
return {
|
|
21567
|
+
success: errors.length === 0,
|
|
21568
|
+
mode: args.mode,
|
|
21569
|
+
dryRun: args.dryRun === true,
|
|
21570
|
+
matchedCount: matched.length,
|
|
21571
|
+
stoppedSessionIds,
|
|
21572
|
+
deletedSessionIds,
|
|
21573
|
+
skippedSessionIds,
|
|
21574
|
+
...errors.length ? { errors } : {}
|
|
21575
|
+
};
|
|
21576
|
+
}
|
|
21494
21577
|
async traceSessionHostAction(action, args, run, summarizeResult) {
|
|
21495
21578
|
const interactionId = typeof args?._interactionId === "string" ? args._interactionId : void 0;
|
|
21496
21579
|
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : void 0;
|
|
@@ -22157,7 +22240,26 @@ var DaemonCommandRouter = class {
|
|
|
22157
22240
|
if (!name) return { success: false, error: "name required" };
|
|
22158
22241
|
try {
|
|
22159
22242
|
const { createMesh: createMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
22160
|
-
const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch });
|
|
22243
|
+
const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy });
|
|
22244
|
+
return { success: true, mesh };
|
|
22245
|
+
} catch (e) {
|
|
22246
|
+
return { success: false, error: e.message };
|
|
22247
|
+
}
|
|
22248
|
+
}
|
|
22249
|
+
case "update_mesh": {
|
|
22250
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
22251
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
22252
|
+
try {
|
|
22253
|
+
const { updateMesh: updateMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
22254
|
+
const patch = {};
|
|
22255
|
+
if (typeof args?.name === "string") patch.name = args.name;
|
|
22256
|
+
if (typeof args?.defaultBranch === "string") patch.defaultBranch = args.defaultBranch;
|
|
22257
|
+
if (args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy)) patch.policy = args.policy;
|
|
22258
|
+
if (args?.coordinator && typeof args.coordinator === "object" && !Array.isArray(args.coordinator)) patch.coordinator = args.coordinator;
|
|
22259
|
+
if (!Object.keys(patch).length) return { success: false, error: "No updates provided" };
|
|
22260
|
+
const mesh = updateMesh2(meshId, patch);
|
|
22261
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
22262
|
+
this.inlineMeshCache.set(meshId, mesh);
|
|
22161
22263
|
return { success: true, mesh };
|
|
22162
22264
|
} catch (e) {
|
|
22163
22265
|
return { success: false, error: e.message };
|
|
@@ -22194,6 +22296,54 @@ var DaemonCommandRouter = class {
|
|
|
22194
22296
|
return { success: false, error: e.message };
|
|
22195
22297
|
}
|
|
22196
22298
|
}
|
|
22299
|
+
case "update_mesh_node": {
|
|
22300
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
22301
|
+
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
22302
|
+
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
22303
|
+
try {
|
|
22304
|
+
const { updateNode: updateNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
22305
|
+
const policy = args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy) ? { ...args.policy } : {};
|
|
22306
|
+
if (Array.isArray(args?.providerPriority)) {
|
|
22307
|
+
const providerPriority = args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean);
|
|
22308
|
+
delete policy.provider_priority;
|
|
22309
|
+
if (providerPriority.length) {
|
|
22310
|
+
policy.providerPriority = providerPriority;
|
|
22311
|
+
} else {
|
|
22312
|
+
delete policy.providerPriority;
|
|
22313
|
+
}
|
|
22314
|
+
}
|
|
22315
|
+
const node = updateNode2(meshId, nodeId, { policy });
|
|
22316
|
+
if (!node) return { success: false, error: "Mesh node not found" };
|
|
22317
|
+
return { success: true, node };
|
|
22318
|
+
} catch (e) {
|
|
22319
|
+
return { success: false, error: e.message };
|
|
22320
|
+
}
|
|
22321
|
+
}
|
|
22322
|
+
case "cleanup_mesh_sessions": {
|
|
22323
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
22324
|
+
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
22325
|
+
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
22326
|
+
try {
|
|
22327
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
22328
|
+
const mesh = meshRecord?.mesh;
|
|
22329
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
22330
|
+
const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
22331
|
+
if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
|
|
22332
|
+
const mode = this.normalizeMeshSessionCleanupMode(args?.mode ?? mesh?.policy?.sessionCleanupOnNodeRemove);
|
|
22333
|
+
const sessionIds = Array.isArray(args?.sessionIds) ? args.sessionIds.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean) : void 0;
|
|
22334
|
+
const result = await this.cleanupMeshSessions({
|
|
22335
|
+
meshId,
|
|
22336
|
+
nodeId,
|
|
22337
|
+
node,
|
|
22338
|
+
mode,
|
|
22339
|
+
sessionIds,
|
|
22340
|
+
dryRun: args?.dryRun === true
|
|
22341
|
+
});
|
|
22342
|
+
return result;
|
|
22343
|
+
} catch (e) {
|
|
22344
|
+
return { success: false, error: e.message };
|
|
22345
|
+
}
|
|
22346
|
+
}
|
|
22197
22347
|
case "remove_mesh_node": {
|
|
22198
22348
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
22199
22349
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
@@ -22202,6 +22352,14 @@ var DaemonCommandRouter = class {
|
|
|
22202
22352
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
22203
22353
|
const mesh = meshRecord?.mesh;
|
|
22204
22354
|
const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
22355
|
+
const sessionCleanupMode = this.normalizeMeshSessionCleanupMode(
|
|
22356
|
+
args?.sessionCleanupMode ?? args?.session_cleanup_mode ?? mesh?.policy?.sessionCleanupOnNodeRemove
|
|
22357
|
+
);
|
|
22358
|
+
let sessionCleanup;
|
|
22359
|
+
if (node && sessionCleanupMode !== "preserve") {
|
|
22360
|
+
sessionCleanup = await this.cleanupMeshSessions({ meshId, nodeId, node, mode: sessionCleanupMode });
|
|
22361
|
+
if (sessionCleanup.success === false) return { success: false, removed: false, sessionCleanup };
|
|
22362
|
+
}
|
|
22205
22363
|
if (node?.isLocalWorktree && node.workspace) {
|
|
22206
22364
|
try {
|
|
22207
22365
|
const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
|
|
@@ -22221,7 +22379,7 @@ var DaemonCommandRouter = class {
|
|
|
22221
22379
|
const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
22222
22380
|
removed = removeNode3(meshId, nodeId);
|
|
22223
22381
|
}
|
|
22224
|
-
return { success: true, removed };
|
|
22382
|
+
return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {} };
|
|
22225
22383
|
} catch (e) {
|
|
22226
22384
|
return { success: false, error: e.message };
|
|
22227
22385
|
}
|