@adhdev/daemon-core 0.9.82-rc.9 → 0.9.82-rc.90
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/cli-adapters/provider-cli-adapter.d.ts +2 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/mesh-config.d.ts +66 -1
- package/dist/index.d.ts +13 -6
- package/dist/index.js +5395 -1197
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5359 -1183
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +60 -0
- package/dist/mesh/mesh-events.d.ts +29 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/refine-config.d.ts +176 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/repo-mesh-types.d.ts +46 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/cli-adapters/provider-cli-adapter.ts +91 -3
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +4 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +20 -10
- package/src/commands/chat-commands.ts +454 -15
- package/src/commands/cli-manager.ts +126 -0
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +2687 -435
- package/src/config/chat-history.ts +9 -7
- package/src/config/mesh-config.ts +245 -1
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/index.ts +31 -5
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +176 -0
- package/src/mesh/coordinator-prompt.ts +30 -7
- package/src/mesh/mesh-active-work.ts +243 -0
- package/src/mesh/mesh-events.ts +400 -47
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/refine-config.ts +356 -0
- package/src/providers/chat-message-normalization.ts +3 -1
- package/src/providers/cli-provider-instance.ts +91 -13
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/provider-loader.ts +10 -4
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +51 -0
- package/src/status/reporter.ts +15 -0
- package/src/system/host-memory.ts +29 -12
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import * as fs from 'fs';
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import * as os from 'os';
|
|
15
|
-
|
|
15
|
+
// Removed execSync import
|
|
16
16
|
import { platform } from 'os';
|
|
17
17
|
import type { ProviderLoader } from './provider-loader.js';
|
|
18
18
|
import type { ProviderModule } from './contracts.js';
|
|
@@ -117,22 +117,40 @@ export class VersionArchive {
|
|
|
117
117
|
|
|
118
118
|
// ─── Version Detection ──────────────────────────────
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
import { exec } from 'child_process';
|
|
121
|
+
|
|
122
|
+
async function runCommand(cmd: string, timeout = 10000): Promise<string | null> {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
exec(cmd, {
|
|
123
125
|
encoding: 'utf-8',
|
|
124
126
|
timeout,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
127
|
+
}, (error, stdout) => {
|
|
128
|
+
if (error) return resolve(null);
|
|
129
|
+
resolve(stdout.trim());
|
|
130
|
+
});
|
|
131
|
+
});
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
function findBinary(name: string): string | null {
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
135
|
+
const isWin = platform() === 'win32';
|
|
136
|
+
const paths = (process.env.PATH || '').split(isWin ? ';' : ':');
|
|
137
|
+
const exes = isWin ? ['.exe', '.cmd', '.bat', ''] : [''];
|
|
138
|
+
|
|
139
|
+
for (const p of paths) {
|
|
140
|
+
if (!p) continue;
|
|
141
|
+
for (const ext of exes) {
|
|
142
|
+
const fullPath = path.join(p, name + ext);
|
|
143
|
+
try {
|
|
144
|
+
if (fs.existsSync(fullPath)) {
|
|
145
|
+
const stat = fs.statSync(fullPath);
|
|
146
|
+
if (stat.isFile() && (isWin || (stat.mode & 0o111))) {
|
|
147
|
+
return fullPath;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} catch { }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
136
154
|
}
|
|
137
155
|
|
|
138
156
|
/** Extract version string from CLI output */
|
|
@@ -162,16 +180,16 @@ function getPlatformVersionCommand(
|
|
|
162
180
|
return undefined;
|
|
163
181
|
}
|
|
164
182
|
|
|
165
|
-
function getVersion(binary: string, versionCommand?: string): string | null {
|
|
183
|
+
async function getVersion(binary: string, versionCommand?: string): Promise<string | null> {
|
|
166
184
|
// Custom version command from provider.json
|
|
167
185
|
if (versionCommand) {
|
|
168
|
-
const raw = runCommand(versionCommand);
|
|
186
|
+
const raw = await runCommand(versionCommand);
|
|
169
187
|
return raw ? parseVersion(raw) : null;
|
|
170
188
|
}
|
|
171
189
|
|
|
172
190
|
// Default: try --version, then -V, then -v
|
|
173
191
|
for (const flag of ['--version', '-V', '-v']) {
|
|
174
|
-
const raw = runCommand(`"${binary}" ${flag}`);
|
|
192
|
+
const raw = await runCommand(`"${binary}" ${flag}`);
|
|
175
193
|
if (raw && raw.length < 500) return parseVersion(raw);
|
|
176
194
|
}
|
|
177
195
|
return null;
|
|
@@ -191,11 +209,11 @@ function checkPathExists(paths: string[]): string | null {
|
|
|
191
209
|
}
|
|
192
210
|
|
|
193
211
|
/** macOS: Get app version from Info.plist */
|
|
194
|
-
function getMacAppVersion(appPath: string): string | null {
|
|
212
|
+
async function getMacAppVersion(appPath: string): Promise<string | null> {
|
|
195
213
|
if (platform() !== 'darwin' || !appPath.endsWith('.app')) return null;
|
|
196
214
|
const plistPath = path.join(appPath, 'Contents', 'Info.plist');
|
|
197
215
|
if (!fs.existsSync(plistPath)) return null;
|
|
198
|
-
const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
|
|
216
|
+
const raw = await runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
|
|
199
217
|
return raw || null;
|
|
200
218
|
}
|
|
201
219
|
|
|
@@ -242,10 +260,10 @@ export async function detectAllVersions(
|
|
|
242
260
|
|
|
243
261
|
// Version: try CLI first, then plist
|
|
244
262
|
if (resolvedBin) {
|
|
245
|
-
info.version = getVersion(resolvedBin, versionCommand);
|
|
263
|
+
info.version = await getVersion(resolvedBin, versionCommand);
|
|
246
264
|
}
|
|
247
265
|
if (!info.version && appPath) {
|
|
248
|
-
info.version = getMacAppVersion(appPath);
|
|
266
|
+
info.version = await getMacAppVersion(appPath);
|
|
249
267
|
}
|
|
250
268
|
|
|
251
269
|
} else if (provider.category === 'cli' || provider.category === 'acp') {
|
|
@@ -256,7 +274,7 @@ export async function detectAllVersions(
|
|
|
256
274
|
info.binary = binPath || null;
|
|
257
275
|
|
|
258
276
|
if (binPath) {
|
|
259
|
-
info.version = getVersion(binPath, versionCommand);
|
|
277
|
+
info.version = await getVersion(binPath, versionCommand);
|
|
260
278
|
}
|
|
261
279
|
|
|
262
280
|
} else if (provider.category === 'extension') {
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -23,11 +23,42 @@ export interface RepoMesh {
|
|
|
23
23
|
defaultBranch?: string;
|
|
24
24
|
policy: RepoMeshPolicy;
|
|
25
25
|
coordinator: RepoMeshCoordinatorConfig;
|
|
26
|
+
meshHost?: RepoMeshHostMetadata;
|
|
26
27
|
projectContext: ProjectContextSnapshot;
|
|
27
28
|
nodes: RepoMeshNode[];
|
|
28
29
|
status: 'active' | 'archived' | 'deleted';
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
export type RepoMeshDaemonRole = 'host' | 'member';
|
|
33
|
+
|
|
34
|
+
export interface RepoMeshHostPairingMetadata {
|
|
35
|
+
status: 'not_configured' | 'pairing' | 'paired' | 'rejected' | 'revoked';
|
|
36
|
+
tokenId?: string;
|
|
37
|
+
joinedAt?: string;
|
|
38
|
+
lastPairedAt?: string;
|
|
39
|
+
lastRejectedAt?: string;
|
|
40
|
+
expiresAt?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RepoMeshHostMetadata {
|
|
44
|
+
/** Local daemon role for this mesh. Missing metadata defaults to host for standalone compatibility. */
|
|
45
|
+
role: RepoMeshDaemonRole;
|
|
46
|
+
/** Daemon that owns mesh truth/status/git/queue/session/ledger/coordinator ownership. */
|
|
47
|
+
hostDaemonId?: string;
|
|
48
|
+
/** Mesh node that represents the host daemon, when known. */
|
|
49
|
+
hostNodeId?: string;
|
|
50
|
+
/** Future standalone manual pairing endpoint entered by member daemons. */
|
|
51
|
+
hostAddress?: string;
|
|
52
|
+
/** Redacted pairing state only; raw join tokens must not be persisted here. */
|
|
53
|
+
pairing?: RepoMeshHostPairingMetadata;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface RepoMeshHostStatus extends RepoMeshHostMetadata {
|
|
57
|
+
canOwnCoordinator: boolean;
|
|
58
|
+
canOwnQueue: boolean;
|
|
59
|
+
defaulted: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
31
62
|
export interface RepoMeshNode {
|
|
32
63
|
id: string;
|
|
33
64
|
daemonId: string;
|
|
@@ -42,6 +73,7 @@ export interface RepoMeshNode {
|
|
|
42
73
|
effectiveCapabilities: RepoMeshNodeCapabilities;
|
|
43
74
|
policy: RepoMeshNodePolicy;
|
|
44
75
|
health: RepoMeshNodeHealth;
|
|
76
|
+
role?: RepoMeshDaemonRole;
|
|
45
77
|
status: 'enabled' | 'disabled' | 'removed';
|
|
46
78
|
}
|
|
47
79
|
|
|
@@ -62,6 +94,13 @@ export interface RepoMeshPolicy {
|
|
|
62
94
|
requirePreTaskCheckpoint: boolean;
|
|
63
95
|
requirePostTaskCheckpoint: boolean;
|
|
64
96
|
requireApprovalForPush: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Narrow Refinery opt-in: when validation and patch-equivalence have passed,
|
|
99
|
+
* allow Refinery to publish submodule gitlink commits to each submodule's
|
|
100
|
+
* configured remote main branch with a non-force push, then verify reachability.
|
|
101
|
+
* Defaults to false; root branch pushes/merges are not affected.
|
|
102
|
+
*/
|
|
103
|
+
allowAutoPublishSubmoduleMainCommits?: boolean;
|
|
65
104
|
requireApprovalForDestructiveGit: boolean;
|
|
66
105
|
dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
|
|
67
106
|
maxParallelTasks: number;
|
|
@@ -126,6 +165,7 @@ export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
|
|
|
126
165
|
requirePreTaskCheckpoint: false,
|
|
127
166
|
requirePostTaskCheckpoint: true,
|
|
128
167
|
requireApprovalForPush: true,
|
|
168
|
+
allowAutoPublishSubmoduleMainCommits: false,
|
|
129
169
|
requireApprovalForDestructiveGit: true,
|
|
130
170
|
dirtyWorkspaceBehavior: 'warn',
|
|
131
171
|
maxParallelTasks: 2,
|
|
@@ -229,6 +269,7 @@ export interface LocalMeshEntry {
|
|
|
229
269
|
defaultBranch?: string;
|
|
230
270
|
policy: RepoMeshPolicy;
|
|
231
271
|
coordinator: RepoMeshCoordinatorConfig;
|
|
272
|
+
meshHost?: RepoMeshHostMetadata;
|
|
232
273
|
nodes: LocalMeshNodeEntry[];
|
|
233
274
|
createdAt: string;
|
|
234
275
|
updatedAt: string;
|
|
@@ -251,6 +292,7 @@ export interface LocalMeshNodeEntry {
|
|
|
251
292
|
clonedFromNodeId?: string;
|
|
252
293
|
/** Optional associated/external repos configured as node metadata. */
|
|
253
294
|
relatedRepos?: RepoMeshRelatedRepo[];
|
|
295
|
+
role?: RepoMeshDaemonRole;
|
|
254
296
|
}
|
|
255
297
|
|
|
256
298
|
// ─── Mesh Status (runtime, not persisted) ───────
|
|
@@ -259,7 +301,9 @@ export interface RepoMeshStatus {
|
|
|
259
301
|
meshId: string;
|
|
260
302
|
meshName: string;
|
|
261
303
|
repoIdentity: string;
|
|
304
|
+
defaultBranch?: string;
|
|
262
305
|
refreshedAt: string;
|
|
306
|
+
meshHost?: RepoMeshHostStatus;
|
|
263
307
|
nodes: RepoMeshNodeStatus[];
|
|
264
308
|
queue?: RepoMeshQueueStatus;
|
|
265
309
|
ledger?: RepoMeshLedgerStatus;
|
|
@@ -300,11 +344,18 @@ export interface RepoMeshNodeStatus {
|
|
|
300
344
|
repoRoot?: string;
|
|
301
345
|
daemonId?: string;
|
|
302
346
|
machineId?: string;
|
|
347
|
+
role?: RepoMeshDaemonRole;
|
|
303
348
|
machineStatus?: string;
|
|
304
349
|
isLocalWorktree?: boolean;
|
|
305
350
|
worktreeBranch?: string;
|
|
306
351
|
health: RepoMeshNodeHealth;
|
|
307
352
|
git?: GitRepoStatus;
|
|
353
|
+
/**
|
|
354
|
+
* True when the selected coordinator has evidence that a peer git probe is still
|
|
355
|
+
* in flight or just timed out during initial mesh handshake, so callers should
|
|
356
|
+
* treat missing git data as pending instead of authoritative absence.
|
|
357
|
+
*/
|
|
358
|
+
gitProbePending?: boolean;
|
|
308
359
|
providers: string[];
|
|
309
360
|
activeSessions: string[];
|
|
310
361
|
activeSessionDetails?: RepoMeshSessionStatus[];
|
package/src/status/reporter.ts
CHANGED
|
@@ -51,6 +51,8 @@ export class DaemonStatusReporter {
|
|
|
51
51
|
private lastStatusSentAt = 0;
|
|
52
52
|
private statusPendingThrottle = false;
|
|
53
53
|
private lastP2PStatusHash = '';
|
|
54
|
+
private lastP2PStatusSentAt: number = 0;
|
|
55
|
+
private p2pDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
54
56
|
private lastServerStatusHash = '';
|
|
55
57
|
private lastStatusSummary = '';
|
|
56
58
|
|
|
@@ -355,7 +357,20 @@ export class DaemonStatusReporter {
|
|
|
355
357
|
: { ...hashTarget, sessions };
|
|
356
358
|
const h = this.simpleHash(JSON.stringify(hashPayload));
|
|
357
359
|
if (h !== this.lastP2PStatusHash) {
|
|
360
|
+
const now = Date.now();
|
|
361
|
+
// Rate limit: max 1 per 500ms
|
|
362
|
+
if (this.lastP2PStatusSentAt && now - this.lastP2PStatusSentAt < 500) {
|
|
363
|
+
if (!this.p2pDebounceTimer) {
|
|
364
|
+
this.p2pDebounceTimer = setTimeout(() => {
|
|
365
|
+
this.p2pDebounceTimer = null;
|
|
366
|
+
this.sendUnifiedStatusReport({ reason: 'p2p_debounce' });
|
|
367
|
+
}, 500);
|
|
368
|
+
}
|
|
369
|
+
return false; // Dropped for now, but will trigger later
|
|
370
|
+
}
|
|
371
|
+
|
|
358
372
|
this.lastP2PStatusHash = h;
|
|
373
|
+
this.lastP2PStatusSentAt = now;
|
|
359
374
|
this.deps.p2p?.sendStatus(payload);
|
|
360
375
|
return true;
|
|
361
376
|
}
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import * as os from 'os';
|
|
14
|
-
import {
|
|
14
|
+
import { exec } from 'child_process';
|
|
15
|
+
import { promisify } from 'util';
|
|
16
|
+
|
|
17
|
+
const execAsync = promisify(exec);
|
|
15
18
|
|
|
16
19
|
export interface HostMemorySnapshot {
|
|
17
20
|
totalMem: number;
|
|
@@ -21,19 +24,22 @@ export interface HostMemorySnapshot {
|
|
|
21
24
|
availableMem: number;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
let cachedDarwinAvail: number | null = null;
|
|
28
|
+
let darwinMemoryInterval: NodeJS.Timeout | null = null;
|
|
29
|
+
|
|
30
|
+
async function updateDarwinMemoryCache() {
|
|
31
|
+
if (os.platform() !== 'darwin') return;
|
|
26
32
|
try {
|
|
27
|
-
const
|
|
33
|
+
const { stdout } = await execAsync('vm_stat', {
|
|
28
34
|
encoding: 'utf-8',
|
|
29
35
|
timeout: 4000,
|
|
30
36
|
maxBuffer: 256 * 1024,
|
|
31
37
|
});
|
|
32
|
-
const pageSizeMatch =
|
|
38
|
+
const pageSizeMatch = stdout.match(/page size of (\d+)\s*bytes/i);
|
|
33
39
|
const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : 4096;
|
|
34
40
|
|
|
35
41
|
const counts: Record<string, number> = {};
|
|
36
|
-
for (const line of
|
|
42
|
+
for (const line of stdout.split('\n')) {
|
|
37
43
|
const m = line.match(/^\s*Pages\s+([^:]+):\s+([\d,]+)\s*\.?/);
|
|
38
44
|
if (!m) continue;
|
|
39
45
|
const key = m[1].trim().toLowerCase().replace(/\s+/g, '_');
|
|
@@ -49,17 +55,28 @@ function parseDarwinAvailableBytes(totalMem: number): number | null {
|
|
|
49
55
|
|
|
50
56
|
const availPages = free + inactive + speculative + purgeable + fileBacked;
|
|
51
57
|
const bytes = availPages * pageSize;
|
|
52
|
-
|
|
53
|
-
return Math.min(bytes, totalMem);
|
|
58
|
+
cachedDarwinAvail = Number.isFinite(bytes) && bytes >= 0 ? Math.min(bytes, os.totalmem()) : null;
|
|
54
59
|
} catch {
|
|
55
|
-
|
|
60
|
+
// silently fallback
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export function getHostMemorySnapshot(): HostMemorySnapshot {
|
|
65
|
+
if (os.platform() === 'darwin' && !darwinMemoryInterval) {
|
|
66
|
+
updateDarwinMemoryCache();
|
|
67
|
+
darwinMemoryInterval = setInterval(updateDarwinMemoryCache, 3000);
|
|
68
|
+
darwinMemoryInterval.unref();
|
|
69
|
+
}
|
|
70
|
+
|
|
60
71
|
const totalMem = os.totalmem();
|
|
61
72
|
const freeMem = os.freemem();
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
const availableMem = os.platform() === 'darwin'
|
|
74
|
+
? (cachedDarwinAvail ?? freeMem)
|
|
75
|
+
: freeMem;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
totalMem,
|
|
79
|
+
freeMem,
|
|
80
|
+
availableMem,
|
|
81
|
+
};
|
|
65
82
|
}
|