agent-relay 2.1.1 → 2.1.2
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/index.cjs +97 -3
- package/dist/src/cli/index.d.ts +11 -1
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +112 -110
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +18 -18
- package/packages/api-types/package.json +1 -1
- package/packages/benchmark/package.json +4 -4
- package/packages/bridge/package.json +8 -8
- package/packages/cli-tester/package.json +1 -1
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +2 -2
- package/packages/daemon/dist/connection.d.ts +5 -0
- package/packages/daemon/dist/connection.d.ts.map +1 -1
- package/packages/daemon/dist/connection.js +19 -1
- package/packages/daemon/dist/connection.js.map +1 -1
- package/packages/daemon/dist/server.js +2 -2
- package/packages/daemon/dist/server.js.map +1 -1
- package/packages/daemon/package.json +12 -12
- package/packages/daemon/src/connection.ts +22 -1
- package/packages/daemon/src/router.test.ts +32 -0
- package/packages/daemon/src/server.ts +2 -2
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +3 -3
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/dist/types.d.ts +5 -0
- package/packages/protocol/dist/types.d.ts.map +1 -1
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/src/types.ts +5 -0
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/dist/client.d.ts +6 -0
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +1 -0
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/client.ts +7 -0
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/packages/wrapper/dist/base-wrapper.d.ts +5 -0
- package/packages/wrapper/dist/base-wrapper.d.ts.map +1 -1
- package/packages/wrapper/dist/base-wrapper.js +14 -1
- package/packages/wrapper/dist/base-wrapper.js.map +1 -1
- package/packages/wrapper/dist/shared.d.ts +36 -0
- package/packages/wrapper/dist/shared.d.ts.map +1 -1
- package/packages/wrapper/dist/shared.js +123 -2
- package/packages/wrapper/dist/shared.js.map +1 -1
- package/packages/wrapper/dist/tmux-wrapper.js +1 -1
- package/packages/wrapper/dist/tmux-wrapper.js.map +1 -1
- package/packages/wrapper/package.json +6 -6
- package/packages/wrapper/src/base-wrapper.ts +15 -0
- package/packages/wrapper/src/shared.test.ts +156 -11
- package/packages/wrapper/src/shared.ts +154 -2
- package/packages/wrapper/src/tmux-wrapper.ts +1 -1
package/dist/index.cjs
CHANGED
|
@@ -35497,6 +35497,7 @@ __export(index_exports, {
|
|
|
35497
35497
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
35498
35498
|
PROVIDER_AUTH_PATTERNS: () => PROVIDER_AUTH_PATTERNS,
|
|
35499
35499
|
PostgresDLQAdapter: () => PostgresDLQAdapter,
|
|
35500
|
+
RESERVED_AGENT_NAMES: () => RESERVED_AGENT_NAMES,
|
|
35500
35501
|
RelayClient: () => RelayClient,
|
|
35501
35502
|
RelayEvent: () => RelayEvent,
|
|
35502
35503
|
RelayPtyOrchestrator: () => RelayPtyOrchestrator,
|
|
@@ -37229,13 +37230,81 @@ function stripAnsi2(str) {
|
|
|
37229
37230
|
function sleep(ms) {
|
|
37230
37231
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
37231
37232
|
}
|
|
37233
|
+
var AUTO_SUGGEST_PATTERNS = {
|
|
37234
|
+
// Dim text styling - commonly used for ghost text
|
|
37235
|
+
dim: /\x1B\[2m/,
|
|
37236
|
+
// Bright black (dark gray) - common for suggestions
|
|
37237
|
+
brightBlack: /\x1B\[90m/,
|
|
37238
|
+
// 256-color grays (8 is dark gray, 240-250 are grays)
|
|
37239
|
+
gray256: /\x1B\[38;5;(?:8|24[0-9]|250)m/,
|
|
37240
|
+
// Cursor save (CSI s or ESC 7)
|
|
37241
|
+
cursorSave: /\x1B\[s|\x1B7/,
|
|
37242
|
+
// Cursor restore (CSI u or ESC 8)
|
|
37243
|
+
cursorRestore: /\x1B\[u|\x1B8/,
|
|
37244
|
+
// Italic text - sometimes used for suggestions
|
|
37245
|
+
italic: /\x1B\[3m/
|
|
37246
|
+
};
|
|
37247
|
+
function detectAutoSuggest(output) {
|
|
37248
|
+
const patterns = [];
|
|
37249
|
+
let confidence = 0;
|
|
37250
|
+
if (AUTO_SUGGEST_PATTERNS.dim.test(output)) {
|
|
37251
|
+
patterns.push("dim");
|
|
37252
|
+
confidence += 0.4;
|
|
37253
|
+
}
|
|
37254
|
+
if (AUTO_SUGGEST_PATTERNS.brightBlack.test(output)) {
|
|
37255
|
+
patterns.push("brightBlack");
|
|
37256
|
+
confidence += 0.4;
|
|
37257
|
+
}
|
|
37258
|
+
if (AUTO_SUGGEST_PATTERNS.gray256.test(output)) {
|
|
37259
|
+
patterns.push("gray256");
|
|
37260
|
+
confidence += 0.3;
|
|
37261
|
+
}
|
|
37262
|
+
if (AUTO_SUGGEST_PATTERNS.italic.test(output)) {
|
|
37263
|
+
patterns.push("italic");
|
|
37264
|
+
confidence += 0.2;
|
|
37265
|
+
}
|
|
37266
|
+
const hasCursorSave = AUTO_SUGGEST_PATTERNS.cursorSave.test(output);
|
|
37267
|
+
const hasCursorRestore = AUTO_SUGGEST_PATTERNS.cursorRestore.test(output);
|
|
37268
|
+
if (hasCursorSave && hasCursorRestore) {
|
|
37269
|
+
patterns.push("cursorSaveRestore");
|
|
37270
|
+
confidence += 0.5;
|
|
37271
|
+
} else if (hasCursorSave || hasCursorRestore) {
|
|
37272
|
+
patterns.push(hasCursorSave ? "cursorSave" : "cursorRestore");
|
|
37273
|
+
confidence += 0.2;
|
|
37274
|
+
}
|
|
37275
|
+
confidence = Math.min(confidence, 1);
|
|
37276
|
+
const stripped = stripAnsi2(output);
|
|
37277
|
+
if (patterns.length === 0) {
|
|
37278
|
+
return { isAutoSuggest: false, confidence: 0, patterns, strippedContent: stripped };
|
|
37279
|
+
}
|
|
37280
|
+
const lines = stripped.split("\n").filter((l) => l.trim().length > 0);
|
|
37281
|
+
if (lines.length > 2) {
|
|
37282
|
+
confidence *= 0.5;
|
|
37283
|
+
}
|
|
37284
|
+
const isAutoSuggest = confidence >= 0.4;
|
|
37285
|
+
return { isAutoSuggest, confidence, patterns, strippedContent: stripped };
|
|
37286
|
+
}
|
|
37287
|
+
function shouldIgnoreForIdleDetection(output) {
|
|
37288
|
+
if (!output || output.length === 0) {
|
|
37289
|
+
return true;
|
|
37290
|
+
}
|
|
37291
|
+
const result = detectAutoSuggest(output);
|
|
37292
|
+
if (result.isAutoSuggest) {
|
|
37293
|
+
return true;
|
|
37294
|
+
}
|
|
37295
|
+
const stripped = stripAnsi2(output).trim();
|
|
37296
|
+
if (stripped.length === 0) {
|
|
37297
|
+
return true;
|
|
37298
|
+
}
|
|
37299
|
+
return false;
|
|
37300
|
+
}
|
|
37232
37301
|
function buildInjectionString(msg) {
|
|
37233
37302
|
const sanitizedBody = stripAnsi2(msg.body || "").replace(/[\r\n]+/g, " ").trim();
|
|
37234
37303
|
if (sanitizedBody.startsWith("Relay message from ")) {
|
|
37235
37304
|
return sanitizedBody;
|
|
37236
37305
|
}
|
|
37237
37306
|
const shortId = msg.messageId.substring(0, 8);
|
|
37238
|
-
const displayFrom = msg.from === "
|
|
37307
|
+
const displayFrom = msg.from === "Dashboard" && typeof msg.data?.senderName === "string" ? msg.data.senderName : msg.from;
|
|
37239
37308
|
const threadHint = msg.thread ? ` [thread:${msg.thread}]` : "";
|
|
37240
37309
|
const importanceHint = msg.importance !== void 0 && msg.importance > 75 ? " [!!]" : msg.importance !== void 0 && msg.importance > 50 ? " [!]" : "";
|
|
37241
37310
|
const channelHint = msg.originalTo === "*" ? " [#general] (reply to #general, not sender)" : msg.originalTo?.startsWith("#") ? ` [${msg.originalTo}] (reply to ${msg.originalTo}, not sender)` : "";
|
|
@@ -42483,8 +42552,17 @@ var BaseWrapper = class extends import_node_events2.EventEmitter {
|
|
|
42483
42552
|
/**
|
|
42484
42553
|
* Feed output to the idle and stuck detectors.
|
|
42485
42554
|
* Call this whenever new output is received from the agent.
|
|
42555
|
+
*
|
|
42556
|
+
* Note: Auto-suggestions (ghost text) are filtered out to prevent
|
|
42557
|
+
* false idle resets. Claude Code and other CLIs show suggestions
|
|
42558
|
+
* in gray/dim text with cursor save/restore, which should not
|
|
42559
|
+
* be treated as "real" output for idle detection.
|
|
42486
42560
|
*/
|
|
42487
42561
|
feedIdleDetectorOutput(output) {
|
|
42562
|
+
if (shouldIgnoreForIdleDetection(output)) {
|
|
42563
|
+
this.stuckDetector.onOutput(output);
|
|
42564
|
+
return;
|
|
42565
|
+
}
|
|
42488
42566
|
this.idleDetector.onOutput(output);
|
|
42489
42567
|
this.stuckDetector.onOutput(output);
|
|
42490
42568
|
}
|
|
@@ -52803,6 +52881,16 @@ function generateEventSchemas() {
|
|
|
52803
52881
|
}
|
|
52804
52882
|
|
|
52805
52883
|
// packages/daemon/dist/connection.js
|
|
52884
|
+
var RESERVED_AGENT_NAMES = /* @__PURE__ */ new Set([
|
|
52885
|
+
"Dashboard",
|
|
52886
|
+
// Dashboard system client
|
|
52887
|
+
"cli",
|
|
52888
|
+
// CLI tool
|
|
52889
|
+
"system",
|
|
52890
|
+
// System messages
|
|
52891
|
+
"_router"
|
|
52892
|
+
// Internal router target
|
|
52893
|
+
]);
|
|
52806
52894
|
var DEFAULT_CONFIG10 = {
|
|
52807
52895
|
...DEFAULT_CONNECTION_CONFIG
|
|
52808
52896
|
};
|
|
@@ -52951,7 +53039,12 @@ var Connection = class {
|
|
|
52951
53039
|
this.sendError("BAD_REQUEST", "Unexpected HELLO", false);
|
|
52952
53040
|
return;
|
|
52953
53041
|
}
|
|
52954
|
-
|
|
53042
|
+
const agentName = envelope.payload.agent;
|
|
53043
|
+
if (RESERVED_AGENT_NAMES.has(agentName) && !envelope.payload._isSystemComponent) {
|
|
53044
|
+
this.sendError("BAD_REQUEST", `Agent name "${agentName}" is reserved for system use`, true);
|
|
53045
|
+
return;
|
|
53046
|
+
}
|
|
53047
|
+
this._agentName = agentName;
|
|
52955
53048
|
this._entityType = envelope.payload.entityType;
|
|
52956
53049
|
this._cli = envelope.payload.cli;
|
|
52957
53050
|
this._program = envelope.payload.program;
|
|
@@ -72681,7 +72774,7 @@ var Daemon = class _Daemon {
|
|
|
72681
72774
|
isInternalAgent(name) {
|
|
72682
72775
|
if (name.startsWith("__"))
|
|
72683
72776
|
return true;
|
|
72684
|
-
return name === "Dashboard" || name === "
|
|
72777
|
+
return name === "Dashboard" || name === "cli";
|
|
72685
72778
|
}
|
|
72686
72779
|
/**
|
|
72687
72780
|
* Stop the daemon.
|
|
@@ -78770,6 +78863,7 @@ init_dist();
|
|
|
78770
78863
|
PROTOCOL_VERSION,
|
|
78771
78864
|
PROVIDER_AUTH_PATTERNS,
|
|
78772
78865
|
PostgresDLQAdapter,
|
|
78866
|
+
RESERVED_AGENT_NAMES,
|
|
78773
78867
|
RelayClient,
|
|
78774
78868
|
RelayEvent,
|
|
78775
78869
|
RelayPtyOrchestrator,
|
package/dist/src/cli/index.d.ts
CHANGED
|
@@ -12,5 +12,15 @@
|
|
|
12
12
|
* relay agents - List connected agents
|
|
13
13
|
* relay who - Show currently active agents
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Install agent-relay-snippet to markdown files using prpm.
|
|
17
|
+
* Installs to CLAUDE.md, GEMINI.md, and AGENTS.md.
|
|
18
|
+
* prpm handles idempotency - won't duplicate if already installed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function installRelaySnippets(options?: {
|
|
21
|
+
silent?: boolean;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
success: boolean;
|
|
24
|
+
installed: string[];
|
|
25
|
+
}>;
|
|
16
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AAuGH;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAqC7H"}
|
package/dist/src/cli/index.js
CHANGED
|
@@ -29,59 +29,9 @@ import readline from 'node:readline';
|
|
|
29
29
|
import { promisify } from 'node:util';
|
|
30
30
|
import { exec, spawn as spawnProcess } from 'node:child_process';
|
|
31
31
|
import { fileURLToPath } from 'node:url';
|
|
32
|
-
const RELAY_DASHBOARD_REPO = 'https://github.com/AgentWorkforce/relay-dashboard';
|
|
33
|
-
/**
|
|
34
|
-
* Prompt user to choose how to handle missing dashboard package.
|
|
35
|
-
* Returns: 'npx' | 'install' | 'skip'
|
|
36
|
-
*/
|
|
37
|
-
async function promptDashboardInstall() {
|
|
38
|
-
const rl = readline.createInterface({
|
|
39
|
-
input: process.stdin,
|
|
40
|
-
output: process.stdout,
|
|
41
|
-
});
|
|
42
|
-
console.log(`
|
|
43
|
-
The web dashboard requires @agent-relay/dashboard-server package.
|
|
44
|
-
|
|
45
|
-
How would you like to proceed?
|
|
46
|
-
1. Start with npx (recommended - auto-installs temporarily)
|
|
47
|
-
2. View installation instructions
|
|
48
|
-
3. Skip and continue without dashboard
|
|
49
|
-
`);
|
|
50
|
-
return new Promise((resolve) => {
|
|
51
|
-
rl.question('Choose [1/2/3]: ', (answer) => {
|
|
52
|
-
rl.close();
|
|
53
|
-
const choice = answer.trim();
|
|
54
|
-
if (choice === '1') {
|
|
55
|
-
resolve('npx');
|
|
56
|
-
}
|
|
57
|
-
else if (choice === '2') {
|
|
58
|
-
resolve('install');
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
resolve('skip');
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Show instructions for installing the external dashboard package.
|
|
68
|
-
*/
|
|
69
|
-
function showDashboardInstallInstructions() {
|
|
70
|
-
console.log(`
|
|
71
|
-
To install the dashboard, run:
|
|
72
|
-
|
|
73
|
-
npm install @agent-relay/dashboard-server @agent-relay/dashboard
|
|
74
|
-
|
|
75
|
-
Then restart with:
|
|
76
|
-
|
|
77
|
-
agent-relay up --dashboard
|
|
78
|
-
|
|
79
|
-
For more options, see: ${RELAY_DASHBOARD_REPO}
|
|
80
|
-
`);
|
|
81
|
-
}
|
|
82
32
|
/**
|
|
83
33
|
* Start dashboard via npx (downloads and runs if not installed).
|
|
84
|
-
* Returns the spawned child process and
|
|
34
|
+
* Returns the spawned child process, port, and a promise that resolves when ready.
|
|
85
35
|
*/
|
|
86
36
|
function startDashboardViaNpx(options) {
|
|
87
37
|
console.log('Starting dashboard via npx (this may take a moment on first run)...');
|
|
@@ -100,10 +50,21 @@ function startDashboardViaNpx(options) {
|
|
|
100
50
|
// Pass any additional env vars needed
|
|
101
51
|
},
|
|
102
52
|
});
|
|
53
|
+
// Promise that resolves when dashboard is ready (or after timeout)
|
|
54
|
+
let resolveReady;
|
|
55
|
+
const ready = new Promise((resolve) => {
|
|
56
|
+
resolveReady = resolve;
|
|
57
|
+
// Fallback timeout in case we miss the ready signal
|
|
58
|
+
setTimeout(resolve, 30000);
|
|
59
|
+
});
|
|
103
60
|
// Forward dashboard output with prefix
|
|
104
61
|
dashboardProcess.stdout?.on('data', (data) => {
|
|
105
62
|
const lines = data.toString().split('\n').filter(Boolean);
|
|
106
63
|
for (const line of lines) {
|
|
64
|
+
// Detect when dashboard is ready (listening message)
|
|
65
|
+
if (line.includes('Dashboard:') || line.includes('listening') || line.includes('ready')) {
|
|
66
|
+
resolveReady();
|
|
67
|
+
}
|
|
107
68
|
// Don't duplicate the "Dashboard:" line
|
|
108
69
|
if (!line.includes('Dashboard:')) {
|
|
109
70
|
console.log(`[dashboard] ${line}`);
|
|
@@ -118,13 +79,56 @@ function startDashboardViaNpx(options) {
|
|
|
118
79
|
});
|
|
119
80
|
dashboardProcess.on('error', (err) => {
|
|
120
81
|
console.error('Failed to start dashboard via npx:', err.message);
|
|
82
|
+
resolveReady(); // Resolve to not block forever
|
|
121
83
|
});
|
|
122
84
|
dashboardProcess.on('exit', (code) => {
|
|
85
|
+
resolveReady(); // Resolve on exit to not block forever
|
|
123
86
|
if (code !== 0 && code !== null) {
|
|
124
87
|
console.error(`Dashboard process exited with code ${code}`);
|
|
125
88
|
}
|
|
126
89
|
});
|
|
127
|
-
return { process: dashboardProcess, port: options.port };
|
|
90
|
+
return { process: dashboardProcess, port: options.port, ready };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Install agent-relay-snippet to markdown files using prpm.
|
|
94
|
+
* Installs to CLAUDE.md, GEMINI.md, and AGENTS.md.
|
|
95
|
+
* prpm handles idempotency - won't duplicate if already installed.
|
|
96
|
+
*/
|
|
97
|
+
export async function installRelaySnippets(options) {
|
|
98
|
+
const execAsync = promisify(exec);
|
|
99
|
+
const installed = [];
|
|
100
|
+
const targets = [
|
|
101
|
+
{ location: 'CLAUDE.md', name: 'CLAUDE.md' },
|
|
102
|
+
{ location: 'GEMINI.md', name: 'GEMINI.md' },
|
|
103
|
+
{ location: undefined, name: 'AGENTS.md' }, // Default location
|
|
104
|
+
];
|
|
105
|
+
for (const target of targets) {
|
|
106
|
+
try {
|
|
107
|
+
const args = ['npx', 'prpm', 'install', '@agent-relay/agent-relay-snippet'];
|
|
108
|
+
if (target.location) {
|
|
109
|
+
args.push('--location', target.location);
|
|
110
|
+
}
|
|
111
|
+
await execAsync(args.join(' '), { timeout: 60000 });
|
|
112
|
+
installed.push(target.name);
|
|
113
|
+
if (!options?.silent) {
|
|
114
|
+
console.log(` ✓ Installed to ${target.name}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
// prpm exits with error if already installed or other issues
|
|
119
|
+
// Check if it's an "already exists" situation by looking at stderr
|
|
120
|
+
if (err.stderr?.includes('already') || err.stdout?.includes('already')) {
|
|
121
|
+
if (!options?.silent) {
|
|
122
|
+
console.log(` ✓ ${target.name} (already installed)`);
|
|
123
|
+
}
|
|
124
|
+
installed.push(target.name);
|
|
125
|
+
}
|
|
126
|
+
else if (!options?.silent) {
|
|
127
|
+
console.error(` ⚠ Failed to install to ${target.name}: ${err.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { success: installed.length > 0, installed };
|
|
128
132
|
}
|
|
129
133
|
dotenvConfig();
|
|
130
134
|
const DEFAULT_DASHBOARD_PORT = process.env.AGENT_RELAY_DASHBOARD_PORT || '3888';
|
|
@@ -465,6 +469,36 @@ program
|
|
|
465
469
|
const socketPath = paths.socketPath;
|
|
466
470
|
const dbPath = paths.dbPath;
|
|
467
471
|
const pidFilePath = pidFilePathForSocket(socketPath);
|
|
472
|
+
// Auto-install relay protocol snippets if not already present
|
|
473
|
+
// Check if the snippet marker exists in any of the target files
|
|
474
|
+
const snippetTargets = ['CLAUDE.md', 'GEMINI.md', 'AGENTS.md'];
|
|
475
|
+
const snippetMarker = '<!-- prpm:snippet:start @agent-relay/agent-relay-snippet';
|
|
476
|
+
const hasSnippetInstalled = snippetTargets.some(file => {
|
|
477
|
+
const filePath = path.join(paths.projectRoot, file);
|
|
478
|
+
if (!fs.existsSync(filePath))
|
|
479
|
+
return false;
|
|
480
|
+
try {
|
|
481
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
482
|
+
return content.includes(snippetMarker);
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
if (!hasSnippetInstalled) {
|
|
489
|
+
console.log('Installing relay protocol snippets...');
|
|
490
|
+
try {
|
|
491
|
+
const result = await installRelaySnippets({ silent: false });
|
|
492
|
+
if (result.success) {
|
|
493
|
+
console.log(`Installed snippets to: ${result.installed.join(', ')}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
// Non-fatal - continue even if snippet install fails
|
|
498
|
+
console.log(`Note: Could not auto-install snippets: ${err.message}`);
|
|
499
|
+
}
|
|
500
|
+
console.log('');
|
|
501
|
+
}
|
|
468
502
|
// Set up log file to avoid console output polluting TUI terminals
|
|
469
503
|
// Only set if not already configured via environment
|
|
470
504
|
if (!process.env.AGENT_RELAY_LOG_FILE) {
|
|
@@ -578,60 +612,27 @@ program
|
|
|
578
612
|
if (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND') {
|
|
579
613
|
// Dashboard package not installed
|
|
580
614
|
if (dashboardRequested) {
|
|
581
|
-
// User explicitly asked for dashboard but it's not installed
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
dashboardPort = npxPort;
|
|
595
|
-
// Clean up dashboard process on exit
|
|
596
|
-
const cleanupDashboard = () => {
|
|
597
|
-
if (dashboardProcess && !dashboardProcess.killed) {
|
|
598
|
-
dashboardProcess.kill('SIGTERM');
|
|
599
|
-
}
|
|
600
|
-
};
|
|
601
|
-
process.on('SIGINT', cleanupDashboard);
|
|
602
|
-
process.on('SIGTERM', cleanupDashboard);
|
|
603
|
-
process.on('exit', cleanupDashboard);
|
|
604
|
-
// Wait a moment for dashboard to start
|
|
605
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
606
|
-
console.log(`Dashboard: http://localhost:${dashboardPort}`);
|
|
615
|
+
// User explicitly asked for dashboard but it's not installed - start via npx
|
|
616
|
+
console.log('Dashboard package not installed. Starting via npx...');
|
|
617
|
+
const { process: dashboardProcess, port: npxPort, ready } = startDashboardViaNpx({
|
|
618
|
+
port,
|
|
619
|
+
dataDir: paths.dataDir,
|
|
620
|
+
teamDir: paths.teamDir,
|
|
621
|
+
projectRoot: paths.projectRoot,
|
|
622
|
+
});
|
|
623
|
+
dashboardPort = npxPort;
|
|
624
|
+
// Clean up dashboard process on exit
|
|
625
|
+
const cleanupDashboard = () => {
|
|
626
|
+
if (dashboardProcess && !dashboardProcess.killed) {
|
|
627
|
+
dashboardProcess.kill('SIGTERM');
|
|
607
628
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const { process: dashboardProcess, port: npxPort } = startDashboardViaNpx({
|
|
616
|
-
port,
|
|
617
|
-
dataDir: paths.dataDir,
|
|
618
|
-
teamDir: paths.teamDir,
|
|
619
|
-
projectRoot: paths.projectRoot,
|
|
620
|
-
});
|
|
621
|
-
dashboardPort = npxPort;
|
|
622
|
-
// Clean up dashboard process on exit
|
|
623
|
-
const cleanupDashboard = () => {
|
|
624
|
-
if (dashboardProcess && !dashboardProcess.killed) {
|
|
625
|
-
dashboardProcess.kill('SIGTERM');
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
process.on('SIGINT', cleanupDashboard);
|
|
629
|
-
process.on('SIGTERM', cleanupDashboard);
|
|
630
|
-
process.on('exit', cleanupDashboard);
|
|
631
|
-
// Wait a moment for dashboard to start
|
|
632
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
633
|
-
console.log(`Dashboard: http://localhost:${dashboardPort}`);
|
|
634
|
-
}
|
|
629
|
+
};
|
|
630
|
+
process.on('SIGINT', cleanupDashboard);
|
|
631
|
+
process.on('SIGTERM', cleanupDashboard);
|
|
632
|
+
process.on('exit', cleanupDashboard);
|
|
633
|
+
// Wait for dashboard to be ready
|
|
634
|
+
await ready;
|
|
635
|
+
console.log(`Dashboard: http://localhost:${dashboardPort}`);
|
|
635
636
|
}
|
|
636
637
|
// Silent if user didn't explicitly request dashboard
|
|
637
638
|
}
|
|
@@ -3155,10 +3156,11 @@ async function runInit(options) {
|
|
|
3155
3156
|
console.log(' ○ Daemon is not running');
|
|
3156
3157
|
}
|
|
3157
3158
|
console.log('');
|
|
3158
|
-
// Step
|
|
3159
|
+
// Step 1: Install MCP for editors (only if RELAY_MCP_AUTO_INSTALL=1)
|
|
3159
3160
|
let mcpInstalled = false;
|
|
3160
|
-
|
|
3161
|
-
|
|
3161
|
+
const mcpAutoInstallEnabled = process.env.RELAY_MCP_AUTO_INSTALL === '1';
|
|
3162
|
+
if (!options.skipMcp && mcpAutoInstallEnabled) {
|
|
3163
|
+
console.log(' ┌─ MCP Server for AI Editors ───────────────────────────────┐');
|
|
3162
3164
|
console.log(' │ │');
|
|
3163
3165
|
console.log(' │ MCP (Model Context Protocol) gives AI editors native │');
|
|
3164
3166
|
console.log(' │ tools for agent communication: │');
|
|
@@ -3193,9 +3195,9 @@ async function runInit(options) {
|
|
|
3193
3195
|
console.log('');
|
|
3194
3196
|
}
|
|
3195
3197
|
}
|
|
3196
|
-
//
|
|
3198
|
+
// Start daemon
|
|
3197
3199
|
if (!daemonRunning && !options.skipDaemon) {
|
|
3198
|
-
console.log(' ┌─
|
|
3200
|
+
console.log(' ┌─ Start the Relay Daemon ──────────────────────────────────┐');
|
|
3199
3201
|
console.log(' │ │');
|
|
3200
3202
|
console.log(' │ The daemon manages agent connections and message │');
|
|
3201
3203
|
console.log(' │ routing. It runs in the background. │');
|