agent-relay 2.1.1 → 2.1.3
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 +179 -8
- 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/dist/adapter.d.ts +2 -0
- package/packages/storage/dist/adapter.d.ts.map +1 -1
- package/packages/storage/dist/adapter.js +7 -1
- package/packages/storage/dist/adapter.js.map +1 -1
- package/packages/storage/dist/jsonl-adapter.d.ts +14 -0
- package/packages/storage/dist/jsonl-adapter.d.ts.map +1 -1
- package/packages/storage/dist/jsonl-adapter.js +75 -0
- package/packages/storage/dist/jsonl-adapter.js.map +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/storage/src/adapter.ts +9 -1
- package/packages/storage/src/jsonl-adapter.test.ts +31 -0
- package/packages/storage/src/jsonl-adapter.ts +86 -0
- 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
|
@@ -26022,7 +26022,7 @@ var jsonl_adapter_exports = {};
|
|
|
26022
26022
|
__export(jsonl_adapter_exports, {
|
|
26023
26023
|
JsonlStorageAdapter: () => JsonlStorageAdapter
|
|
26024
26024
|
});
|
|
26025
|
-
var import_node_fs20, import_node_path22, DEFAULT_RETENTION_MS2, DEFAULT_CLEANUP_INTERVAL_MS2, JsonlStorageAdapter;
|
|
26025
|
+
var import_node_fs20, import_node_path22, DEFAULT_RETENTION_MS2, DEFAULT_CLEANUP_INTERVAL_MS2, DEFAULT_WATCH_DEBOUNCE_MS, JsonlStorageAdapter;
|
|
26026
26026
|
var init_jsonl_adapter = __esm({
|
|
26027
26027
|
"packages/storage/dist/jsonl-adapter.js"() {
|
|
26028
26028
|
"use strict";
|
|
@@ -26030,6 +26030,7 @@ var init_jsonl_adapter = __esm({
|
|
|
26030
26030
|
import_node_path22 = __toESM(require("node:path"), 1);
|
|
26031
26031
|
DEFAULT_RETENTION_MS2 = 7 * 24 * 60 * 60 * 1e3;
|
|
26032
26032
|
DEFAULT_CLEANUP_INTERVAL_MS2 = 60 * 60 * 1e3;
|
|
26033
|
+
DEFAULT_WATCH_DEBOUNCE_MS = 100;
|
|
26033
26034
|
JsonlStorageAdapter = class {
|
|
26034
26035
|
baseDir;
|
|
26035
26036
|
messageDir;
|
|
@@ -26045,6 +26046,12 @@ var init_jsonl_adapter = __esm({
|
|
|
26045
26046
|
deletedMessages = /* @__PURE__ */ new Set();
|
|
26046
26047
|
sessions = /* @__PURE__ */ new Map();
|
|
26047
26048
|
resumeIndex = /* @__PURE__ */ new Map();
|
|
26049
|
+
watchForChanges;
|
|
26050
|
+
watchDebounceMs;
|
|
26051
|
+
messageWatcher;
|
|
26052
|
+
sessionWatcher;
|
|
26053
|
+
reloadDebounceTimer;
|
|
26054
|
+
sessionReloadDebounceTimer;
|
|
26048
26055
|
constructor(options) {
|
|
26049
26056
|
this.baseDir = options.baseDir;
|
|
26050
26057
|
this.messageDir = import_node_path22.default.join(this.baseDir, "messages");
|
|
@@ -26052,6 +26059,8 @@ var init_jsonl_adapter = __esm({
|
|
|
26052
26059
|
this.retentionMs = options.messageRetentionMs ?? DEFAULT_RETENTION_MS2;
|
|
26053
26060
|
this.cleanupIntervalMs = options.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS2;
|
|
26054
26061
|
this.fallbackReason = options.reason;
|
|
26062
|
+
this.watchForChanges = options.watchForChanges ?? false;
|
|
26063
|
+
this.watchDebounceMs = options.watchDebounceMs ?? DEFAULT_WATCH_DEBOUNCE_MS;
|
|
26055
26064
|
}
|
|
26056
26065
|
async init() {
|
|
26057
26066
|
await import_node_fs20.default.promises.mkdir(this.messageDir, { recursive: true });
|
|
@@ -26062,12 +26071,16 @@ var init_jsonl_adapter = __esm({
|
|
|
26062
26071
|
if (this.cleanupIntervalMs > 0) {
|
|
26063
26072
|
this.startCleanupTimer();
|
|
26064
26073
|
}
|
|
26074
|
+
if (this.watchForChanges) {
|
|
26075
|
+
this.startFileWatching();
|
|
26076
|
+
}
|
|
26065
26077
|
}
|
|
26066
26078
|
async close() {
|
|
26067
26079
|
if (this.cleanupTimer) {
|
|
26068
26080
|
clearInterval(this.cleanupTimer);
|
|
26069
26081
|
this.cleanupTimer = void 0;
|
|
26070
26082
|
}
|
|
26083
|
+
this.stopFileWatching();
|
|
26071
26084
|
this.messages.clear();
|
|
26072
26085
|
this.deletedMessages.clear();
|
|
26073
26086
|
this.sessions.clear();
|
|
@@ -26323,6 +26336,64 @@ var init_jsonl_adapter = __esm({
|
|
|
26323
26336
|
this.cleanupTimer.unref();
|
|
26324
26337
|
}
|
|
26325
26338
|
}
|
|
26339
|
+
startFileWatching() {
|
|
26340
|
+
try {
|
|
26341
|
+
this.messageWatcher = import_node_fs20.default.watch(this.messageDir, (eventType, filename) => {
|
|
26342
|
+
if (filename && filename.endsWith(".jsonl")) {
|
|
26343
|
+
this.debouncedReloadMessages();
|
|
26344
|
+
}
|
|
26345
|
+
});
|
|
26346
|
+
if (this.messageWatcher.unref) {
|
|
26347
|
+
this.messageWatcher.unref();
|
|
26348
|
+
}
|
|
26349
|
+
} catch {
|
|
26350
|
+
}
|
|
26351
|
+
try {
|
|
26352
|
+
this.sessionWatcher = import_node_fs20.default.watch(this.sessionFile, () => {
|
|
26353
|
+
this.debouncedReloadSessions();
|
|
26354
|
+
});
|
|
26355
|
+
if (this.sessionWatcher.unref) {
|
|
26356
|
+
this.sessionWatcher.unref();
|
|
26357
|
+
}
|
|
26358
|
+
} catch {
|
|
26359
|
+
}
|
|
26360
|
+
}
|
|
26361
|
+
stopFileWatching() {
|
|
26362
|
+
if (this.messageWatcher) {
|
|
26363
|
+
this.messageWatcher.close();
|
|
26364
|
+
this.messageWatcher = void 0;
|
|
26365
|
+
}
|
|
26366
|
+
if (this.sessionWatcher) {
|
|
26367
|
+
this.sessionWatcher.close();
|
|
26368
|
+
this.sessionWatcher = void 0;
|
|
26369
|
+
}
|
|
26370
|
+
if (this.reloadDebounceTimer) {
|
|
26371
|
+
clearTimeout(this.reloadDebounceTimer);
|
|
26372
|
+
this.reloadDebounceTimer = void 0;
|
|
26373
|
+
}
|
|
26374
|
+
if (this.sessionReloadDebounceTimer) {
|
|
26375
|
+
clearTimeout(this.sessionReloadDebounceTimer);
|
|
26376
|
+
this.sessionReloadDebounceTimer = void 0;
|
|
26377
|
+
}
|
|
26378
|
+
}
|
|
26379
|
+
debouncedReloadMessages() {
|
|
26380
|
+
if (this.reloadDebounceTimer) {
|
|
26381
|
+
clearTimeout(this.reloadDebounceTimer);
|
|
26382
|
+
}
|
|
26383
|
+
this.reloadDebounceTimer = setTimeout(() => {
|
|
26384
|
+
this.loadMessagesFromDisk().catch(() => {
|
|
26385
|
+
});
|
|
26386
|
+
}, this.watchDebounceMs);
|
|
26387
|
+
}
|
|
26388
|
+
debouncedReloadSessions() {
|
|
26389
|
+
if (this.sessionReloadDebounceTimer) {
|
|
26390
|
+
clearTimeout(this.sessionReloadDebounceTimer);
|
|
26391
|
+
}
|
|
26392
|
+
this.sessionReloadDebounceTimer = setTimeout(() => {
|
|
26393
|
+
this.loadSessionsFromDisk().catch(() => {
|
|
26394
|
+
});
|
|
26395
|
+
}, this.watchDebounceMs);
|
|
26396
|
+
}
|
|
26326
26397
|
async loadMessagesFromDisk() {
|
|
26327
26398
|
this.messages.clear();
|
|
26328
26399
|
this.deletedMessages.clear();
|
|
@@ -35497,6 +35568,7 @@ __export(index_exports, {
|
|
|
35497
35568
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
35498
35569
|
PROVIDER_AUTH_PATTERNS: () => PROVIDER_AUTH_PATTERNS,
|
|
35499
35570
|
PostgresDLQAdapter: () => PostgresDLQAdapter,
|
|
35571
|
+
RESERVED_AGENT_NAMES: () => RESERVED_AGENT_NAMES,
|
|
35500
35572
|
RelayClient: () => RelayClient,
|
|
35501
35573
|
RelayEvent: () => RelayEvent,
|
|
35502
35574
|
RelayPtyOrchestrator: () => RelayPtyOrchestrator,
|
|
@@ -37229,13 +37301,81 @@ function stripAnsi2(str) {
|
|
|
37229
37301
|
function sleep(ms) {
|
|
37230
37302
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
37231
37303
|
}
|
|
37304
|
+
var AUTO_SUGGEST_PATTERNS = {
|
|
37305
|
+
// Dim text styling - commonly used for ghost text
|
|
37306
|
+
dim: /\x1B\[2m/,
|
|
37307
|
+
// Bright black (dark gray) - common for suggestions
|
|
37308
|
+
brightBlack: /\x1B\[90m/,
|
|
37309
|
+
// 256-color grays (8 is dark gray, 240-250 are grays)
|
|
37310
|
+
gray256: /\x1B\[38;5;(?:8|24[0-9]|250)m/,
|
|
37311
|
+
// Cursor save (CSI s or ESC 7)
|
|
37312
|
+
cursorSave: /\x1B\[s|\x1B7/,
|
|
37313
|
+
// Cursor restore (CSI u or ESC 8)
|
|
37314
|
+
cursorRestore: /\x1B\[u|\x1B8/,
|
|
37315
|
+
// Italic text - sometimes used for suggestions
|
|
37316
|
+
italic: /\x1B\[3m/
|
|
37317
|
+
};
|
|
37318
|
+
function detectAutoSuggest(output) {
|
|
37319
|
+
const patterns = [];
|
|
37320
|
+
let confidence = 0;
|
|
37321
|
+
if (AUTO_SUGGEST_PATTERNS.dim.test(output)) {
|
|
37322
|
+
patterns.push("dim");
|
|
37323
|
+
confidence += 0.4;
|
|
37324
|
+
}
|
|
37325
|
+
if (AUTO_SUGGEST_PATTERNS.brightBlack.test(output)) {
|
|
37326
|
+
patterns.push("brightBlack");
|
|
37327
|
+
confidence += 0.4;
|
|
37328
|
+
}
|
|
37329
|
+
if (AUTO_SUGGEST_PATTERNS.gray256.test(output)) {
|
|
37330
|
+
patterns.push("gray256");
|
|
37331
|
+
confidence += 0.3;
|
|
37332
|
+
}
|
|
37333
|
+
if (AUTO_SUGGEST_PATTERNS.italic.test(output)) {
|
|
37334
|
+
patterns.push("italic");
|
|
37335
|
+
confidence += 0.2;
|
|
37336
|
+
}
|
|
37337
|
+
const hasCursorSave = AUTO_SUGGEST_PATTERNS.cursorSave.test(output);
|
|
37338
|
+
const hasCursorRestore = AUTO_SUGGEST_PATTERNS.cursorRestore.test(output);
|
|
37339
|
+
if (hasCursorSave && hasCursorRestore) {
|
|
37340
|
+
patterns.push("cursorSaveRestore");
|
|
37341
|
+
confidence += 0.5;
|
|
37342
|
+
} else if (hasCursorSave || hasCursorRestore) {
|
|
37343
|
+
patterns.push(hasCursorSave ? "cursorSave" : "cursorRestore");
|
|
37344
|
+
confidence += 0.2;
|
|
37345
|
+
}
|
|
37346
|
+
confidence = Math.min(confidence, 1);
|
|
37347
|
+
const stripped = stripAnsi2(output);
|
|
37348
|
+
if (patterns.length === 0) {
|
|
37349
|
+
return { isAutoSuggest: false, confidence: 0, patterns, strippedContent: stripped };
|
|
37350
|
+
}
|
|
37351
|
+
const lines = stripped.split("\n").filter((l) => l.trim().length > 0);
|
|
37352
|
+
if (lines.length > 2) {
|
|
37353
|
+
confidence *= 0.5;
|
|
37354
|
+
}
|
|
37355
|
+
const isAutoSuggest = confidence >= 0.4;
|
|
37356
|
+
return { isAutoSuggest, confidence, patterns, strippedContent: stripped };
|
|
37357
|
+
}
|
|
37358
|
+
function shouldIgnoreForIdleDetection(output) {
|
|
37359
|
+
if (!output || output.length === 0) {
|
|
37360
|
+
return true;
|
|
37361
|
+
}
|
|
37362
|
+
const result = detectAutoSuggest(output);
|
|
37363
|
+
if (result.isAutoSuggest) {
|
|
37364
|
+
return true;
|
|
37365
|
+
}
|
|
37366
|
+
const stripped = stripAnsi2(output).trim();
|
|
37367
|
+
if (stripped.length === 0) {
|
|
37368
|
+
return true;
|
|
37369
|
+
}
|
|
37370
|
+
return false;
|
|
37371
|
+
}
|
|
37232
37372
|
function buildInjectionString(msg) {
|
|
37233
37373
|
const sanitizedBody = stripAnsi2(msg.body || "").replace(/[\r\n]+/g, " ").trim();
|
|
37234
37374
|
if (sanitizedBody.startsWith("Relay message from ")) {
|
|
37235
37375
|
return sanitizedBody;
|
|
37236
37376
|
}
|
|
37237
37377
|
const shortId = msg.messageId.substring(0, 8);
|
|
37238
|
-
const displayFrom = msg.from === "
|
|
37378
|
+
const displayFrom = msg.from === "Dashboard" && typeof msg.data?.senderName === "string" ? msg.data.senderName : msg.from;
|
|
37239
37379
|
const threadHint = msg.thread ? ` [thread:${msg.thread}]` : "";
|
|
37240
37380
|
const importanceHint = msg.importance !== void 0 && msg.importance > 75 ? " [!!]" : msg.importance !== void 0 && msg.importance > 50 ? " [!]" : "";
|
|
37241
37381
|
const channelHint = msg.originalTo === "*" ? " [#general] (reply to #general, not sender)" : msg.originalTo?.startsWith("#") ? ` [${msg.originalTo}] (reply to ${msg.originalTo}, not sender)` : "";
|
|
@@ -42483,8 +42623,17 @@ var BaseWrapper = class extends import_node_events2.EventEmitter {
|
|
|
42483
42623
|
/**
|
|
42484
42624
|
* Feed output to the idle and stuck detectors.
|
|
42485
42625
|
* Call this whenever new output is received from the agent.
|
|
42626
|
+
*
|
|
42627
|
+
* Note: Auto-suggestions (ghost text) are filtered out to prevent
|
|
42628
|
+
* false idle resets. Claude Code and other CLIs show suggestions
|
|
42629
|
+
* in gray/dim text with cursor save/restore, which should not
|
|
42630
|
+
* be treated as "real" output for idle detection.
|
|
42486
42631
|
*/
|
|
42487
42632
|
feedIdleDetectorOutput(output) {
|
|
42633
|
+
if (shouldIgnoreForIdleDetection(output)) {
|
|
42634
|
+
this.stuckDetector.onOutput(output);
|
|
42635
|
+
return;
|
|
42636
|
+
}
|
|
42488
42637
|
this.idleDetector.onOutput(output);
|
|
42489
42638
|
this.stuckDetector.onOutput(output);
|
|
42490
42639
|
}
|
|
@@ -52803,6 +52952,16 @@ function generateEventSchemas() {
|
|
|
52803
52952
|
}
|
|
52804
52953
|
|
|
52805
52954
|
// packages/daemon/dist/connection.js
|
|
52955
|
+
var RESERVED_AGENT_NAMES = /* @__PURE__ */ new Set([
|
|
52956
|
+
"Dashboard",
|
|
52957
|
+
// Dashboard system client
|
|
52958
|
+
"cli",
|
|
52959
|
+
// CLI tool
|
|
52960
|
+
"system",
|
|
52961
|
+
// System messages
|
|
52962
|
+
"_router"
|
|
52963
|
+
// Internal router target
|
|
52964
|
+
]);
|
|
52806
52965
|
var DEFAULT_CONFIG10 = {
|
|
52807
52966
|
...DEFAULT_CONNECTION_CONFIG
|
|
52808
52967
|
};
|
|
@@ -52951,7 +53110,12 @@ var Connection = class {
|
|
|
52951
53110
|
this.sendError("BAD_REQUEST", "Unexpected HELLO", false);
|
|
52952
53111
|
return;
|
|
52953
53112
|
}
|
|
52954
|
-
|
|
53113
|
+
const agentName = envelope.payload.agent;
|
|
53114
|
+
if (RESERVED_AGENT_NAMES.has(agentName) && !envelope.payload._isSystemComponent) {
|
|
53115
|
+
this.sendError("BAD_REQUEST", `Agent name "${agentName}" is reserved for system use`, true);
|
|
53116
|
+
return;
|
|
53117
|
+
}
|
|
53118
|
+
this._agentName = agentName;
|
|
52955
53119
|
this._entityType = envelope.payload.entityType;
|
|
52956
53120
|
this._cli = envelope.payload.cli;
|
|
52957
53121
|
this._program = envelope.payload.program;
|
|
@@ -69989,7 +70153,8 @@ async function createStorageAdapter(dbPath, config2) {
|
|
|
69989
70153
|
const finalConfig = {
|
|
69990
70154
|
type: config2?.type ?? envConfig.type ?? "jsonl",
|
|
69991
70155
|
path: config2?.path ?? envConfig.path ?? dbPath,
|
|
69992
|
-
url: config2?.url ?? envConfig.url
|
|
70156
|
+
url: config2?.url ?? envConfig.url,
|
|
70157
|
+
watchForChanges: config2?.watchForChanges
|
|
69993
70158
|
};
|
|
69994
70159
|
const storageType = finalConfig.type?.toLowerCase();
|
|
69995
70160
|
switch (storageType) {
|
|
@@ -70027,7 +70192,8 @@ async function createStorageAdapter(dbPath, config2) {
|
|
|
70027
70192
|
console.warn("[storage] \u26A0\uFE0F Falling back to JSONL storage (append-only files)");
|
|
70028
70193
|
const adapter2 = new JsonlStorageAdapter2({
|
|
70029
70194
|
baseDir,
|
|
70030
|
-
reason: "upgrade to Node.js 22+ or run: npm rebuild better-sqlite3"
|
|
70195
|
+
reason: "upgrade to Node.js 22+ or run: npm rebuild better-sqlite3",
|
|
70196
|
+
watchForChanges: finalConfig.watchForChanges
|
|
70031
70197
|
});
|
|
70032
70198
|
await adapter2.init();
|
|
70033
70199
|
return adapter2;
|
|
@@ -70048,7 +70214,10 @@ async function createStorageAdapter(dbPath, config2) {
|
|
|
70048
70214
|
const { JsonlStorageAdapter: JsonlStorageAdapter2 } = await Promise.resolve().then(() => (init_jsonl_adapter(), jsonl_adapter_exports));
|
|
70049
70215
|
const baseDir = import_node_path23.default.dirname(finalConfig.path);
|
|
70050
70216
|
console.error("[storage] Using JSONL storage");
|
|
70051
|
-
const adapter = new JsonlStorageAdapter2({
|
|
70217
|
+
const adapter = new JsonlStorageAdapter2({
|
|
70218
|
+
baseDir,
|
|
70219
|
+
watchForChanges: finalConfig.watchForChanges
|
|
70220
|
+
});
|
|
70052
70221
|
await adapter.init();
|
|
70053
70222
|
return adapter;
|
|
70054
70223
|
}
|
|
@@ -70068,7 +70237,8 @@ async function createStorageAdapter(dbPath, config2) {
|
|
|
70068
70237
|
console.warn("[storage] \u26A0\uFE0F Falling back to JSONL storage (append-only files)");
|
|
70069
70238
|
const adapter2 = new JsonlStorageAdapter2({
|
|
70070
70239
|
baseDir,
|
|
70071
|
-
reason: "upgrade to Node.js 22+ or run: npm rebuild better-sqlite3"
|
|
70240
|
+
reason: "upgrade to Node.js 22+ or run: npm rebuild better-sqlite3",
|
|
70241
|
+
watchForChanges: finalConfig.watchForChanges
|
|
70072
70242
|
});
|
|
70073
70243
|
await adapter2.init();
|
|
70074
70244
|
return adapter2;
|
|
@@ -72681,7 +72851,7 @@ var Daemon = class _Daemon {
|
|
|
72681
72851
|
isInternalAgent(name) {
|
|
72682
72852
|
if (name.startsWith("__"))
|
|
72683
72853
|
return true;
|
|
72684
|
-
return name === "Dashboard" || name === "
|
|
72854
|
+
return name === "Dashboard" || name === "cli";
|
|
72685
72855
|
}
|
|
72686
72856
|
/**
|
|
72687
72857
|
* Stop the daemon.
|
|
@@ -78770,6 +78940,7 @@ init_dist();
|
|
|
78770
78940
|
PROTOCOL_VERSION,
|
|
78771
78941
|
PROVIDER_AUTH_PATTERNS,
|
|
78772
78942
|
PostgresDLQAdapter,
|
|
78943
|
+
RESERVED_AGENT_NAMES,
|
|
78773
78944
|
RelayClient,
|
|
78774
78945
|
RelayEvent,
|
|
78775
78946
|
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. │');
|