@maintainer-pro/ai-bridge 0.1.29 → 0.1.30
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/package.json +2 -2
- package/src/daemon.mjs +169 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maintainer-pro/ai-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "Local bridge daemon that pairs a machine to Maintainer Pro and configures multiple client sandboxes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"maintainer-pro",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">=22"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@maintainer-pro/ai-cli": "^0.1.
|
|
31
|
+
"@maintainer-pro/ai-cli": "^0.1.16",
|
|
32
32
|
"@maintainer-pro/ai-server": "^0.1.8"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/daemon.mjs
CHANGED
|
@@ -451,13 +451,30 @@ async function loadAiCli() {
|
|
|
451
451
|
/** @type {{ at: number, ids: string[] } | null} */
|
|
452
452
|
let cliProviderCache = null;
|
|
453
453
|
|
|
454
|
-
|
|
455
|
-
|
|
454
|
+
function missingCliSetupText(folder) {
|
|
455
|
+
const folderLine = folder
|
|
456
|
+
? `No coding agent CLI is available in this folder:\n ${folder}`
|
|
457
|
+
: "No coding agent CLI found on this computer.";
|
|
458
|
+
return [
|
|
459
|
+
`${folderLine} Chat will not work until one is installed and reachable from this folder.`,
|
|
460
|
+
"",
|
|
461
|
+
" • Cursor Agent CLI — https://cursor.com (command: agent)",
|
|
462
|
+
" • Claude Code — https://docs.anthropic.com/en/docs/claude-code (command: claude)",
|
|
463
|
+
" • Antigravity — https://antigravity.google/ (command: agy)",
|
|
464
|
+
"",
|
|
465
|
+
"Then either sign in and re-run the Maintainer Pro command from a terminal where that CLI works, or set the full path in this folder's .env:",
|
|
466
|
+
" AI_CLI_PROVIDER=cursor",
|
|
467
|
+
" AI_CLI_COMMAND=C:\\\\Users\\\\you\\\\AppData\\\\Local\\\\cursor-agent\\\\agent.cmd",
|
|
468
|
+
].join("\n");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function detectCliProviders(force = false) {
|
|
472
|
+
if (!force && cliProviderCache && Date.now() - cliProviderCache.at < 60_000) {
|
|
456
473
|
return cliProviderCache.ids;
|
|
457
474
|
}
|
|
458
475
|
try {
|
|
459
|
-
const
|
|
460
|
-
const provider = await resolveProvider({ preference: "auto" });
|
|
476
|
+
const cli = await loadAiCli();
|
|
477
|
+
const provider = await cli.resolveProvider({ preference: "auto" });
|
|
461
478
|
cliProviderCache = { at: Date.now(), ids: [provider.id] };
|
|
462
479
|
return cliProviderCache.ids;
|
|
463
480
|
} catch {
|
|
@@ -466,6 +483,65 @@ async function detectCliProviders() {
|
|
|
466
483
|
}
|
|
467
484
|
}
|
|
468
485
|
|
|
486
|
+
async function withCliProbe(result, folders) {
|
|
487
|
+
const roots = [...new Set((folders || []).filter(Boolean).map((row) => path.resolve(row)))];
|
|
488
|
+
let probe = { available: false };
|
|
489
|
+
for (const folder of roots) {
|
|
490
|
+
probe = await probeFolderCli(folder);
|
|
491
|
+
if (probe.available) break;
|
|
492
|
+
}
|
|
493
|
+
const folder = probe.folder || roots[0] || result.folderPath;
|
|
494
|
+
if (probe.available) {
|
|
495
|
+
log(
|
|
496
|
+
`coding agent CLI ready in ${folder} (${probe.providerId || "auto"}${
|
|
497
|
+
probe.source ? ` via ${probe.source}` : ""
|
|
498
|
+
})`
|
|
499
|
+
);
|
|
500
|
+
} else if (folder) {
|
|
501
|
+
activity(
|
|
502
|
+
result.sandboxId,
|
|
503
|
+
"warn",
|
|
504
|
+
`No coding agent CLI in ${folder}. Chat needs agent, claude, or agy — or AI_CLI_COMMAND in this folder's .env.`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
...result,
|
|
509
|
+
missingCli: !probe.available,
|
|
510
|
+
cliProvider: probe.providerId || null,
|
|
511
|
+
cliCommand: probe.command || null,
|
|
512
|
+
warning: !probe.available
|
|
513
|
+
? [result.warning, missingCliSetupText(folder)].filter(Boolean).join("\n\n")
|
|
514
|
+
: result.warning,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
async function probeFolderCli(folder) {
|
|
519
|
+
const root = folder ? path.resolve(folder) : "";
|
|
520
|
+
if (!root) return { available: false };
|
|
521
|
+
try {
|
|
522
|
+
const cli = await loadAiCli();
|
|
523
|
+
if (typeof cli.probeCliInFolder === "function") {
|
|
524
|
+
return await cli.probeCliInFolder(root);
|
|
525
|
+
}
|
|
526
|
+
const provider = await cli.resolveProvider({
|
|
527
|
+
preference: "auto",
|
|
528
|
+
workspaceDir: root,
|
|
529
|
+
});
|
|
530
|
+
return { available: true, providerId: provider.id, folder: root };
|
|
531
|
+
} catch {
|
|
532
|
+
return { available: false, folder: root };
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function warnIfMissingCli() {
|
|
537
|
+
const ids = await detectCliProviders(true);
|
|
538
|
+
if (ids.length) {
|
|
539
|
+
log(`coding agent CLI ready (${ids.join(", ")})`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
warn(missingCliSetupText());
|
|
543
|
+
}
|
|
544
|
+
|
|
469
545
|
async function resolveWorkspaceHostApps(ws, opts = {}) {
|
|
470
546
|
const folders = workspaceFolders(ws);
|
|
471
547
|
const folder = folders[0] || path.resolve(ws.folderPath || "");
|
|
@@ -3714,11 +3790,18 @@ async function startAiServerForWorkspace(ws, opts = {}) {
|
|
|
3714
3790
|
|
|
3715
3791
|
const storeEnv = await loadSandboxStoreEnv(ws, cfg);
|
|
3716
3792
|
const folderEnv = readProjectEnvValues(folder);
|
|
3793
|
+
const cliProbe = await probeFolderCli(folder);
|
|
3717
3794
|
const dataDir = dataDirFor(folder, ws.sandboxId);
|
|
3718
3795
|
const instanceEnv = {
|
|
3719
3796
|
...folderEnv,
|
|
3720
3797
|
...overrideEnv,
|
|
3721
3798
|
...storeEnv,
|
|
3799
|
+
...(cliProbe.command && !folderEnv.AI_CLI_COMMAND
|
|
3800
|
+
? { AI_CLI_COMMAND: cliProbe.command }
|
|
3801
|
+
: {}),
|
|
3802
|
+
...(cliProbe.providerId && !folderEnv.AI_CLI_PROVIDER
|
|
3803
|
+
? { AI_CLI_PROVIDER: cliProbe.providerId }
|
|
3804
|
+
: {}),
|
|
3722
3805
|
AI_SERVER_URL: localAi,
|
|
3723
3806
|
CORS_ORIGIN: overrideEnv.CORS_ORIGIN || folderEnv.CORS_ORIGIN || "",
|
|
3724
3807
|
CORS_ORIGINS: overrideEnv.CORS_ORIGINS || folderEnv.CORS_ORIGINS || "",
|
|
@@ -4448,7 +4531,7 @@ async function setupWorkspace(cfg, action) {
|
|
|
4448
4531
|
const host = workspaceHostReport(existingWs);
|
|
4449
4532
|
const aiServerUp = await isChatServerOnPort(existingWs.port);
|
|
4450
4533
|
log(`setup added folder ${resolved} sandbox=${shortId(sandboxId)}`);
|
|
4451
|
-
return {
|
|
4534
|
+
return await withCliProbe({
|
|
4452
4535
|
sandboxId,
|
|
4453
4536
|
folderPath: existingWs.folderPath,
|
|
4454
4537
|
extraFolders: extra,
|
|
@@ -4476,7 +4559,9 @@ async function setupWorkspace(cfg, action) {
|
|
|
4476
4559
|
reasons: ports.reasons || [],
|
|
4477
4560
|
projectInfo: existingWs.projectInfo || null,
|
|
4478
4561
|
ignorePaths: access.ignorePaths,
|
|
4479
|
-
}
|
|
4562
|
+
},
|
|
4563
|
+
workspaceFolders(existingWs)
|
|
4564
|
+
);
|
|
4480
4565
|
}
|
|
4481
4566
|
|
|
4482
4567
|
const client = configureClient({
|
|
@@ -4566,32 +4651,35 @@ async function setupWorkspace(cfg, action) {
|
|
|
4566
4651
|
);
|
|
4567
4652
|
|
|
4568
4653
|
const host = workspaceHostReport(entry);
|
|
4569
|
-
return
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4654
|
+
return await withCliProbe(
|
|
4655
|
+
{
|
|
4656
|
+
sandboxId,
|
|
4657
|
+
folderPath: entry.folderPath,
|
|
4658
|
+
extraFolders: entry.extraFolders || [],
|
|
4659
|
+
addFolder: false,
|
|
4660
|
+
port: entry.port,
|
|
4661
|
+
appUrl: host.appUrl || entry.appUrl || appUrl,
|
|
4662
|
+
origins: host.origins.length
|
|
4663
|
+
? host.origins
|
|
4664
|
+
: [...new Set([corsOrigin, aiOrigin, entry.appUrl].filter(Boolean))],
|
|
4665
|
+
wroteEnv: false,
|
|
4666
|
+
clientKind: client.kind,
|
|
4667
|
+
clientFiles: client.filesWritten,
|
|
4668
|
+
clientNotes: client.notes,
|
|
4669
|
+
aiServerUp,
|
|
4670
|
+
startedHosts: [],
|
|
4671
|
+
openUrl,
|
|
4672
|
+
processIssues,
|
|
4673
|
+
warning,
|
|
4674
|
+
waitingForStart,
|
|
4675
|
+
needsReview,
|
|
4676
|
+
hostApps: ports.apps || entry.hostApps || [],
|
|
4677
|
+
reasons: ports.reasons || [],
|
|
4678
|
+
projectInfo,
|
|
4679
|
+
ignorePaths: access.ignorePaths,
|
|
4680
|
+
},
|
|
4681
|
+
workspaceFolders(entry)
|
|
4682
|
+
);
|
|
4595
4683
|
}
|
|
4596
4684
|
|
|
4597
4685
|
async function proposeSetupForWorkspace(ws, cfg, opts = {}) {
|
|
@@ -4738,16 +4826,28 @@ async function runActions(cfg, actions) {
|
|
|
4738
4826
|
};
|
|
4739
4827
|
}
|
|
4740
4828
|
} else if (action.code === "recheck") {
|
|
4829
|
+
cliProviderCache = null;
|
|
4741
4830
|
const sandboxId = action.sandboxId || action.payload?.sandboxId;
|
|
4742
4831
|
const ws = (cfg.workspaces || []).find((w) => w.sandboxId === sandboxId);
|
|
4743
4832
|
if (!ws) {
|
|
4744
4833
|
log(`${label} recheck: no local workspace`);
|
|
4745
4834
|
}
|
|
4835
|
+
const cli = await detectCliProviders(true);
|
|
4836
|
+
const folderProbe = ws
|
|
4837
|
+
? await withCliProbe(
|
|
4838
|
+
{ sandboxId, folderPath: ws.folderPath },
|
|
4839
|
+
workspaceFolders(ws)
|
|
4840
|
+
)
|
|
4841
|
+
: { missingCli: cli.length === 0 };
|
|
4746
4842
|
const hostApps = ws
|
|
4747
4843
|
? await resolveWorkspaceHostApps(ws, { cfg, force: true, allowAi: false })
|
|
4748
4844
|
: null;
|
|
4749
4845
|
result = {
|
|
4750
4846
|
recheckedAt: new Date().toISOString(),
|
|
4847
|
+
cliProviders: cli,
|
|
4848
|
+
missingCli: folderProbe.missingCli === true,
|
|
4849
|
+
cliProvider: folderProbe.cliProvider || null,
|
|
4850
|
+
cliCommand: folderProbe.cliCommand || null,
|
|
4751
4851
|
hostApps: hostApps?.apps || [],
|
|
4752
4852
|
projectInfo: ws?.projectInfo || null,
|
|
4753
4853
|
};
|
|
@@ -5216,18 +5316,41 @@ function adminWsUrl(adminUrl, token) {
|
|
|
5216
5316
|
async function buildIssues(cfg, workspaceStates) {
|
|
5217
5317
|
/** @type {Array<Record<string, unknown>>} */
|
|
5218
5318
|
const issues = [];
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5319
|
+
if (workspaceStates.length) {
|
|
5320
|
+
for (const st of workspaceStates) {
|
|
5321
|
+
const ws = (cfg.workspaces || []).find((row) => row.sandboxId === st.sandboxId);
|
|
5322
|
+
const folders = ws ? workspaceFolders(ws) : [st.folderPath].filter(Boolean);
|
|
5323
|
+
let probe = { available: false, folder: folders[0] || st.folderPath };
|
|
5324
|
+
for (const folder of folders) {
|
|
5325
|
+
probe = await probeFolderCli(folder);
|
|
5326
|
+
if (probe.available) break;
|
|
5327
|
+
}
|
|
5328
|
+
if (probe.available) continue;
|
|
5329
|
+
issues.push({
|
|
5330
|
+
code: "missing_cli",
|
|
5331
|
+
severity: "error",
|
|
5332
|
+
title: "No coding agent CLI in this folder",
|
|
5333
|
+
message: missingCliSetupText(probe.folder || folders[0] || st.folderPath),
|
|
5334
|
+
resolution:
|
|
5335
|
+
"Install a CLI and re-run the Maintainer Pro command from a terminal where it works, or set AI_CLI_COMMAND in this folder's .env, then Check again.",
|
|
5336
|
+
actionCode: "recheck",
|
|
5337
|
+
sandboxId: st.sandboxId,
|
|
5338
|
+
});
|
|
5339
|
+
}
|
|
5340
|
+
} else {
|
|
5341
|
+
const cli = await detectCliProviders();
|
|
5342
|
+
if (!cli.length) {
|
|
5343
|
+
issues.push({
|
|
5344
|
+
code: "missing_cli",
|
|
5345
|
+
severity: "error",
|
|
5346
|
+
title: "No coding agent CLI found",
|
|
5347
|
+
message: missingCliSetupText(),
|
|
5348
|
+
resolution:
|
|
5349
|
+
"Install Cursor (agent), Claude Code (claude), or Antigravity (agy), sign in, then stop and re-run the Maintainer Pro command. Use Check again after it is on PATH.",
|
|
5350
|
+
actionCode: "recheck",
|
|
5351
|
+
sandboxId: null,
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
5231
5354
|
}
|
|
5232
5355
|
for (const st of workspaceStates) {
|
|
5233
5356
|
if (st.aiServerUp || st.appsRunning || st.startingAi) continue;
|
|
@@ -5377,6 +5500,7 @@ async function main() {
|
|
|
5377
5500
|
log(
|
|
5378
5501
|
`online via websocket (presence every ${HEARTBEAT_MS / 1000}s, ping every ${WS_PING_MS / 1000}s; reconnects until stopped)`
|
|
5379
5502
|
);
|
|
5503
|
+
await warnIfMissingCli();
|
|
5380
5504
|
await restoreHostsAfterReconnect(cfg);
|
|
5381
5505
|
|
|
5382
5506
|
/** @type {unknown[]} */
|
|
@@ -5391,6 +5515,7 @@ async function main() {
|
|
|
5391
5515
|
const batch = claimedActions.splice(0, claimedActions.length);
|
|
5392
5516
|
await runActions(cfg, batch);
|
|
5393
5517
|
}
|
|
5518
|
+
await sendPresenceOverWs();
|
|
5394
5519
|
} catch (err) {
|
|
5395
5520
|
warn(err instanceof Error ? err.message : String(err));
|
|
5396
5521
|
} finally {
|