@adhdev/daemon-core 0.9.66 → 0.9.67
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/config/mesh-config.d.ts +47 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +346 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +335 -17
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/coordinator-prompt.d.ts +18 -0
- package/dist/mesh/mesh-sync.d.ts +51 -0
- package/dist/repo-mesh-types.d.ts +168 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/config/mesh-config.ts +228 -0
- package/src/index.ts +33 -0
- package/src/mesh/coordinator-prompt.ts +156 -0
- package/src/mesh/mesh-sync.ts +109 -0
- package/src/repo-mesh-types.ts +212 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mesh Sync — Sync local mesh config to/from cloud D1
|
|
3
|
+
*
|
|
4
|
+
* When cloud is available, this module pushes local mesh config
|
|
5
|
+
* to the server and pulls remote meshes that were created from
|
|
6
|
+
* other machines. The local ~/.adhdev/meshes.json remains the
|
|
7
|
+
* canonical source; cloud is a persistence/relay layer.
|
|
8
|
+
*
|
|
9
|
+
* This is called lazily (not on daemon startup) — only when the
|
|
10
|
+
* user explicitly opens the mesh page or runs `adhdev mesh sync`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { listMeshes, getMesh, createMesh, deleteMesh, addNode, removeNode } from '../config/mesh-config.js';
|
|
14
|
+
import type { LocalMeshEntry, LocalMeshNodeEntry } from '../repo-mesh-types.js';
|
|
15
|
+
|
|
16
|
+
export interface MeshSyncTransport {
|
|
17
|
+
/** GET /api/v1/repo-meshes */
|
|
18
|
+
listRemoteMeshes(): Promise<{ meshes: RemoteMeshRecord[] }>;
|
|
19
|
+
/** POST /api/v1/repo-meshes */
|
|
20
|
+
createRemoteMesh(data: {
|
|
21
|
+
name: string;
|
|
22
|
+
repo_identity: string;
|
|
23
|
+
repo_remote_url?: string;
|
|
24
|
+
default_branch?: string;
|
|
25
|
+
policy?: string;
|
|
26
|
+
}): Promise<{ mesh: RemoteMeshRecord }>;
|
|
27
|
+
/** DELETE /api/v1/repo-meshes/:id */
|
|
28
|
+
deleteRemoteMesh(meshId: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RemoteMeshRecord {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
repo_identity: string;
|
|
35
|
+
repo_remote_url: string | null;
|
|
36
|
+
default_branch: string | null;
|
|
37
|
+
policy: string;
|
|
38
|
+
status: string;
|
|
39
|
+
created_at: string;
|
|
40
|
+
updated_at: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MeshSyncResult {
|
|
44
|
+
pushed: number;
|
|
45
|
+
pulled: number;
|
|
46
|
+
deleted: number;
|
|
47
|
+
errors: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Push local meshes to cloud (upsert by repo_identity).
|
|
52
|
+
* Pull remote meshes that don't exist locally.
|
|
53
|
+
*/
|
|
54
|
+
export async function syncMeshes(transport: MeshSyncTransport): Promise<MeshSyncResult> {
|
|
55
|
+
const result: MeshSyncResult = { pushed: 0, pulled: 0, deleted: 0, errors: [] };
|
|
56
|
+
|
|
57
|
+
let remoteMeshes: RemoteMeshRecord[];
|
|
58
|
+
try {
|
|
59
|
+
const res = await transport.listRemoteMeshes();
|
|
60
|
+
remoteMeshes = res.meshes;
|
|
61
|
+
} catch (e: any) {
|
|
62
|
+
result.errors.push(`Failed to list remote meshes: ${e.message}`);
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const localMeshes = listMeshes();
|
|
67
|
+
const remoteByIdentity = new Map(remoteMeshes.map(m => [m.repo_identity, m]));
|
|
68
|
+
const localByIdentity = new Map(localMeshes.map(m => [m.repoIdentity, m]));
|
|
69
|
+
|
|
70
|
+
// Push: local meshes not in cloud
|
|
71
|
+
for (const local of localMeshes) {
|
|
72
|
+
if (!remoteByIdentity.has(local.repoIdentity)) {
|
|
73
|
+
try {
|
|
74
|
+
await transport.createRemoteMesh({
|
|
75
|
+
name: local.name,
|
|
76
|
+
repo_identity: local.repoIdentity,
|
|
77
|
+
repo_remote_url: local.repoRemoteUrl,
|
|
78
|
+
default_branch: local.defaultBranch,
|
|
79
|
+
policy: JSON.stringify(local.policy),
|
|
80
|
+
});
|
|
81
|
+
result.pushed++;
|
|
82
|
+
} catch (e: any) {
|
|
83
|
+
result.errors.push(`Push failed for "${local.name}": ${e.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Pull: remote meshes not in local
|
|
89
|
+
for (const remote of remoteMeshes) {
|
|
90
|
+
if (!localByIdentity.has(remote.repo_identity)) {
|
|
91
|
+
try {
|
|
92
|
+
let policy;
|
|
93
|
+
try { policy = JSON.parse(remote.policy); } catch { policy = undefined; }
|
|
94
|
+
createMesh({
|
|
95
|
+
name: remote.name,
|
|
96
|
+
repoIdentity: remote.repo_identity,
|
|
97
|
+
repoRemoteUrl: remote.repo_remote_url || undefined,
|
|
98
|
+
defaultBranch: remote.default_branch || undefined,
|
|
99
|
+
policy,
|
|
100
|
+
});
|
|
101
|
+
result.pulled++;
|
|
102
|
+
} catch (e: any) {
|
|
103
|
+
result.errors.push(`Pull failed for "${remote.name}": ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo Mesh Types — Cross-package type definitions for repo-scoped orchestration
|
|
3
|
+
*
|
|
4
|
+
* A Repo Mesh is a repo-scoped execution environment that groups
|
|
5
|
+
* machines/workspaces around one Git repository identity. A coordinator
|
|
6
|
+
* agent delegates work to mesh nodes via natural conversation.
|
|
7
|
+
*
|
|
8
|
+
* These types are OSS-level and usable without cloud infrastructure.
|
|
9
|
+
* Import via: import type { ... } from '@adhdev/daemon-core/repo-mesh-types'
|
|
10
|
+
*
|
|
11
|
+
* IMPORTANT: This file must remain runtime-free (types only).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { GitRepoStatus, GitCompactSummary } from './git/git-types.js';
|
|
15
|
+
|
|
16
|
+
// ─── Core Mesh Types ────────────────────────────
|
|
17
|
+
|
|
18
|
+
export interface RepoMesh {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
repoIdentity: string;
|
|
22
|
+
repoRemoteUrl?: string;
|
|
23
|
+
defaultBranch?: string;
|
|
24
|
+
policy: RepoMeshPolicy;
|
|
25
|
+
coordinator: RepoMeshCoordinatorConfig;
|
|
26
|
+
projectContext: ProjectContextSnapshot;
|
|
27
|
+
nodes: RepoMeshNode[];
|
|
28
|
+
status: 'active' | 'archived' | 'deleted';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RepoMeshNode {
|
|
32
|
+
id: string;
|
|
33
|
+
daemonId: string;
|
|
34
|
+
machineId?: string;
|
|
35
|
+
machineLabel: string;
|
|
36
|
+
workspace: string;
|
|
37
|
+
repoRoot?: string;
|
|
38
|
+
git?: GitCompactSummary;
|
|
39
|
+
providers: string[];
|
|
40
|
+
detectedCapabilities: RepoMeshNodeCapabilities;
|
|
41
|
+
userOverrides: Partial<RepoMeshNodeCapabilities>;
|
|
42
|
+
effectiveCapabilities: RepoMeshNodeCapabilities;
|
|
43
|
+
policy: RepoMeshNodePolicy;
|
|
44
|
+
health: RepoMeshNodeHealth;
|
|
45
|
+
status: 'enabled' | 'disabled' | 'removed';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type RepoMeshNodeHealth =
|
|
49
|
+
| 'online'
|
|
50
|
+
| 'offline'
|
|
51
|
+
| 'degraded'
|
|
52
|
+
| 'dirty'
|
|
53
|
+
| 'wrong_branch'
|
|
54
|
+
| 'unknown';
|
|
55
|
+
|
|
56
|
+
// ─── Policy Types ───────────────────────────────
|
|
57
|
+
|
|
58
|
+
export interface RepoMeshPolicy {
|
|
59
|
+
requirePreTaskCheckpoint: boolean;
|
|
60
|
+
requirePostTaskCheckpoint: boolean;
|
|
61
|
+
requireApprovalForPush: boolean;
|
|
62
|
+
requireApprovalForDestructiveGit: boolean;
|
|
63
|
+
dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
|
|
64
|
+
maxParallelTasks: number;
|
|
65
|
+
allowedProviders?: string[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface RepoMeshNodePolicy {
|
|
69
|
+
readOnly?: boolean;
|
|
70
|
+
canPush?: boolean;
|
|
71
|
+
maxConcurrentSessions?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
|
|
75
|
+
requirePreTaskCheckpoint: false,
|
|
76
|
+
requirePostTaskCheckpoint: true,
|
|
77
|
+
requireApprovalForPush: true,
|
|
78
|
+
requireApprovalForDestructiveGit: true,
|
|
79
|
+
dirtyWorkspaceBehavior: 'warn',
|
|
80
|
+
maxParallelTasks: 2,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ─── Capabilities ───────────────────────────────
|
|
84
|
+
|
|
85
|
+
export interface RepoMeshNodeCapabilities {
|
|
86
|
+
platform?: string;
|
|
87
|
+
packageManagers?: string[];
|
|
88
|
+
detectedCommands?: DetectedCommand[];
|
|
89
|
+
canRunLongJobs?: boolean;
|
|
90
|
+
canRunDocker?: boolean;
|
|
91
|
+
canRunBrowserE2E?: boolean;
|
|
92
|
+
canAccessSecrets?: boolean;
|
|
93
|
+
canPush?: boolean;
|
|
94
|
+
readOnly?: boolean;
|
|
95
|
+
userLabels?: string[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface DetectedCommand {
|
|
99
|
+
command: string;
|
|
100
|
+
sourcePath: string;
|
|
101
|
+
confidence: 'high' | 'medium' | 'low';
|
|
102
|
+
requiresApproval?: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Project Context ────────────────────────────
|
|
106
|
+
|
|
107
|
+
export interface ProjectContextSnapshot {
|
|
108
|
+
version: number;
|
|
109
|
+
generatedAt: string;
|
|
110
|
+
sources: ProjectContextSource[];
|
|
111
|
+
repo: {
|
|
112
|
+
identity: string;
|
|
113
|
+
remoteUrl?: string;
|
|
114
|
+
defaultBranch?: string;
|
|
115
|
+
currentBranches: string[];
|
|
116
|
+
};
|
|
117
|
+
layout: {
|
|
118
|
+
packageManager?: string;
|
|
119
|
+
workspaceFiles: string[];
|
|
120
|
+
packageRoots: string[];
|
|
121
|
+
likelyEntryPoints: string[];
|
|
122
|
+
};
|
|
123
|
+
commands: {
|
|
124
|
+
build?: DetectedCommand[];
|
|
125
|
+
test?: DetectedCommand[];
|
|
126
|
+
typecheck?: DetectedCommand[];
|
|
127
|
+
lint?: DetectedCommand[];
|
|
128
|
+
e2e?: DetectedCommand[];
|
|
129
|
+
};
|
|
130
|
+
instructions: {
|
|
131
|
+
files: string[];
|
|
132
|
+
summary: string;
|
|
133
|
+
};
|
|
134
|
+
conventions: {
|
|
135
|
+
pathHints: string[];
|
|
136
|
+
validationNotes: string[];
|
|
137
|
+
riskyAreas: string[];
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface ProjectContextSource {
|
|
142
|
+
kind: 'daemon_status' | 'git' | 'project_file' | 'instruction_file' | 'user_override' | 'probe_error';
|
|
143
|
+
nodeId?: string;
|
|
144
|
+
path?: string;
|
|
145
|
+
observedAt: string;
|
|
146
|
+
confidence: 'high' | 'medium' | 'low';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Coordinator Config ─────────────────────────
|
|
150
|
+
|
|
151
|
+
export interface RepoMeshCoordinatorConfig {
|
|
152
|
+
/** Provider to use for coordinator session (e.g. 'claude-cli', 'cursor') */
|
|
153
|
+
providerType?: string;
|
|
154
|
+
/** Preferred node to run coordinator on (null = auto) */
|
|
155
|
+
preferredNodeId?: string;
|
|
156
|
+
/** Additional system prompt context for coordinator */
|
|
157
|
+
systemPromptSuffix?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Local Mesh Config (OSS standalone) ─────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Local mesh configuration stored in ~/.adhdev/meshes.json
|
|
164
|
+
* Used by OSS standalone mode without cloud infrastructure.
|
|
165
|
+
*/
|
|
166
|
+
export interface LocalMeshConfig {
|
|
167
|
+
meshes: LocalMeshEntry[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface LocalMeshEntry {
|
|
171
|
+
id: string;
|
|
172
|
+
name: string;
|
|
173
|
+
repoIdentity: string;
|
|
174
|
+
repoRemoteUrl?: string;
|
|
175
|
+
defaultBranch?: string;
|
|
176
|
+
policy: RepoMeshPolicy;
|
|
177
|
+
coordinator: RepoMeshCoordinatorConfig;
|
|
178
|
+
nodes: LocalMeshNodeEntry[];
|
|
179
|
+
createdAt: string;
|
|
180
|
+
updatedAt: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface LocalMeshNodeEntry {
|
|
184
|
+
id: string;
|
|
185
|
+
workspace: string;
|
|
186
|
+
repoRoot?: string;
|
|
187
|
+
userOverrides: Partial<RepoMeshNodeCapabilities>;
|
|
188
|
+
policy: RepoMeshNodePolicy;
|
|
189
|
+
/** For single-machine mesh: same daemon, different worktree */
|
|
190
|
+
isLocalWorktree?: boolean;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ─── Mesh Status (runtime, not persisted) ───────
|
|
194
|
+
|
|
195
|
+
export interface RepoMeshStatus {
|
|
196
|
+
meshId: string;
|
|
197
|
+
meshName: string;
|
|
198
|
+
repoIdentity: string;
|
|
199
|
+
refreshedAt: string;
|
|
200
|
+
nodes: RepoMeshNodeStatus[];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface RepoMeshNodeStatus {
|
|
204
|
+
nodeId: string;
|
|
205
|
+
machineLabel: string;
|
|
206
|
+
workspace: string;
|
|
207
|
+
health: RepoMeshNodeHealth;
|
|
208
|
+
git?: GitRepoStatus;
|
|
209
|
+
providers: string[];
|
|
210
|
+
activeSessions: string[];
|
|
211
|
+
error?: string;
|
|
212
|
+
}
|