agent-relay-server 0.63.0 → 0.63.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/docs/openapi.json +1 -1
- package/package.json +4 -2
- package/public/assets/{MessageBubble-Cl6ufPHw.js → MessageBubble-D130169C.js} +2 -2
- package/public/assets/{MessageBubble-Cl6ufPHw.js.map → MessageBubble-D130169C.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-BnfjmkQ8.js → PlanGraphPanel-CbwbZmKY.js} +2 -2
- package/public/assets/{PlanGraphPanel-BnfjmkQ8.js.map → PlanGraphPanel-CbwbZmKY.js.map} +1 -1
- package/public/assets/{activity-aFxvGLtd.js → activity-SJjW9fgO.js} +2 -2
- package/public/assets/{activity-aFxvGLtd.js.map → activity-SJjW9fgO.js.map} +1 -1
- package/public/assets/{agents-DW5djToQ.js → agents-GPly8tGL.js} +2 -2
- package/public/assets/{agents-DW5djToQ.js.map → agents-GPly8tGL.js.map} +1 -1
- package/public/assets/{automation-Do4OpauI.js → automation-BFO5jLy8.js} +2 -2
- package/public/assets/{automation-Do4OpauI.js.map → automation-BFO5jLy8.js.map} +1 -1
- package/public/assets/chat-hP_8I1aE.js +2 -0
- package/public/assets/{chat-CvaQ2czL.js.map → chat-hP_8I1aE.js.map} +1 -1
- package/public/assets/{formatted-body-impl-0Lf8e1Ic.js → formatted-body-impl-DaBd6Vrd.js} +2 -2
- package/public/assets/{formatted-body-impl-0Lf8e1Ic.js.map → formatted-body-impl-DaBd6Vrd.js.map} +1 -1
- package/public/assets/{index-C3su8ezq.js → index-CmM_Ozy7.js} +5 -5
- package/public/assets/{index-C3su8ezq.js.map → index-CmM_Ozy7.js.map} +1 -1
- package/public/assets/{insights-64uExH1E.js → insights-4Z7kZ7NB.js} +2 -2
- package/public/assets/{insights-64uExH1E.js.map → insights-4Z7kZ7NB.js.map} +1 -1
- package/public/assets/{maintenance-EVBpKH07.js → maintenance-17uq77zC.js} +2 -2
- package/public/assets/{maintenance-EVBpKH07.js.map → maintenance-17uq77zC.js.map} +1 -1
- package/public/assets/{managed-agents-BLZPxGuw.js → managed-agents-CT_1x4C3.js} +2 -2
- package/public/assets/{managed-agents-BLZPxGuw.js.map → managed-agents-CT_1x4C3.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-Bg6rCWYN.js → markdown-preview-impl-DzcW3gVL.js} +2 -2
- package/public/assets/{markdown-preview-impl-Bg6rCWYN.js.map → markdown-preview-impl-DzcW3gVL.js.map} +1 -1
- package/public/assets/{memory-Bjr8bgjj.js → memory-B-zfQSp3.js} +2 -2
- package/public/assets/{memory-Bjr8bgjj.js.map → memory-B-zfQSp3.js.map} +1 -1
- package/public/assets/{messages-CRYw1F4z.js → messages-Ckt9aNgZ.js} +2 -2
- package/public/assets/{messages-CRYw1F4z.js.map → messages-Ckt9aNgZ.js.map} +1 -1
- package/public/assets/{orchestrators-D-co1vdp.js → orchestrators-DVpGfTtB.js} +2 -2
- package/public/assets/{orchestrators-D-co1vdp.js.map → orchestrators-DVpGfTtB.js.map} +1 -1
- package/public/assets/{overview-Ddy5czri.js → overview-C8FoAUmj.js} +2 -2
- package/public/assets/{overview-Ddy5czri.js.map → overview-C8FoAUmj.js.map} +1 -1
- package/public/assets/{pairs-CFVM8uHL.js → pairs-WEM0MEmH.js} +2 -2
- package/public/assets/{pairs-CFVM8uHL.js.map → pairs-WEM0MEmH.js.map} +1 -1
- package/public/assets/{security-Df4_76bh.js → security-FzjxX7I2.js} +2 -2
- package/public/assets/{security-Df4_76bh.js.map → security-FzjxX7I2.js.map} +1 -1
- package/public/assets/{select-Cq3BBnL9.js → select-BPpSWAx4.js} +2 -2
- package/public/assets/{select-Cq3BBnL9.js.map → select-BPpSWAx4.js.map} +1 -1
- package/public/assets/{settings-DOLEYXZL.js → settings-Bl8wcAYn.js} +2 -2
- package/public/assets/{settings-DOLEYXZL.js.map → settings-Bl8wcAYn.js.map} +1 -1
- package/public/assets/{tasks-BcsmPi9_.js → tasks-Dk6TtgpH.js} +2 -2
- package/public/assets/{tasks-BcsmPi9_.js.map → tasks-Dk6TtgpH.js.map} +1 -1
- package/public/assets/{teams-BQrV3eKI.js → teams-DRJjIYRl.js} +2 -2
- package/public/assets/{teams-BQrV3eKI.js.map → teams-DRJjIYRl.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-Dle04qpH.js → terminal-viewer-impl-YWCrgF9G.js} +2 -2
- package/public/assets/{terminal-viewer-impl-Dle04qpH.js.map → terminal-viewer-impl-YWCrgF9G.js.map} +1 -1
- package/public/assets/{work-queue-BLhTLg4o.js → work-queue-DYtnYiKs.js} +2 -2
- package/public/assets/{work-queue-BLhTLg4o.js.map → work-queue-DYtnYiKs.js.map} +1 -1
- package/public/index.html +1 -1
- package/runner/src/adapter.ts +2 -1
- package/runner/src/config.ts +143 -5
- package/src/artifact-storage.ts +3 -5
- package/src/bus.ts +3 -4
- package/src/cli/_shared.ts +5 -4
- package/src/cli/agent-detect.ts +6 -4
- package/src/cli/introspect.ts +3 -2
- package/src/cli/token.ts +2 -1
- package/src/cli/workspace.ts +2 -1
- package/src/config.ts +176 -1
- package/src/connectors.ts +3 -3
- package/src/db/connection.ts +2 -1
- package/src/db/continuation-archives.ts +3 -3
- package/src/index.ts +6 -5
- package/src/maintenance/constants.ts +39 -22
- package/src/provider-config-store.ts +2 -1
- package/src/provider-state.ts +1 -3
- package/src/routes/agent-sessions.ts +5 -4
- package/src/routes/orchestrator-proxy.ts +5 -4
- package/src/routes/workspaces.ts +5 -4
- package/src/security.ts +13 -7
- package/src/services/issue-lifecycle.ts +1 -4
- package/src/services/plan-graph.ts +1 -4
- package/src/upgrade.ts +7 -9
- package/src/workspace-auto-merge.ts +4 -5
- package/src/workspace-claim.ts +1 -2
- package/src/workspace-orphans.ts +2 -7
- package/src/workspace-probe.ts +2 -1
- package/public/assets/chat-CvaQ2czL.js +0 -2
package/src/config.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Shared runtime constants. Import from here rather than redefining.
|
|
2
2
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
4
5
|
import { join, dirname } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { DEFAULT_RELAY_URL, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
|
|
6
8
|
import { CONTRACT_VERSIONS } from "./contracts";
|
|
7
9
|
|
|
8
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -19,6 +21,31 @@ function envPositiveInt(name: string, fallback: number): number {
|
|
|
19
21
|
return Math.floor(parsed);
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
function envNumberOrDefault(name: string, fallback: number): number {
|
|
25
|
+
return Number(process.env[name]) || fallback;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function envNonNegativeNumber(name: string, fallback: number): number {
|
|
29
|
+
const parsed = Number(process.env[name]);
|
|
30
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function envSafePositiveInteger(name: string, fallback: number): number {
|
|
34
|
+
const raw = process.env[name];
|
|
35
|
+
if (!raw) return fallback;
|
|
36
|
+
const parsed = Number(raw);
|
|
37
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function envTrimmed(name: string): string {
|
|
41
|
+
return (process.env[name] || "").trim();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function envTrimmedOrUndefined(name: string): string | undefined {
|
|
45
|
+
const value = process.env[name]?.trim();
|
|
46
|
+
return value || undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
22
49
|
export const STALE_TTL_MS = envPositiveInt("STALE_TTL_MS", 120_000); // 2min without heartbeat → offline
|
|
23
50
|
// #451 — default TTL for fire-and-forget FAN-OUT messages (broadcast/tag/cap/label/team) that the
|
|
24
51
|
// sender didn't bound. A stale fan-out push must not wake an agent that matches the target hours
|
|
@@ -42,12 +69,160 @@ export const MAIN_MERGE_PAUSE_TTL_MS = envPositiveInt("AGENT_RELAY_MAIN_MERGE_PA
|
|
|
42
69
|
export const MAX_BODY_BYTES = 64 * 1024;
|
|
43
70
|
export const INTEGRATION_RATE_LIMIT_PER_MINUTE = envPositiveInt("AGENT_RELAY_INTEGRATION_RATE_LIMIT_PER_MINUTE", 120);
|
|
44
71
|
|
|
45
|
-
export const
|
|
72
|
+
export const AUTOMATION_INTERVAL_MS = envNumberOrDefault("AGENT_RELAY_AUTOMATION_INTERVAL_MS", 30 * 1000);
|
|
73
|
+
export const IDLE_TIMEOUT_SECONDS = envNumberOrDefault("AGENT_RELAY_IDLE_TIMEOUT_SECONDS", 255);
|
|
74
|
+
export const LOG_REQUESTS = process.env.AGENT_RELAY_LOG_REQUESTS === "1";
|
|
75
|
+
|
|
76
|
+
const AUTH_TOKEN = process.env.AGENT_RELAY_TOKEN || "";
|
|
46
77
|
export const CORS_ORIGINS = (process.env.AGENT_RELAY_CORS_ORIGINS || "")
|
|
47
78
|
.split(",")
|
|
48
79
|
.map((origin) => origin.trim())
|
|
49
80
|
.filter(Boolean);
|
|
50
81
|
|
|
82
|
+
export function relayToken(): string {
|
|
83
|
+
return process.env.AGENT_RELAY_TOKEN ?? AUTH_TOKEN;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function relayAuthHeaders(base: Record<string, string> = {}): Record<string, string> {
|
|
87
|
+
const token = relayToken();
|
|
88
|
+
return token ? { ...base, [RELAY_TOKEN_HEADER]: token } : base;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function allowUnauthenticated(): boolean {
|
|
92
|
+
return process.env.AGENT_RELAY_ALLOW_UNAUTH === "1";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function tokenSecretFromEnv(): string | undefined {
|
|
96
|
+
return process.env.AGENT_RELAY_TOKEN_SECRET;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function corsOriginsFromEnv(): string[] | undefined {
|
|
100
|
+
const raw = process.env.AGENT_RELAY_CORS_ORIGINS;
|
|
101
|
+
if (raw === undefined) return undefined;
|
|
102
|
+
return raw.split(",").map((origin) => origin.trim()).filter(Boolean);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function connectorsDirFromEnv(): string | undefined {
|
|
106
|
+
return process.env.AGENT_RELAY_CONNECTORS_DIR;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function connectorActionTimeoutMs(fallback: number): number {
|
|
110
|
+
const raw = Number(process.env.AGENT_RELAY_CONNECTOR_ACTION_TIMEOUT_MS);
|
|
111
|
+
return Number.isFinite(raw) && raw > 0 ? raw : fallback;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function rateLimitFallbackMs(): number {
|
|
115
|
+
return envNumberOrDefault("AGENT_RELAY_RATE_LIMIT_FALLBACK_MS", 60 * 60 * 1000);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function artifactsDir(): string {
|
|
119
|
+
return process.env.AGENT_RELAY_ARTIFACTS_DIR || "~/.agent-relay/artifacts";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function maxArtifactBytesFromEnv(fallback: number): number {
|
|
123
|
+
return envSafePositiveInteger("AGENT_RELAY_MAX_ARTIFACT_BYTES", fallback);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const OUTBOX_RETENTION_MS = envNumberOrDefault("AGENT_RELAY_OUTBOX_RETENTION_MS", 60 * 60 * 1000);
|
|
127
|
+
export const TOKEN_RECORD_RETENTION_SECONDS = envNumberOrDefault("AGENT_RELAY_TOKEN_RECORD_RETENTION_SECONDS", 7 * 24 * 60 * 60);
|
|
128
|
+
export const CONFLICT_SCAN_INTERVAL_MS = envNumberOrDefault("AGENT_RELAY_CONFLICT_SCAN_INTERVAL_MS", 10 * 60 * 1000);
|
|
129
|
+
export const WORKSPACE_RETENTION_MS = envNumberOrDefault("AGENT_RELAY_WORKSPACE_RETENTION_MS", DAY_MS);
|
|
130
|
+
export const DB_MAINTENANCE_INTERVAL_MS = envNumberOrDefault("AGENT_RELAY_DB_MAINTENANCE_INTERVAL_MS", DAY_MS);
|
|
131
|
+
export const DB_VACUUM_EVERY = envNumberOrDefault("AGENT_RELAY_DB_VACUUM_EVERY", 7);
|
|
132
|
+
export const WORKSPACE_REVIEW_TTL_MS = envNumberOrDefault("AGENT_RELAY_WORKSPACE_REVIEW_TTL_MS", 3 * DAY_MS);
|
|
133
|
+
export const WORKSPACE_GC_INTERVAL_MS = envNumberOrDefault("AGENT_RELAY_WORKSPACE_GC_INTERVAL_MS", 60 * 60 * 1000);
|
|
134
|
+
export const WORKSPACE_ORPHAN_REAPER_INTERVAL_MS = envNumberOrDefault("AGENT_RELAY_WORKSPACE_ORPHAN_REAPER_INTERVAL_MS", 30 * 60 * 1000);
|
|
135
|
+
export const WORKSPACE_AUTO_MERGE_INTERVAL_MS = envNumberOrDefault("AGENT_RELAY_WORKSPACE_AUTO_MERGE_INTERVAL_MS", CONFLICT_SCAN_INTERVAL_MS);
|
|
136
|
+
export const ORPHAN_REAPER_INTERVAL_MS = envNumberOrDefault("AGENT_RELAY_ORPHAN_REAPER_INTERVAL_MS", 5 * 60 * 1000);
|
|
137
|
+
export const STEWARD_WAKE_COOLDOWN_MS = envNumberOrDefault("AGENT_RELAY_STEWARD_WAKE_COOLDOWN_MS", 10 * 60 * 1000);
|
|
138
|
+
export const UNLANDED_FLAG_COOLDOWN_MS = envNumberOrDefault("AGENT_RELAY_ORPHAN_FLAG_COOLDOWN_MS", 6 * 60 * 60 * 1000);
|
|
139
|
+
export const STEWARD_CLAIM_TTL_MS = envNumberOrDefault("AGENT_RELAY_WORKSPACE_CLAIM_TTL_MS", 15 * 60_000);
|
|
140
|
+
export const DB_SLOW_QUERY_MS = envNumberOrDefault("AGENT_RELAY_DB_SLOW_MS", 0);
|
|
141
|
+
|
|
142
|
+
export function stewardEscalationMs(): number {
|
|
143
|
+
return envNumberOrDefault("AGENT_RELAY_WORKSPACE_STEWARD_ESCALATION_MS", 60 * 60 * 1000);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function stewardFallbackTarget(): string {
|
|
147
|
+
return envTrimmed("AGENT_RELAY_WORKSPACE_STEWARD_FALLBACK");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function envMsOrDefault(name: string, fallback: number): number {
|
|
151
|
+
return envNonNegativeNumber(name, fallback);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function orphanGraceMs(): number {
|
|
155
|
+
return envMsOrDefault("AGENT_RELAY_ORPHAN_GRACE_MS", 30 * 60 * 1000);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function orphanReapCooldownMs(): number {
|
|
159
|
+
return envMsOrDefault("AGENT_RELAY_ORPHAN_REAP_COOLDOWN_MS", 5 * 60 * 1000);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function orphanReapEnabled(): boolean {
|
|
163
|
+
return process.env.AGENT_RELAY_ORPHAN_REAP !== "0";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function orphanWorktreeReapEnabled(): boolean {
|
|
167
|
+
return process.env.AGENT_RELAY_ORPHAN_WORKTREE_REAP !== "0";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function workspaceAutoMergeEnabled(): boolean {
|
|
171
|
+
return process.env.AGENT_RELAY_WORKSPACE_AUTO_MERGE !== "0";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function continuationArchiveFtsEnabled(): boolean {
|
|
175
|
+
return process.env.AGENT_RELAY_DISABLE_CONTINUATION_ARCHIVE_FTS !== "1";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function busStaleGraceMs(): number {
|
|
179
|
+
return envNumberOrDefault("AGENT_RELAY_STALE_GRACE_MS", 120_000);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function defaultIssueRepo(): string | undefined {
|
|
183
|
+
return process.env.AGENT_RELAY_DEFAULT_ISSUE_REPO || process.env.GITHUB_REPOSITORY || undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function cliRelayUrl(): string {
|
|
187
|
+
return process.env.AGENT_RELAY_URL || DEFAULT_RELAY_URL;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function cliRelayToken(): string | undefined {
|
|
191
|
+
return process.env.AGENT_RELAY_TOKEN;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function workspaceJsonFromEnv(): string | undefined {
|
|
195
|
+
return process.env.AGENT_RELAY_WORKSPACE_JSON;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function providerSessionIdFromEnv(): string | undefined {
|
|
199
|
+
return process.env.AGENT_RELAY_PROVIDER_SESSION_ID;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function projectFromEnv(): string | undefined {
|
|
203
|
+
return process.env.AGENT_RELAY_PROJECT;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function agentIdFromEnv(): string | undefined {
|
|
207
|
+
return process.env.AGENT_RELAY_ID;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function codexStatePathFromEnv(): string | undefined {
|
|
211
|
+
return process.env.AGENT_RELAY_CODEX_STATE_PATH;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function contextPathFromEnv(): string | undefined {
|
|
215
|
+
return process.env.AGENT_RELAY_CONTEXT_PATH;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function localOrchestratorIdFromEnv(): string | undefined {
|
|
219
|
+
return envTrimmedOrUndefined("AGENT_RELAY_ORCHESTRATOR_ID");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function providerConfigHomeFromEnv(): string {
|
|
223
|
+
return process.env.AGENT_RELAY_HOME || join(process.env.HOME || ".", ".agent-relay");
|
|
224
|
+
}
|
|
225
|
+
|
|
51
226
|
export type IntegrationTokenConfig = {
|
|
52
227
|
name: string;
|
|
53
228
|
token: string;
|
package/src/connectors.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { ConnectorAction, ConnectorActionResult, ConnectorManifest, Connect
|
|
|
5
5
|
import { ValidationError } from "./db";
|
|
6
6
|
import { isRecord } from "agent-relay-sdk";
|
|
7
7
|
import { registerConnectorConfigSetting, validateConnectorConfig } from "./settings/modules";
|
|
8
|
+
import { connectorActionTimeoutMs as configuredConnectorActionTimeoutMs, connectorsDirFromEnv } from "./config";
|
|
8
9
|
|
|
9
10
|
const CONNECTOR_SCHEMA = "agent-relay.connector.v1";
|
|
10
11
|
const VALID_KINDS = new Set(["channel", "event", "provider", "orchestrator"]);
|
|
@@ -12,7 +13,7 @@ const VALID_ACTIONS = new Set(["install", "uninstall", "enable", "disable", "sta
|
|
|
12
13
|
const DEFAULT_ACTION_TIMEOUT_MS = 30_000;
|
|
13
14
|
|
|
14
15
|
function connectorRegistryDir(): string {
|
|
15
|
-
const configured =
|
|
16
|
+
const configured = connectorsDirFromEnv();
|
|
16
17
|
if (configured) return resolveHome(configured);
|
|
17
18
|
return join(homedir(), ".agent-relay", "connectors");
|
|
18
19
|
}
|
|
@@ -177,8 +178,7 @@ export function writeConnectorConfig(id: string, config: Record<string, unknown>
|
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
function connectorActionTimeoutMs(): number {
|
|
180
|
-
|
|
181
|
-
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_ACTION_TIMEOUT_MS;
|
|
181
|
+
return configuredConnectorActionTimeoutMs(DEFAULT_ACTION_TIMEOUT_MS);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
const LIFECYCLE_ACTIONS = new Set(["install", "uninstall", "start", "stop", "restart", "enable", "disable"]);
|
package/src/db/connection.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
+
import { DB_SLOW_QUERY_MS } from "../config";
|
|
2
3
|
|
|
3
4
|
// The shared SQLite handle. Set once by initDb() (schema.ts) and read everywhere
|
|
4
5
|
// else through getDb(). Kept module-private so the only mutation path is setDb().
|
|
@@ -33,7 +34,7 @@ export function applyConnectionPragmas(conn: Database): void {
|
|
|
33
34
|
// Slow-query log. Off unless AGENT_RELAY_DB_SLOW_MS is set (>0). Wraps the
|
|
34
35
|
// execution of the heavy dynamic queries so the next slow query surfaces before
|
|
35
36
|
// it wedges something (see issue #197 — the 8.6s reply-obligation query).
|
|
36
|
-
const SLOW_QUERY_MS =
|
|
37
|
+
const SLOW_QUERY_MS = DB_SLOW_QUERY_MS;
|
|
37
38
|
export function timedQuery<T>(label: string, run: () => T): T {
|
|
38
39
|
if (SLOW_QUERY_MS <= 0) return run();
|
|
39
40
|
const start = performance.now();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MAX_BODY_BYTES } from "../config";
|
|
1
|
+
import { MAX_BODY_BYTES, continuationArchiveFtsEnabled } from "../config";
|
|
2
2
|
import { getContinuationEnvelope } from "./continuations.ts";
|
|
3
3
|
import { getDb, ValidationError } from "./connection.ts";
|
|
4
4
|
|
|
@@ -28,7 +28,7 @@ interface ContinuationArchiveRow {
|
|
|
28
28
|
const MAX_RECALL_LIMIT = 20;
|
|
29
29
|
|
|
30
30
|
export function ensureContinuationArchiveSearchSchema(): void {
|
|
31
|
-
if (
|
|
31
|
+
if (!continuationArchiveFtsEnabled()) return;
|
|
32
32
|
try {
|
|
33
33
|
getDb().query(`
|
|
34
34
|
CREATE VIRTUAL TABLE IF NOT EXISTS continuation_archive_segments
|
|
@@ -116,7 +116,7 @@ export function searchContinuationArchives(input: {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
function continuationArchiveSearchMode(): "fts5" | "like" {
|
|
119
|
-
if (
|
|
119
|
+
if (!continuationArchiveFtsEnabled()) return "like";
|
|
120
120
|
const row = getDb().query(`
|
|
121
121
|
SELECT 1 FROM sqlite_master
|
|
122
122
|
WHERE type = 'table' AND name = 'continuation_archive_segments'
|
package/src/index.ts
CHANGED
|
@@ -15,7 +15,11 @@ import { startPlanLiveBindings } from "./services/plan-live-bindings";
|
|
|
15
15
|
import { resolve, sep } from "path";
|
|
16
16
|
import { gzipSync, brotliCompressSync, constants as zlibConstants } from "node:zlib";
|
|
17
17
|
import {
|
|
18
|
+
AUTOMATION_INTERVAL_MS,
|
|
19
|
+
IDLE_TIMEOUT_SECONDS,
|
|
20
|
+
LOG_REQUESTS,
|
|
18
21
|
VERSION,
|
|
22
|
+
relayToken,
|
|
19
23
|
} from "./config";
|
|
20
24
|
import { CONTRACT_VERSIONS } from "./contracts";
|
|
21
25
|
import {
|
|
@@ -69,9 +73,6 @@ function startServer(): void {
|
|
|
69
73
|
const PORT = Number(process.env.PORT) || 4850;
|
|
70
74
|
const HOST = process.env.HOST || "127.0.0.1";
|
|
71
75
|
const DB_PATH = process.env.DB_PATH || "agent-relay.db";
|
|
72
|
-
const AUTOMATION_INTERVAL_MS = Number(process.env.AGENT_RELAY_AUTOMATION_INTERVAL_MS) || 30 * 1000;
|
|
73
|
-
const IDLE_TIMEOUT_SECONDS = Number(process.env.AGENT_RELAY_IDLE_TIMEOUT_SECONDS) || 255;
|
|
74
|
-
const LOG_REQUESTS = process.env.AGENT_RELAY_LOG_REQUESTS === "1";
|
|
75
76
|
|
|
76
77
|
assertSafeNetworkConfig(HOST);
|
|
77
78
|
initDb(DB_PATH);
|
|
@@ -246,9 +247,9 @@ export function createFetchHandler(
|
|
|
246
247
|
upstream.protocol = upstream.protocol === "https:" ? "wss:" : "ws:";
|
|
247
248
|
// Pass the relay token as a header (not a query param) so it never lands in
|
|
248
249
|
// access/proxy logs (#149). The orchestrator accepts both, but headers are safer.
|
|
249
|
-
const
|
|
250
|
+
const token = relayToken();
|
|
250
251
|
const upgraded = server.upgrade(req, {
|
|
251
|
-
data: { kind: "terminal-proxy", upstreamUrl: upstream.toString(), upstreamToken:
|
|
252
|
+
data: { kind: "terminal-proxy", upstreamUrl: upstream.toString(), upstreamToken: token, pending: [] },
|
|
252
253
|
});
|
|
253
254
|
if (!upgraded) return new Response("WebSocket upgrade failed", { status: 400 });
|
|
254
255
|
return undefined;
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CONFLICT_SCAN_INTERVAL_MS,
|
|
3
|
+
DAY_MS,
|
|
4
|
+
DB_MAINTENANCE_INTERVAL_MS,
|
|
5
|
+
DB_VACUUM_EVERY,
|
|
6
|
+
ORPHAN_REAPER_INTERVAL_MS,
|
|
7
|
+
OUTBOX_RETENTION_MS,
|
|
8
|
+
REAP_INTERVAL_MS,
|
|
9
|
+
TOKEN_RECORD_RETENTION_SECONDS,
|
|
10
|
+
WORKSPACE_AUTO_MERGE_INTERVAL_MS,
|
|
11
|
+
WORKSPACE_GC_INTERVAL_MS,
|
|
12
|
+
WORKSPACE_ORPHAN_REAPER_INTERVAL_MS,
|
|
13
|
+
WORKSPACE_RETENTION_MS,
|
|
14
|
+
WORKSPACE_REVIEW_TTL_MS,
|
|
15
|
+
envMsOrDefault,
|
|
16
|
+
orphanReapCooldownMs,
|
|
17
|
+
orphanReapEnabled,
|
|
18
|
+
orphanGraceMs,
|
|
19
|
+
stewardEscalationMs,
|
|
20
|
+
stewardFallbackTarget,
|
|
21
|
+
} from "../config";
|
|
2
22
|
import { READY_TO_LAND_STATUSES, TERMINAL_WORKSPACE_STATUSES } from "../workspace-phase";
|
|
3
23
|
import type { WorkspaceStatus } from "../types";
|
|
4
24
|
|
|
@@ -6,30 +26,27 @@ export { DAY_MS, REAP_INTERVAL_MS, TERMINAL_WORKSPACE_STATUSES };
|
|
|
6
26
|
|
|
7
27
|
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
8
28
|
export const SCHEDULER_TICK_MS = 10_000;
|
|
9
|
-
export const OUTBOX_RETENTION_MS = Number(process.env.AGENT_RELAY_OUTBOX_RETENTION_MS) || 60 * 60 * 1000;
|
|
10
|
-
export const TOKEN_RECORD_RETENTION_SECONDS = Number(process.env.AGENT_RELAY_TOKEN_RECORD_RETENTION_SECONDS) || 7 * 24 * 60 * 60;
|
|
11
|
-
export const CONFLICT_SCAN_INTERVAL_MS = Number(process.env.AGENT_RELAY_CONFLICT_SCAN_INTERVAL_MS) || 10 * 60 * 1000;
|
|
12
|
-
export const WORKSPACE_RETENTION_MS = Number(process.env.AGENT_RELAY_WORKSPACE_RETENTION_MS) || DAY_MS;
|
|
13
|
-
export const DB_MAINTENANCE_INTERVAL_MS = Number(process.env.AGENT_RELAY_DB_MAINTENANCE_INTERVAL_MS) || DAY_MS;
|
|
14
|
-
export const DB_VACUUM_EVERY = Number(process.env.AGENT_RELAY_DB_VACUUM_EVERY) || 7;
|
|
15
|
-
export const WORKSPACE_REVIEW_TTL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_REVIEW_TTL_MS) || 3 * DAY_MS;
|
|
16
|
-
export const WORKSPACE_GC_INTERVAL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_GC_INTERVAL_MS) || 60 * 60 * 1000;
|
|
17
|
-
export const WORKSPACE_ORPHAN_REAPER_INTERVAL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_ORPHAN_REAPER_INTERVAL_MS) || 30 * 60 * 1000;
|
|
18
|
-
export const WORKSPACE_AUTO_MERGE_INTERVAL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_AUTO_MERGE_INTERVAL_MS) || CONFLICT_SCAN_INTERVAL_MS;
|
|
19
|
-
export const ORPHAN_REAPER_INTERVAL_MS = Number(process.env.AGENT_RELAY_ORPHAN_REAPER_INTERVAL_MS) || 5 * 60 * 1000;
|
|
20
|
-
|
|
21
|
-
export const stewardEscalationMs = () => Number(process.env.AGENT_RELAY_WORKSPACE_STEWARD_ESCALATION_MS) || 60 * 60 * 1000;
|
|
22
|
-
export const stewardFallbackTarget = () => (process.env.AGENT_RELAY_WORKSPACE_STEWARD_FALLBACK || "").trim();
|
|
23
29
|
export const STRANDABLE_STATUSES = new Set<WorkspaceStatus>([...READY_TO_LAND_STATUSES, "conflict"]);
|
|
24
30
|
export const CONFLICT_SCAN_STATUSES = new Set<WorkspaceStatus>(["active", "ready", "review_requested", "merge_planned", "conflict"]);
|
|
25
31
|
export const LANDED_RECONCILE_STATUSES = new Set<WorkspaceStatus>(["merge_planned", "review_requested", "conflict"]);
|
|
26
32
|
|
|
27
|
-
export const envMsOrDefault = (name: string, fallback: number): number => {
|
|
28
|
-
const v = Number(process.env[name]);
|
|
29
|
-
return Number.isFinite(v) && v >= 0 ? v : fallback;
|
|
30
|
-
};
|
|
31
|
-
export const orphanGraceMs = () => envMsOrDefault("AGENT_RELAY_ORPHAN_GRACE_MS", 30 * 60 * 1000);
|
|
32
|
-
export const orphanReapCooldownMs = () => envMsOrDefault("AGENT_RELAY_ORPHAN_REAP_COOLDOWN_MS", 5 * 60 * 1000);
|
|
33
33
|
export const teamReapGraceMs = () => envMsOrDefault("TEAM_REAP_GRACE_MS", 30 * 60 * 1000);
|
|
34
34
|
export const teamReapIntervalMs = () => envMsOrDefault("TEAM_REAP_INTERVAL_MS", 5 * 60 * 1000);
|
|
35
|
-
export
|
|
35
|
+
export {
|
|
36
|
+
CONFLICT_SCAN_INTERVAL_MS,
|
|
37
|
+
DB_MAINTENANCE_INTERVAL_MS,
|
|
38
|
+
DB_VACUUM_EVERY,
|
|
39
|
+
ORPHAN_REAPER_INTERVAL_MS,
|
|
40
|
+
OUTBOX_RETENTION_MS,
|
|
41
|
+
TOKEN_RECORD_RETENTION_SECONDS,
|
|
42
|
+
WORKSPACE_AUTO_MERGE_INTERVAL_MS,
|
|
43
|
+
WORKSPACE_GC_INTERVAL_MS,
|
|
44
|
+
WORKSPACE_ORPHAN_REAPER_INTERVAL_MS,
|
|
45
|
+
WORKSPACE_RETENTION_MS,
|
|
46
|
+
WORKSPACE_REVIEW_TTL_MS,
|
|
47
|
+
orphanGraceMs,
|
|
48
|
+
orphanReapCooldownMs,
|
|
49
|
+
orphanReapEnabled,
|
|
50
|
+
stewardEscalationMs,
|
|
51
|
+
stewardFallbackTarget,
|
|
52
|
+
};
|
|
@@ -3,6 +3,7 @@ import { hostname } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { ValidationError, listOrchestrators } from "./db";
|
|
5
5
|
import { getConfig, listConfig, setConfig } from "./config-store";
|
|
6
|
+
import { providerConfigHomeFromEnv } from "./config";
|
|
6
7
|
import { cleanString, cleanStringArray, optionalEnum } from "./validation";
|
|
7
8
|
import { emitConfigChanged } from "./sse";
|
|
8
9
|
import {
|
|
@@ -234,5 +235,5 @@ function maskEnv(env: Record<string, string>): Record<string, string> {
|
|
|
234
235
|
}
|
|
235
236
|
|
|
236
237
|
function agentRelayHome(): string {
|
|
237
|
-
return
|
|
238
|
+
return providerConfigHomeFromEnv();
|
|
238
239
|
}
|
package/src/provider-state.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isRecord } from "agent-relay-sdk";
|
|
2
|
+
import { rateLimitFallbackMs } from "./config";
|
|
2
3
|
|
|
3
4
|
// One server-side home for reading an agent's provider runtime state (the
|
|
4
5
|
// `meta.providerState` that both Claude and Codex ride for blocked/approval
|
|
@@ -48,9 +49,6 @@ export function isRateLimitHold(agent: HasMeta | null | undefined): boolean {
|
|
|
48
49
|
// rather than waiting the full (unknown) limit window. Re-entering the hold on a
|
|
49
50
|
// still-limited retry is cheap, and a subscription limit almost always carries a
|
|
50
51
|
// reset time. Read at call-time so operators/tests can tune it.
|
|
51
|
-
const rateLimitFallbackMs = (): number =>
|
|
52
|
-
Number(process.env.AGENT_RELAY_RATE_LIMIT_FALLBACK_MS) || 60 * 60 * 1000;
|
|
53
|
-
|
|
54
52
|
/**
|
|
55
53
|
* When a rate-limit hold should be lifted. The provider's `resetAt` is
|
|
56
54
|
* authoritative; otherwise fall back to `enteredAt + fallback window`. Null only
|
|
@@ -14,6 +14,7 @@ import { authContextFromRequest } from "../services/auth-context";
|
|
|
14
14
|
import { ShutdownAuthError, ShutdownTargetError, shutdownAgent } from "../services/shutdown-agent";
|
|
15
15
|
import { type AgentCard, type SpawnApprovalMode } from "../types";
|
|
16
16
|
import { type ProviderEffort } from "agent-relay-sdk/provider-catalog";
|
|
17
|
+
import { relayToken } from "../config";
|
|
17
18
|
|
|
18
19
|
const CLAUDE_RESUME_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19
20
|
|
|
@@ -486,8 +487,8 @@ async function proxyOrchestratorDelete(req: Request, orchestratorId: string, pat
|
|
|
486
487
|
const incoming = new URL(req.url);
|
|
487
488
|
const proxyUrl = `${orch.apiUrl}${path}${incoming.search}`;
|
|
488
489
|
const headers: Record<string, string> = {};
|
|
489
|
-
const
|
|
490
|
-
if (
|
|
490
|
+
const token = relayToken();
|
|
491
|
+
if (token) headers[RELAY_TOKEN_HEADER] = token;
|
|
491
492
|
try {
|
|
492
493
|
const res = await fetch(proxyUrl, {
|
|
493
494
|
method: "DELETE",
|
|
@@ -507,8 +508,8 @@ async function proxyOrchestratorJson(orchestratorId: string, path: string, metho
|
|
|
507
508
|
if (!orch.apiUrl) return error("orchestrator does not expose an API", 422);
|
|
508
509
|
if (orch.status !== "online") return error("orchestrator is offline", 422);
|
|
509
510
|
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
510
|
-
const
|
|
511
|
-
if (
|
|
511
|
+
const token = relayToken();
|
|
512
|
+
if (token) headers[RELAY_TOKEN_HEADER] = token;
|
|
512
513
|
try {
|
|
513
514
|
const res = await fetch(`${orch.apiUrl}${path}`, {
|
|
514
515
|
method,
|
|
@@ -5,6 +5,7 @@ import { authorizeRoute, cleanJsonArray, error, json, type Handler } from "./_sh
|
|
|
5
5
|
import { cleanStringArray } from "../validation";
|
|
6
6
|
import { emitOrchestratorStatus } from "../sse";
|
|
7
7
|
import { type OrchestratorRuntimeInput, type SpawnProvider } from "../types";
|
|
8
|
+
import { relayToken } from "../config";
|
|
8
9
|
|
|
9
10
|
export const getOrchestratorDirectories: Handler = async (req, params) => {
|
|
10
11
|
const orch = getOrchestrator(params.id!);
|
|
@@ -63,8 +64,8 @@ async function proxyOrchestratorGet(req: Request, orchestratorId: string, path:
|
|
|
63
64
|
const incoming = new URL(req.url);
|
|
64
65
|
const proxyUrl = `${orch.apiUrl}${path}${incoming.search}`;
|
|
65
66
|
const headers: Record<string, string> = {};
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
67
|
+
const token = relayToken();
|
|
68
|
+
if (token) headers[RELAY_TOKEN_HEADER] = token;
|
|
68
69
|
try {
|
|
69
70
|
const res = await fetch(proxyUrl, { headers, signal: AbortSignal.timeout(10_000) });
|
|
70
71
|
const contentType = res.headers.get("content-type") ?? "";
|
|
@@ -88,8 +89,8 @@ async function proxyOrchestratorPost(req: Request, orchestratorId: string, path:
|
|
|
88
89
|
const headers: Record<string, string> = {
|
|
89
90
|
"Content-Type": req.headers.get("content-type") ?? "application/json",
|
|
90
91
|
};
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
92
|
+
const token = relayToken();
|
|
93
|
+
if (token) headers[RELAY_TOKEN_HEADER] = token;
|
|
93
94
|
try {
|
|
94
95
|
const body = await req.text();
|
|
95
96
|
const res = await fetch(proxyUrl, {
|
package/src/routes/workspaces.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { resolve } from "node:path";
|
|
|
13
13
|
import { type WorkspaceAutoMergePolicy, type WorkspaceDiagnostics, type WorkspaceGitState, type WorkspaceMergeStrategy, type WorkspaceRecord, type WorkspaceStatus } from "../types";
|
|
14
14
|
import { AUTO_MERGE_POLICIES } from "../workspace-merge";
|
|
15
15
|
import { workspaceActiveClaim } from "../workspace-claim";
|
|
16
|
+
import { relayToken } from "../config";
|
|
16
17
|
|
|
17
18
|
export const getWorkspaces: Handler = (req) => {
|
|
18
19
|
try {
|
|
@@ -50,8 +51,8 @@ async function proxyWorkspaceHostGet(workspaceId: string, hostPath: string, extr
|
|
|
50
51
|
if (workspace.baseSha) query.set("baseSha", workspace.baseSha);
|
|
51
52
|
for (const [key, value] of Object.entries(extraQuery ?? {})) query.set(key, value);
|
|
52
53
|
const headers: Record<string, string> = {};
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
54
|
+
const token = relayToken();
|
|
55
|
+
if (token) headers[RELAY_TOKEN_HEADER] = token;
|
|
55
56
|
try {
|
|
56
57
|
const res = await fetch(`${orch.apiUrl}${hostPath}?${query.toString()}`, {
|
|
57
58
|
headers,
|
|
@@ -145,8 +146,8 @@ async function fetchWorkspaceGitState(workspace: WorkspaceRecord): Promise<{ sta
|
|
|
145
146
|
if (workspace.baseRef) query.set("baseRef", workspace.baseRef);
|
|
146
147
|
if (workspace.baseSha) query.set("baseSha", workspace.baseSha);
|
|
147
148
|
const headers: Record<string, string> = {};
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
149
|
+
const token = relayToken();
|
|
150
|
+
if (token) headers[RELAY_TOKEN_HEADER] = token;
|
|
150
151
|
try {
|
|
151
152
|
const res = await fetch(`${orch.apiUrl}/api/workspace/state?${query.toString()}`, { headers, signal: AbortSignal.timeout(10_000) });
|
|
152
153
|
if (!res.ok) return { unavailable: `host returned ${res.status}` };
|
package/src/security.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CORS_ORIGINS,
|
|
3
|
+
allowUnauthenticated,
|
|
4
|
+
corsOriginsFromEnv,
|
|
5
|
+
getIntegrationTokens,
|
|
6
|
+
relayToken,
|
|
7
|
+
tokenSecretFromEnv,
|
|
8
|
+
type IntegrationTokenConfig,
|
|
9
|
+
} from "./config";
|
|
2
10
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
3
11
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
12
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -14,7 +22,7 @@ export function isLoopbackHost(hostname: string): boolean {
|
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
export function assertSafeNetworkConfig(host: string): void {
|
|
17
|
-
if (authToken() ||
|
|
25
|
+
if (authToken() || allowUnauthenticated()) return;
|
|
18
26
|
if (isLoopbackHost(host)) return;
|
|
19
27
|
throw new Error(
|
|
20
28
|
`Refusing to bind unauthenticated relay on ${host}. Set AGENT_RELAY_TOKEN or AGENT_RELAY_ALLOW_UNAUTH=1.`,
|
|
@@ -407,7 +415,7 @@ export function unauthorized(req: Request): Response {
|
|
|
407
415
|
}
|
|
408
416
|
|
|
409
417
|
function authToken(): string {
|
|
410
|
-
return
|
|
418
|
+
return relayToken();
|
|
411
419
|
}
|
|
412
420
|
|
|
413
421
|
function extractToken(req: Request): string | null {
|
|
@@ -417,7 +425,7 @@ function extractToken(req: Request): string | null {
|
|
|
417
425
|
}
|
|
418
426
|
|
|
419
427
|
function tokenSecret(): string {
|
|
420
|
-
const env =
|
|
428
|
+
const env = tokenSecretFromEnv();
|
|
421
429
|
if (env) return env;
|
|
422
430
|
const path = join(process.env.HOME || ".", ".agent-relay", "token-secret");
|
|
423
431
|
if (existsSync(path)) return readFileSync(path, "utf8").trim();
|
|
@@ -542,7 +550,5 @@ function isTokenRevoked(jti: string): boolean {
|
|
|
542
550
|
}
|
|
543
551
|
|
|
544
552
|
function corsOrigins(): string[] {
|
|
545
|
-
|
|
546
|
-
if (raw === undefined) return CORS_ORIGINS;
|
|
547
|
-
return raw.split(",").map((origin) => origin.trim()).filter(Boolean);
|
|
553
|
+
return corsOriginsFromEnv() ?? CORS_ORIGINS;
|
|
548
554
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { getIssueRef, resolveIssueRefByExternalKey, resolveIssueRefFromString, updateIssueRef } from "../db/issue-refs";
|
|
10
10
|
import { associateIssue } from "../db/issue-associations";
|
|
11
11
|
import { resolveIssueTrackerAdapter } from "../issue-tracker";
|
|
12
|
+
import { defaultIssueRepo } from "../config";
|
|
12
13
|
import type { IssueTrackerAdapter } from "../issue-tracker";
|
|
13
14
|
import { recordAndEmitIssueActivity } from "./issue-activity";
|
|
14
15
|
import type { IssueAssociationEntityType, IssueProvider, IssueRef, WorkspaceRecord } from "../types";
|
|
@@ -78,10 +79,6 @@ function logFailure(opts: IssueLifecycleOptions, message: string): void {
|
|
|
78
79
|
else console.warn(message);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
function defaultIssueRepo(): string | undefined {
|
|
82
|
-
return process.env.AGENT_RELAY_DEFAULT_ISSUE_REPO || process.env.GITHUB_REPOSITORY || undefined;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
82
|
/**
|
|
86
83
|
* `owner/repo` out of any git remote URL form: `git@github.com:owner/repo.git`,
|
|
87
84
|
* `https://github.com/owner/repo.git`, `ssh://git@host/owner/repo`, trailing slash/`.git`
|
|
@@ -4,6 +4,7 @@ import { associateIssue, planNodeIssueEntityId, removeAssociation } from "../db/
|
|
|
4
4
|
import { resolveIssueRefFromString } from "../db/issue-refs";
|
|
5
5
|
import { emitRelayEvent } from "../events";
|
|
6
6
|
import { cleanString, cleanStringArray, optionalEnum } from "../validation";
|
|
7
|
+
import { defaultIssueRepo } from "../config";
|
|
7
8
|
import type { AuthContext } from "./auth-context";
|
|
8
9
|
import type { AgentCard, Plan, PlanEdge, PlanNode, PlanOwnerRef, PlanStatus } from "../types";
|
|
9
10
|
|
|
@@ -86,10 +87,6 @@ function cleanRefs(value: unknown): PlanNode["refs"] | undefined {
|
|
|
86
87
|
return Object.keys(out).length ? out : undefined;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
function defaultIssueRepo(): string | undefined {
|
|
90
|
-
return process.env.AGENT_RELAY_DEFAULT_ISSUE_REPO || process.env.GITHUB_REPOSITORY || undefined;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
90
|
function syncPlanNodeIssueAssociation(planId: string, node: PlanNode): void {
|
|
94
91
|
if (!node.refs?.issueId) return;
|
|
95
92
|
associateIssue(node.refs.issueId, "plan-node", planNodeIssueEntityId(planId, node.id));
|
package/src/upgrade.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir, hostname as osHostname } from "node:os";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import { VERSION } from "./config";
|
|
4
|
+
import { VERSION, cliRelayUrl, localOrchestratorIdFromEnv, relayAuthHeaders } from "./config";
|
|
5
5
|
import type { RuntimeContracts, RuntimePackageMetadata } from "./contracts";
|
|
6
6
|
import { defaultRuntimePrefix, runtimeBinPath } from "./runtime-prefix";
|
|
7
|
-
import { errMessage
|
|
7
|
+
import { errMessage } from "agent-relay-sdk";
|
|
8
8
|
import { shellEscape as shellQuote } from "agent-relay-sdk/shell-utils";
|
|
9
9
|
|
|
10
10
|
export type UpgradeProvider = "auto" | "all" | "codex" | "claude" | "orchestrator";
|
|
@@ -462,9 +462,8 @@ async function npmViewVersion(spec: string): Promise<string | undefined> {
|
|
|
462
462
|
|
|
463
463
|
async function runningServerVersion(): Promise<string | undefined> {
|
|
464
464
|
try {
|
|
465
|
-
const headers
|
|
466
|
-
|
|
467
|
-
const relayUrl = (process.env.AGENT_RELAY_URL || "http://127.0.0.1:4850").replace(/\/+$/, "");
|
|
465
|
+
const headers = relayAuthHeaders();
|
|
466
|
+
const relayUrl = cliRelayUrl().replace(/\/+$/, "");
|
|
468
467
|
const response = await fetch(`${relayUrl}/api/stats`, { headers });
|
|
469
468
|
if (!response.ok) return undefined;
|
|
470
469
|
const payload = await response.json() as { version?: string };
|
|
@@ -476,9 +475,8 @@ async function runningServerVersion(): Promise<string | undefined> {
|
|
|
476
475
|
|
|
477
476
|
async function runningOrchestrators(): Promise<UpgradeSnapshot["runningOrchestrators"]> {
|
|
478
477
|
try {
|
|
479
|
-
const headers
|
|
480
|
-
|
|
481
|
-
const relayUrl = (process.env.AGENT_RELAY_URL || "http://127.0.0.1:4850").replace(/\/+$/, "");
|
|
478
|
+
const headers = relayAuthHeaders();
|
|
479
|
+
const relayUrl = cliRelayUrl().replace(/\/+$/, "");
|
|
482
480
|
const response = await fetch(`${relayUrl}/api/orchestrators`, { headers });
|
|
483
481
|
if (!response.ok) return [];
|
|
484
482
|
const payload = await response.json() as Array<{
|
|
@@ -588,7 +586,7 @@ export function resolveLocalOrchestratorId(homeDirOverride?: string): string {
|
|
|
588
586
|
// Unparsable config — fall through to env/hostname.
|
|
589
587
|
}
|
|
590
588
|
}
|
|
591
|
-
const envId =
|
|
589
|
+
const envId = localOrchestratorIdFromEnv();
|
|
592
590
|
if (envId) return envId;
|
|
593
591
|
return osHostname().replace(/\./g, "-");
|
|
594
592
|
}
|