@buildautomaton/cli 0.1.15 → 0.1.16
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/README.md +4 -4
- package/dist/cli.js +1013 -541
- package/dist/cli.js.map +4 -4
- package/dist/index.js +418 -148
- package/dist/index.js.map +4 -4
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -2240,7 +2240,7 @@ var require_websocket = __commonJS({
|
|
|
2240
2240
|
var http = __require("http");
|
|
2241
2241
|
var net = __require("net");
|
|
2242
2242
|
var tls = __require("tls");
|
|
2243
|
-
var { randomBytes, createHash: createHash2 } = __require("crypto");
|
|
2243
|
+
var { randomBytes: randomBytes2, createHash: createHash2 } = __require("crypto");
|
|
2244
2244
|
var { Duplex, Readable: Readable2 } = __require("stream");
|
|
2245
2245
|
var { URL: URL2 } = __require("url");
|
|
2246
2246
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -2770,7 +2770,7 @@ var require_websocket = __commonJS({
|
|
|
2770
2770
|
}
|
|
2771
2771
|
}
|
|
2772
2772
|
const defaultPort = isSecure ? 443 : 80;
|
|
2773
|
-
const key =
|
|
2773
|
+
const key = randomBytes2(16).toString("base64");
|
|
2774
2774
|
const request = isSecure ? https2.request : http.request;
|
|
2775
2775
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2776
2776
|
let perMessageDeflate;
|
|
@@ -22787,8 +22787,8 @@ var PREVIEW_API_BASE_PATH = "/__preview";
|
|
|
22787
22787
|
var PREVIEW_SECRET_HEADER = "X-Preview-Secret";
|
|
22788
22788
|
var DEFAULT_PORT = 3e3;
|
|
22789
22789
|
var DEFAULT_COMMAND = "npm run preview";
|
|
22790
|
-
var PREVIEW_COMMAND_ENV = "
|
|
22791
|
-
var PREVIEW_PORT_ENV = "
|
|
22790
|
+
var PREVIEW_COMMAND_ENV = "BUILDAUTOMATON_PREVIEW_COMMAND";
|
|
22791
|
+
var PREVIEW_PORT_ENV = "BUILDAUTOMATON_PREVIEW_PORT";
|
|
22792
22792
|
var previewProcess = null;
|
|
22793
22793
|
var previewPort = DEFAULT_PORT;
|
|
22794
22794
|
var previewSecret = "";
|
|
@@ -22841,7 +22841,7 @@ var OPERATIONS = [
|
|
|
22841
22841
|
var previewSkill = {
|
|
22842
22842
|
id: "preview",
|
|
22843
22843
|
name: "Preview",
|
|
22844
|
-
description: "Start and manage a local preview server that implements the BuildAutomaton Preview Server API. Configure the command with
|
|
22844
|
+
description: "Start and manage a local preview server that implements the BuildAutomaton Preview Server API. Configure the command with BUILDAUTOMATON_PREVIEW_COMMAND (default: npm run preview). The server receives PORT and PREVIEW_SECRET and must expose /__preview/status and /__preview/stop.",
|
|
22845
22845
|
operations: OPERATIONS,
|
|
22846
22846
|
async execute(operationId, params) {
|
|
22847
22847
|
const command = getPreviewCommand();
|
|
@@ -23564,8 +23564,11 @@ function isLocalApiUrl(apiUrl) {
|
|
|
23564
23564
|
return false;
|
|
23565
23565
|
}
|
|
23566
23566
|
}
|
|
23567
|
+
function appUrlForApiUrl(apiUrl) {
|
|
23568
|
+
return apiUrl && isLocalApiUrl(apiUrl) ? process.env.BUILDAUTOMATON_APP_URL ?? "http://localhost:3000" : process.env.BUILDAUTOMATON_APP_URL ?? "https://app.buildautomaton.com";
|
|
23569
|
+
}
|
|
23567
23570
|
async function openBrowser(connectionId, initialWorkspaceId, preferredBridgeName, apiUrl, logFn = log) {
|
|
23568
|
-
const appUrl =
|
|
23571
|
+
const appUrl = appUrlForApiUrl(apiUrl);
|
|
23569
23572
|
let connectCliUrl = `${appUrl.replace(/\/$/, "")}/bridges/connect?connectionId=${connectionId}`;
|
|
23570
23573
|
if (initialWorkspaceId) {
|
|
23571
23574
|
try {
|
|
@@ -28994,7 +28997,7 @@ async function createCursorAcpClient(options) {
|
|
|
28994
28997
|
onRequest,
|
|
28995
28998
|
onFileChange
|
|
28996
28999
|
} = options;
|
|
28997
|
-
const dbgFs = process.env.
|
|
29000
|
+
const dbgFs = process.env.BUILDAUTOMATON_DEBUG_ACP_FS === "1";
|
|
28998
29001
|
const isWindows = process.platform === "win32";
|
|
28999
29002
|
const child = spawn4(command[0], command.slice(1), {
|
|
29000
29003
|
cwd,
|
|
@@ -31541,7 +31544,7 @@ function startFileIndexWatcher(cwd = getBridgeWorkspaceDirectory()) {
|
|
|
31541
31544
|
}
|
|
31542
31545
|
|
|
31543
31546
|
// src/dev-servers/manager/dev-server-manager.ts
|
|
31544
|
-
import { rm } from "node:fs/promises";
|
|
31547
|
+
import { rm as rm2 } from "node:fs/promises";
|
|
31545
31548
|
|
|
31546
31549
|
// src/dev-servers/process/send-server-status.ts
|
|
31547
31550
|
function sendDevServerStatus(getWs, serverId, status, options) {
|
|
@@ -32050,8 +32053,90 @@ var StreamTail = class {
|
|
|
32050
32053
|
}
|
|
32051
32054
|
};
|
|
32052
32055
|
|
|
32053
|
-
// src/dev-servers/manager/dev-server-
|
|
32056
|
+
// src/dev-servers/manager/dev-server-constants.ts
|
|
32054
32057
|
var BRIDGE_SHUTDOWN_GRACE_MS = 8e3;
|
|
32058
|
+
|
|
32059
|
+
// src/dev-servers/manager/dev-server-firehose-messages.ts
|
|
32060
|
+
function buildFirehoseSnapshotMessage(params) {
|
|
32061
|
+
const payload = {
|
|
32062
|
+
type: "log_snapshot",
|
|
32063
|
+
serverId: params.serverId,
|
|
32064
|
+
viewerId: params.viewerId,
|
|
32065
|
+
stdoutTail: params.tails.stdout,
|
|
32066
|
+
stderrTail: params.tails.stderr
|
|
32067
|
+
};
|
|
32068
|
+
return params.e2ee ? params.e2ee.encryptFields(payload, ["stdoutTail", "stderrTail"]) : payload;
|
|
32069
|
+
}
|
|
32070
|
+
function buildFirehoseLogChunkMessage(params) {
|
|
32071
|
+
const payload = {
|
|
32072
|
+
type: "log_chunk",
|
|
32073
|
+
serverId: params.serverId,
|
|
32074
|
+
stream: params.stream,
|
|
32075
|
+
text: params.text
|
|
32076
|
+
};
|
|
32077
|
+
return params.e2ee ? params.e2ee.encryptFields(payload, ["text"]) : payload;
|
|
32078
|
+
}
|
|
32079
|
+
|
|
32080
|
+
// src/dev-servers/manager/dev-server-firehose-sink.ts
|
|
32081
|
+
var DevServerFirehoseSink = class {
|
|
32082
|
+
constructor(options) {
|
|
32083
|
+
this.options = options;
|
|
32084
|
+
}
|
|
32085
|
+
logViewerRefCountByServerId = /* @__PURE__ */ new Map();
|
|
32086
|
+
firehoseSend = null;
|
|
32087
|
+
attach(send) {
|
|
32088
|
+
this.firehoseSend = send;
|
|
32089
|
+
}
|
|
32090
|
+
detach() {
|
|
32091
|
+
this.firehoseSend = null;
|
|
32092
|
+
this.logViewerRefCountByServerId.clear();
|
|
32093
|
+
}
|
|
32094
|
+
openLogViewer(serverId, viewerId) {
|
|
32095
|
+
const next = (this.logViewerRefCountByServerId.get(serverId) ?? 0) + 1;
|
|
32096
|
+
this.logViewerRefCountByServerId.set(serverId, next);
|
|
32097
|
+
this.sendSnapshot(serverId, viewerId);
|
|
32098
|
+
}
|
|
32099
|
+
closeLogViewer(serverId) {
|
|
32100
|
+
const n = (this.logViewerRefCountByServerId.get(serverId) ?? 0) - 1;
|
|
32101
|
+
if (n <= 0) this.logViewerRefCountByServerId.delete(serverId);
|
|
32102
|
+
else this.logViewerRefCountByServerId.set(serverId, n);
|
|
32103
|
+
}
|
|
32104
|
+
pushLogChunk(serverId, stream, chunk) {
|
|
32105
|
+
if ((this.logViewerRefCountByServerId.get(serverId) ?? 0) <= 0) return;
|
|
32106
|
+
if (!this.options.isPipedCaptureEnabled(serverId)) return;
|
|
32107
|
+
if (!this.firehoseSend) return;
|
|
32108
|
+
const text = chunk.toString("utf8");
|
|
32109
|
+
setImmediate(() => {
|
|
32110
|
+
if (!this.firehoseSend) return;
|
|
32111
|
+
this.firehoseSend(buildFirehoseLogChunkMessage({ serverId, stream, text, e2ee: this.options.e2ee }));
|
|
32112
|
+
});
|
|
32113
|
+
}
|
|
32114
|
+
sendSnapshot(serverId, viewerId) {
|
|
32115
|
+
const payload = buildFirehoseSnapshotMessage({
|
|
32116
|
+
serverId,
|
|
32117
|
+
viewerId,
|
|
32118
|
+
tails: this.options.getTails(serverId),
|
|
32119
|
+
e2ee: this.options.e2ee
|
|
32120
|
+
});
|
|
32121
|
+
setImmediate(() => {
|
|
32122
|
+
const send = this.firehoseSend;
|
|
32123
|
+
if (!send) return;
|
|
32124
|
+
send(payload);
|
|
32125
|
+
});
|
|
32126
|
+
}
|
|
32127
|
+
};
|
|
32128
|
+
|
|
32129
|
+
// src/dev-servers/manager/cleanup-merged-log-dir.ts
|
|
32130
|
+
import { rm } from "node:fs/promises";
|
|
32131
|
+
function cleanupMergedLogDirForServer(map2, serverId) {
|
|
32132
|
+
const mergedDir = map2.get(serverId);
|
|
32133
|
+
if (!mergedDir) return;
|
|
32134
|
+
map2.delete(serverId);
|
|
32135
|
+
void rm(mergedDir, { recursive: true, force: true }).catch(() => {
|
|
32136
|
+
});
|
|
32137
|
+
}
|
|
32138
|
+
|
|
32139
|
+
// src/dev-servers/manager/dev-server-manager.ts
|
|
32055
32140
|
var emptyTails = () => ({ stdout: [], stderr: [] });
|
|
32056
32141
|
var DevServerManager = class {
|
|
32057
32142
|
defsById = /* @__PURE__ */ new Map();
|
|
@@ -32059,66 +32144,36 @@ var DevServerManager = class {
|
|
|
32059
32144
|
streamTailsByServerId = /* @__PURE__ */ new Map();
|
|
32060
32145
|
spawnGenerationByServerId = /* @__PURE__ */ new Map();
|
|
32061
32146
|
pipedCaptureByServerId = /* @__PURE__ */ new Map();
|
|
32062
|
-
logViewerRefCountByServerId = /* @__PURE__ */ new Map();
|
|
32063
|
-
firehoseSend = null;
|
|
32064
32147
|
mergedLogPollByServerId = /* @__PURE__ */ new Map();
|
|
32065
32148
|
mergedLogCleanupDirByServerId = /* @__PURE__ */ new Map();
|
|
32066
32149
|
abortControllersByServerId = /* @__PURE__ */ new Map();
|
|
32067
32150
|
getWs;
|
|
32068
32151
|
log;
|
|
32069
32152
|
getBridgeCwd;
|
|
32153
|
+
e2ee;
|
|
32154
|
+
firehoseSink;
|
|
32070
32155
|
constructor(options) {
|
|
32071
32156
|
this.getWs = options.getWs;
|
|
32072
32157
|
this.log = options.log;
|
|
32073
32158
|
this.getBridgeCwd = options.getBridgeCwd ?? (() => process.cwd());
|
|
32159
|
+
this.e2ee = options.e2ee;
|
|
32160
|
+
this.firehoseSink = new DevServerFirehoseSink({
|
|
32161
|
+
getTails: (serverId) => this.snapshotTails(serverId),
|
|
32162
|
+
isPipedCaptureEnabled: (serverId) => this.pipedCaptureByServerId.get(serverId) === true,
|
|
32163
|
+
e2ee: this.e2ee
|
|
32164
|
+
});
|
|
32074
32165
|
}
|
|
32075
32166
|
attachFirehose(send) {
|
|
32076
|
-
this.
|
|
32167
|
+
this.firehoseSink.attach(send);
|
|
32077
32168
|
}
|
|
32078
32169
|
detachFirehose() {
|
|
32079
|
-
this.
|
|
32080
|
-
this.logViewerRefCountByServerId.clear();
|
|
32170
|
+
this.firehoseSink.detach();
|
|
32081
32171
|
}
|
|
32082
32172
|
handleFirehoseLogViewerOpen(serverId, _viewerId) {
|
|
32083
|
-
|
|
32084
|
-
this.logViewerRefCountByServerId.set(serverId, next);
|
|
32085
|
-
this.sendSnapshotToFirehose(serverId, _viewerId);
|
|
32173
|
+
this.firehoseSink.openLogViewer(serverId, _viewerId);
|
|
32086
32174
|
}
|
|
32087
32175
|
handleFirehoseLogViewerClose(serverId, _viewerId) {
|
|
32088
|
-
|
|
32089
|
-
if (n <= 0) this.logViewerRefCountByServerId.delete(serverId);
|
|
32090
|
-
else this.logViewerRefCountByServerId.set(serverId, n);
|
|
32091
|
-
}
|
|
32092
|
-
sendSnapshotToFirehose(serverId, viewerId) {
|
|
32093
|
-
const tails = this.streamTailsByServerId.get(serverId);
|
|
32094
|
-
const payload = {
|
|
32095
|
-
type: "log_snapshot",
|
|
32096
|
-
serverId,
|
|
32097
|
-
viewerId,
|
|
32098
|
-
stdoutTail: tails?.stdout.getTail() ?? [],
|
|
32099
|
-
stderrTail: tails?.stderr.getTail() ?? []
|
|
32100
|
-
};
|
|
32101
|
-
setImmediate(() => {
|
|
32102
|
-
const send = this.firehoseSend;
|
|
32103
|
-
if (!send) return;
|
|
32104
|
-
send(payload);
|
|
32105
|
-
});
|
|
32106
|
-
}
|
|
32107
|
-
pushRemoteLogChunk(serverId, stream, chunk) {
|
|
32108
|
-
if ((this.logViewerRefCountByServerId.get(serverId) ?? 0) <= 0) return;
|
|
32109
|
-
if (!this.pipedCaptureByServerId.get(serverId)) return;
|
|
32110
|
-
const send = this.firehoseSend;
|
|
32111
|
-
if (!send) return;
|
|
32112
|
-
const text = chunk.toString("utf8");
|
|
32113
|
-
setImmediate(() => {
|
|
32114
|
-
if (!this.firehoseSend) return;
|
|
32115
|
-
this.firehoseSend({
|
|
32116
|
-
type: "log_chunk",
|
|
32117
|
-
serverId,
|
|
32118
|
-
stream,
|
|
32119
|
-
text
|
|
32120
|
-
});
|
|
32121
|
-
});
|
|
32176
|
+
this.firehoseSink.closeLogViewer(serverId);
|
|
32122
32177
|
}
|
|
32123
32178
|
applyConfig(servers) {
|
|
32124
32179
|
this.defsById.clear();
|
|
@@ -32171,12 +32226,7 @@ var DevServerManager = class {
|
|
|
32171
32226
|
}
|
|
32172
32227
|
this.clearTails(serverId);
|
|
32173
32228
|
this.pipedCaptureByServerId.delete(serverId);
|
|
32174
|
-
|
|
32175
|
-
if (mergedDir) {
|
|
32176
|
-
this.mergedLogCleanupDirByServerId.delete(serverId);
|
|
32177
|
-
void rm(mergedDir, { recursive: true, force: true }).catch(() => {
|
|
32178
|
-
});
|
|
32179
|
-
}
|
|
32229
|
+
cleanupMergedLogDirForServer(this.mergedLogCleanupDirByServerId, serverId);
|
|
32180
32230
|
this.sendStatus(serverId, "stopped", void 0, tails);
|
|
32181
32231
|
}
|
|
32182
32232
|
start(serverId) {
|
|
@@ -32259,7 +32309,7 @@ var DevServerManager = class {
|
|
|
32259
32309
|
log: this.log,
|
|
32260
32310
|
stdoutTail,
|
|
32261
32311
|
stderrTail,
|
|
32262
|
-
pushRemoteLogChunk: (sid, stream, chunk) => this.
|
|
32312
|
+
pushRemoteLogChunk: (sid, stream, chunk) => this.firehoseSink.pushLogChunk(sid, stream, chunk),
|
|
32263
32313
|
sendStatus: (status, detail, tails) => this.sendStatus(serverId, status, detail, tails),
|
|
32264
32314
|
setPollInterval: (iv) => {
|
|
32265
32315
|
if (iv) this.mergedLogPollByServerId.set(serverId, iv);
|
|
@@ -32273,7 +32323,7 @@ var DevServerManager = class {
|
|
|
32273
32323
|
this.mergedLogCleanupDirByServerId.delete(serverId);
|
|
32274
32324
|
},
|
|
32275
32325
|
rmMergedCleanupDir: (dir) => {
|
|
32276
|
-
void
|
|
32326
|
+
void rm2(dir, { recursive: true, force: true }).catch(() => {
|
|
32277
32327
|
});
|
|
32278
32328
|
},
|
|
32279
32329
|
clearTailBuffers: () => this.clearTails(serverId)
|
|
@@ -32310,12 +32360,7 @@ var DevServerManager = class {
|
|
|
32310
32360
|
this.processes.delete(serverId);
|
|
32311
32361
|
this.clearPoll(serverId);
|
|
32312
32362
|
this.pipedCaptureByServerId.delete(serverId);
|
|
32313
|
-
|
|
32314
|
-
if (mergedDir) {
|
|
32315
|
-
this.mergedLogCleanupDirByServerId.delete(serverId);
|
|
32316
|
-
void rm(mergedDir, { recursive: true, force: true }).catch(() => {
|
|
32317
|
-
});
|
|
32318
|
-
}
|
|
32363
|
+
cleanupMergedLogDirForServer(this.mergedLogCleanupDirByServerId, serverId);
|
|
32319
32364
|
const tails = this.snapshotTails(serverId);
|
|
32320
32365
|
this.clearTails(serverId);
|
|
32321
32366
|
this.sendStatus(serverId, "unknown", "Bridge closed before process exited", tails);
|
|
@@ -32775,7 +32820,7 @@ function reportGitRepos(getWs, log2) {
|
|
|
32775
32820
|
var handleAuthToken = (msg, { log: log2 }) => {
|
|
32776
32821
|
if (typeof msg.token !== "string") return;
|
|
32777
32822
|
log2("Received auth token. Save it for future runs:");
|
|
32778
|
-
log2(` export
|
|
32823
|
+
log2(` export BUILDAUTOMATON_AUTH_TOKEN="${msg.token}"`);
|
|
32779
32824
|
};
|
|
32780
32825
|
|
|
32781
32826
|
// src/bridge/routing/handlers/bridge-identified.ts
|
|
@@ -32895,21 +32940,28 @@ function handleBridgePrompt(msg, deps) {
|
|
|
32895
32940
|
const sessionId = msg.sessionId;
|
|
32896
32941
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
32897
32942
|
const promptId = typeof msg.id === "string" ? msg.id : void 0;
|
|
32943
|
+
const sendBridgeMessage = (message, encryptedFields = []) => {
|
|
32944
|
+
const s = getWs();
|
|
32945
|
+
if (!s) return false;
|
|
32946
|
+
const wire = deps.e2ee && encryptedFields.length > 0 ? deps.e2ee.encryptFields(message, encryptedFields) : message;
|
|
32947
|
+
sendWsMessage(s, wire);
|
|
32948
|
+
return true;
|
|
32949
|
+
};
|
|
32898
32950
|
if (!promptText.trim()) {
|
|
32899
32951
|
log2(
|
|
32900
32952
|
`[Bridge service] Prompt ignored: empty or missing prompt text (session ${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026, run ${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026).`
|
|
32901
32953
|
);
|
|
32902
|
-
|
|
32903
|
-
|
|
32904
|
-
sendWsMessage(s, {
|
|
32954
|
+
sendBridgeMessage(
|
|
32955
|
+
{
|
|
32905
32956
|
type: "prompt_result",
|
|
32906
32957
|
...promptId ? { id: promptId } : {},
|
|
32907
32958
|
...sessionId ? { sessionId } : {},
|
|
32908
32959
|
...runId ? { runId } : {},
|
|
32909
32960
|
success: false,
|
|
32910
32961
|
error: "Empty or missing prompt text from the bridge; this turn was not sent to the agent."
|
|
32911
|
-
}
|
|
32912
|
-
|
|
32962
|
+
},
|
|
32963
|
+
["error"]
|
|
32964
|
+
);
|
|
32913
32965
|
return;
|
|
32914
32966
|
}
|
|
32915
32967
|
const isNewSession = msg.isNewSession === true;
|
|
@@ -32918,8 +32970,7 @@ function handleBridgePrompt(msg, deps) {
|
|
|
32918
32970
|
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
32919
32971
|
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
32920
32972
|
const sendResult2 = (result) => {
|
|
32921
|
-
|
|
32922
|
-
if (s) sendWsMessage(s, result);
|
|
32973
|
+
sendBridgeMessage(result, result.type === "prompt_result" ? ["output", "error"] : []);
|
|
32923
32974
|
};
|
|
32924
32975
|
const sendSessionUpdate = (payload) => {
|
|
32925
32976
|
const s = getWs();
|
|
@@ -32928,7 +32979,15 @@ function handleBridgePrompt(msg, deps) {
|
|
|
32928
32979
|
return;
|
|
32929
32980
|
}
|
|
32930
32981
|
const p = payload;
|
|
32931
|
-
|
|
32982
|
+
const wire = p.type === "session_update" && deps.e2ee ? deps.e2ee.encryptFields(payload, ["payload"]) : p.type === "session_file_change" && deps.e2ee ? deps.e2ee.encryptFields(payload, [
|
|
32983
|
+
"path",
|
|
32984
|
+
"oldText",
|
|
32985
|
+
"newText",
|
|
32986
|
+
"patchContent",
|
|
32987
|
+
"isDirectory",
|
|
32988
|
+
"directoryRemoved"
|
|
32989
|
+
]) : payload;
|
|
32990
|
+
sendWsMessage(s, wire);
|
|
32932
32991
|
};
|
|
32933
32992
|
async function preambleAndPrompt(resolvedCwd) {
|
|
32934
32993
|
const s = getWs();
|
|
@@ -33309,28 +33368,30 @@ async function readFileAsync(relativePath, startLine, endLine, lineOffset, lineC
|
|
|
33309
33368
|
|
|
33310
33369
|
// src/files/handle-file-browser-search.ts
|
|
33311
33370
|
var SEARCH_LIMIT = 100;
|
|
33312
|
-
function handleFileBrowserSearch(msg, socket) {
|
|
33371
|
+
function handleFileBrowserSearch(msg, socket, e2ee) {
|
|
33313
33372
|
void (async () => {
|
|
33314
33373
|
await yieldToEventLoop();
|
|
33315
33374
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
33316
33375
|
const cwd = getBridgeWorkspaceDirectory();
|
|
33317
33376
|
const index = loadFileIndex(cwd);
|
|
33318
33377
|
if (index === null) {
|
|
33319
|
-
|
|
33378
|
+
const payload2 = {
|
|
33320
33379
|
type: "file_browser_search_response",
|
|
33321
33380
|
id: msg.id,
|
|
33322
33381
|
paths: [],
|
|
33323
33382
|
indexReady: false
|
|
33324
|
-
}
|
|
33383
|
+
};
|
|
33384
|
+
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload2, ["paths"]) : payload2);
|
|
33325
33385
|
return;
|
|
33326
33386
|
}
|
|
33327
33387
|
const results = await searchFileIndexAsync(index, q, SEARCH_LIMIT);
|
|
33328
|
-
|
|
33388
|
+
const payload = {
|
|
33329
33389
|
type: "file_browser_search_response",
|
|
33330
33390
|
id: msg.id,
|
|
33331
33391
|
paths: results,
|
|
33332
33392
|
indexReady: true
|
|
33333
|
-
}
|
|
33393
|
+
};
|
|
33394
|
+
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["paths"]) : payload);
|
|
33334
33395
|
})();
|
|
33335
33396
|
}
|
|
33336
33397
|
function triggerFileIndexBuild() {
|
|
@@ -33342,7 +33403,10 @@ function triggerFileIndexBuild() {
|
|
|
33342
33403
|
}
|
|
33343
33404
|
|
|
33344
33405
|
// src/files/handle-file-browser-request.ts
|
|
33345
|
-
function
|
|
33406
|
+
function sendFileBrowserMessage(socket, e2ee, payload) {
|
|
33407
|
+
sendWsMessage(socket, e2ee ? e2ee.encryptFields(payload, ["entries", "content", "totalLines", "size", "lineOffset"]) : payload);
|
|
33408
|
+
}
|
|
33409
|
+
function handleFileBrowserRequest(msg, socket, e2ee) {
|
|
33346
33410
|
void (async () => {
|
|
33347
33411
|
const reqPath = msg.path.replace(/^\/+/, "") || ".";
|
|
33348
33412
|
const op = msg.op === "read" ? "read" : "list";
|
|
@@ -33351,7 +33415,7 @@ function handleFileBrowserRequest(msg, socket) {
|
|
|
33351
33415
|
if ("error" in result) {
|
|
33352
33416
|
sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
|
|
33353
33417
|
} else {
|
|
33354
|
-
|
|
33418
|
+
sendFileBrowserMessage(socket, e2ee, { type: "file_browser_response", id: msg.id, entries: result.entries });
|
|
33355
33419
|
if (reqPath === "." || reqPath === "") {
|
|
33356
33420
|
triggerFileIndexBuild();
|
|
33357
33421
|
}
|
|
@@ -33373,27 +33437,28 @@ function handleFileBrowserRequest(msg, socket) {
|
|
|
33373
33437
|
size: result.size
|
|
33374
33438
|
};
|
|
33375
33439
|
if (result.lineOffset != null) payload.lineOffset = result.lineOffset;
|
|
33376
|
-
|
|
33440
|
+
sendFileBrowserMessage(socket, e2ee, payload);
|
|
33377
33441
|
}
|
|
33378
33442
|
}
|
|
33379
33443
|
})();
|
|
33380
33444
|
}
|
|
33381
33445
|
|
|
33382
33446
|
// src/bridge/routing/handlers/file-browser-messages.ts
|
|
33383
|
-
function handleFileBrowserRequestMessage(msg, { getWs }) {
|
|
33447
|
+
function handleFileBrowserRequestMessage(msg, { getWs, e2ee }) {
|
|
33384
33448
|
if (typeof msg.id !== "string" || typeof msg.path !== "string") return;
|
|
33385
33449
|
const socket = getWs();
|
|
33386
33450
|
if (!socket) return;
|
|
33387
33451
|
handleFileBrowserRequest(
|
|
33388
33452
|
msg,
|
|
33389
|
-
socket
|
|
33453
|
+
socket,
|
|
33454
|
+
e2ee
|
|
33390
33455
|
);
|
|
33391
33456
|
}
|
|
33392
|
-
function handleFileBrowserSearchMessage(msg, { getWs }) {
|
|
33457
|
+
function handleFileBrowserSearchMessage(msg, { getWs, e2ee }) {
|
|
33393
33458
|
if (typeof msg.id !== "string") return;
|
|
33394
33459
|
const socket = getWs();
|
|
33395
33460
|
if (!socket) return;
|
|
33396
|
-
handleFileBrowserSearch(msg, socket);
|
|
33461
|
+
handleFileBrowserSearch(msg, socket, e2ee);
|
|
33397
33462
|
}
|
|
33398
33463
|
|
|
33399
33464
|
// src/bridge/routing/handlers/skill-layout-request.ts
|
|
@@ -33463,9 +33528,10 @@ var handleRefreshLocalSkills = (_msg, deps) => {
|
|
|
33463
33528
|
};
|
|
33464
33529
|
|
|
33465
33530
|
// src/bridge/routing/handlers/session-git-request.ts
|
|
33466
|
-
function sendResult(ws, id, payload) {
|
|
33531
|
+
function sendResult(ws, id, payload, e2ee, encryptedFields = []) {
|
|
33467
33532
|
if (!ws) return;
|
|
33468
|
-
|
|
33533
|
+
const message = { type: "session_git_result", id, ...payload };
|
|
33534
|
+
sendWsMessage(ws, e2ee && encryptedFields.length > 0 ? e2ee.encryptFields(message, encryptedFields) : message);
|
|
33469
33535
|
}
|
|
33470
33536
|
var handleSessionGitRequestMessage = (msg, deps) => {
|
|
33471
33537
|
if (typeof msg.id !== "string") return;
|
|
@@ -33475,7 +33541,7 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
33475
33541
|
return;
|
|
33476
33542
|
void (async () => {
|
|
33477
33543
|
const ws = deps.getWs();
|
|
33478
|
-
const reply = (payload) => sendResult(ws, msg.id, payload);
|
|
33544
|
+
const reply = (payload, encryptedFields = []) => sendResult(ws, msg.id, payload, deps.e2ee, encryptedFields);
|
|
33479
33545
|
try {
|
|
33480
33546
|
if (action === "status") {
|
|
33481
33547
|
const r = await deps.sessionWorktreeManager.getSessionWorkingTreeStatus(sessionId);
|
|
@@ -33501,7 +33567,7 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
33501
33567
|
reply({
|
|
33502
33568
|
ok: true,
|
|
33503
33569
|
repos
|
|
33504
|
-
});
|
|
33570
|
+
}, ["repos"]);
|
|
33505
33571
|
return;
|
|
33506
33572
|
}
|
|
33507
33573
|
if (action === "push") {
|
|
@@ -33603,8 +33669,15 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
33603
33669
|
|
|
33604
33670
|
// src/bridge/routing/handlers/dev-server-control.ts
|
|
33605
33671
|
var handleDevServerControl = (msg, deps) => {
|
|
33606
|
-
|
|
33607
|
-
|
|
33672
|
+
let wire = msg;
|
|
33673
|
+
try {
|
|
33674
|
+
wire = deps.e2ee ? deps.e2ee.decryptMessage(msg) : msg;
|
|
33675
|
+
} catch (e) {
|
|
33676
|
+
deps.log(`[E2EE] Could not decrypt dev server command: ${e instanceof Error ? e.message : String(e)}`);
|
|
33677
|
+
return;
|
|
33678
|
+
}
|
|
33679
|
+
const serverId = typeof wire.serverId === "string" ? wire.serverId : "";
|
|
33680
|
+
const action = wire.action === "start" || wire.action === "stop" ? wire.action : null;
|
|
33608
33681
|
if (!serverId || !action) return;
|
|
33609
33682
|
deps.devServerManager?.handleControl(serverId, action);
|
|
33610
33683
|
};
|
|
@@ -33730,7 +33803,8 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
33730
33803
|
messageDeps,
|
|
33731
33804
|
tokens,
|
|
33732
33805
|
persistTokens,
|
|
33733
|
-
onAuthInvalid
|
|
33806
|
+
onAuthInvalid,
|
|
33807
|
+
e2ee
|
|
33734
33808
|
} = params;
|
|
33735
33809
|
let authRefreshInFlight = false;
|
|
33736
33810
|
function handleOpen() {
|
|
@@ -33743,15 +33817,15 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
33743
33817
|
}
|
|
33744
33818
|
const socket = getWs();
|
|
33745
33819
|
if (socket) {
|
|
33746
|
-
sendWsMessage(socket, { type: "identify", role: "cli" });
|
|
33820
|
+
sendWsMessage(socket, { type: "identify", role: "cli", ...e2ee ? { e: e2ee.handshake } : {} });
|
|
33747
33821
|
reportGitRepos(getWs, logFn);
|
|
33748
33822
|
}
|
|
33749
33823
|
if (justAuthenticated && socket) {
|
|
33750
33824
|
logFn(
|
|
33751
33825
|
"Save these for future runs (access token may rotate; refresh token is stored in ~/.buildautomaton/config.json when you use browser auth):"
|
|
33752
33826
|
);
|
|
33753
|
-
logFn(` export
|
|
33754
|
-
logFn(` export
|
|
33827
|
+
logFn(` export BUILDAUTOMATON_AUTH_TOKEN="${tokens.accessToken}"`);
|
|
33828
|
+
logFn(` export BUILDAUTOMATON_WORKSPACE_ID="${workspaceId}"`);
|
|
33755
33829
|
}
|
|
33756
33830
|
}
|
|
33757
33831
|
function handleClose(code, reason) {
|
|
@@ -33833,6 +33907,100 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
33833
33907
|
return { connect };
|
|
33834
33908
|
}
|
|
33835
33909
|
|
|
33910
|
+
// ../e2ee/src/constants.ts
|
|
33911
|
+
var E2EE_NONCE_BYTES = 12;
|
|
33912
|
+
|
|
33913
|
+
// ../e2ee/src/types.ts
|
|
33914
|
+
function isE2eeEnvelope(value) {
|
|
33915
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
33916
|
+
const o = value;
|
|
33917
|
+
return typeof o.k === "string" && typeof o.n === "string" && typeof o.c === "string";
|
|
33918
|
+
}
|
|
33919
|
+
|
|
33920
|
+
// ../e2ee/src/encoding.ts
|
|
33921
|
+
function base64UrlEncode(bytes) {
|
|
33922
|
+
let binary = "";
|
|
33923
|
+
for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
|
|
33924
|
+
const b64 = btoa(binary);
|
|
33925
|
+
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
33926
|
+
}
|
|
33927
|
+
function base64UrlDecode(value) {
|
|
33928
|
+
const b64 = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
33929
|
+
const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
|
|
33930
|
+
const binary = atob(padded);
|
|
33931
|
+
const out = new Uint8Array(binary.length);
|
|
33932
|
+
for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
|
|
33933
|
+
return out;
|
|
33934
|
+
}
|
|
33935
|
+
|
|
33936
|
+
// src/lib/e2ee/cli-e2ee-runtime.ts
|
|
33937
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
33938
|
+
function nonceFromCounter(prefix, counter) {
|
|
33939
|
+
const nonce = Buffer.alloc(E2EE_NONCE_BYTES);
|
|
33940
|
+
prefix.copy(nonce, 0);
|
|
33941
|
+
nonce.writeBigUInt64BE(counter, 4);
|
|
33942
|
+
return nonce;
|
|
33943
|
+
}
|
|
33944
|
+
function createCliE2eeRuntime(key) {
|
|
33945
|
+
const rawKey = Buffer.from(base64UrlDecode(key.k));
|
|
33946
|
+
const prefix = randomBytes(4);
|
|
33947
|
+
let counter = 0n;
|
|
33948
|
+
function nextNonce() {
|
|
33949
|
+
counter += 1n;
|
|
33950
|
+
return nonceFromCounter(prefix, counter);
|
|
33951
|
+
}
|
|
33952
|
+
function encryptObject(messageWithoutSensitiveFields, plaintext) {
|
|
33953
|
+
const nonce = nextNonce();
|
|
33954
|
+
const cipher = createCipheriv("aes-256-gcm", rawKey, nonce);
|
|
33955
|
+
const ciphertext = Buffer.concat([cipher.update(JSON.stringify(plaintext), "utf8"), cipher.final()]);
|
|
33956
|
+
const tag = cipher.getAuthTag();
|
|
33957
|
+
return {
|
|
33958
|
+
k: key.id,
|
|
33959
|
+
n: base64UrlEncode(nonce),
|
|
33960
|
+
c: base64UrlEncode(Buffer.concat([ciphertext, tag]))
|
|
33961
|
+
};
|
|
33962
|
+
}
|
|
33963
|
+
return {
|
|
33964
|
+
keyId: key.id,
|
|
33965
|
+
handshake: { k: key.id, a: key.a },
|
|
33966
|
+
encryptFields(message, fields) {
|
|
33967
|
+
const plaintext = {};
|
|
33968
|
+
const stripped = { ...message };
|
|
33969
|
+
let hasPlaintext = false;
|
|
33970
|
+
for (const field of fields) {
|
|
33971
|
+
if (Object.prototype.hasOwnProperty.call(stripped, field) && stripped[field] !== void 0) {
|
|
33972
|
+
plaintext[field] = stripped[field];
|
|
33973
|
+
delete stripped[field];
|
|
33974
|
+
hasPlaintext = true;
|
|
33975
|
+
}
|
|
33976
|
+
}
|
|
33977
|
+
if (!hasPlaintext) return message;
|
|
33978
|
+
stripped.ee = encryptObject(stripped, plaintext);
|
|
33979
|
+
return stripped;
|
|
33980
|
+
},
|
|
33981
|
+
decryptMessage(message) {
|
|
33982
|
+
const envelope = message.ee;
|
|
33983
|
+
if (!isE2eeEnvelope(envelope)) return message;
|
|
33984
|
+
if (envelope.k !== key.id) throw new Error(`E2EE key mismatch: ${envelope.k}`);
|
|
33985
|
+
const sealed = Buffer.from(base64UrlDecode(envelope.c));
|
|
33986
|
+
if (sealed.length < 16) throw new Error("Invalid E2EE payload.");
|
|
33987
|
+
const ciphertext = sealed.subarray(0, sealed.length - 16);
|
|
33988
|
+
const tag = sealed.subarray(sealed.length - 16);
|
|
33989
|
+
const nonce = Buffer.from(base64UrlDecode(envelope.n));
|
|
33990
|
+
const decipher = createDecipheriv("aes-256-gcm", rawKey, nonce);
|
|
33991
|
+
decipher.setAuthTag(tag);
|
|
33992
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
33993
|
+
const parsed = JSON.parse(decrypted);
|
|
33994
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
33995
|
+
throw new Error("E2EE payload did not decode to an object.");
|
|
33996
|
+
}
|
|
33997
|
+
const merged = { ...message, ...parsed };
|
|
33998
|
+
delete merged.ee;
|
|
33999
|
+
return merged;
|
|
34000
|
+
}
|
|
34001
|
+
};
|
|
34002
|
+
}
|
|
34003
|
+
|
|
33836
34004
|
// src/bridge/connection/create-bridge-connection.ts
|
|
33837
34005
|
async function createBridgeConnection(options) {
|
|
33838
34006
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
@@ -33866,7 +34034,8 @@ async function createBridgeConnection(options) {
|
|
|
33866
34034
|
function getWs() {
|
|
33867
34035
|
return state.currentWs;
|
|
33868
34036
|
}
|
|
33869
|
-
const
|
|
34037
|
+
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
34038
|
+
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeCwd: getBridgeWorkspaceDirectory, e2ee });
|
|
33870
34039
|
const onBridgeIdentified = createOnBridgeIdentified({
|
|
33871
34040
|
sessionWorktreeManager,
|
|
33872
34041
|
devServerManager,
|
|
@@ -33885,7 +34054,8 @@ async function createBridgeConnection(options) {
|
|
|
33885
34054
|
onBridgeIdentified,
|
|
33886
34055
|
sendLocalSkillsReport,
|
|
33887
34056
|
reportAutoDetectedAgents,
|
|
33888
|
-
devServerManager
|
|
34057
|
+
devServerManager,
|
|
34058
|
+
e2ee
|
|
33889
34059
|
};
|
|
33890
34060
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
33891
34061
|
state,
|
|
@@ -33897,7 +34067,8 @@ async function createBridgeConnection(options) {
|
|
|
33897
34067
|
messageDeps,
|
|
33898
34068
|
tokens,
|
|
33899
34069
|
persistTokens,
|
|
33900
|
-
onAuthInvalid
|
|
34070
|
+
onAuthInvalid,
|
|
34071
|
+
e2ee
|
|
33901
34072
|
});
|
|
33902
34073
|
connect();
|
|
33903
34074
|
const stopFileIndexWatcher = startFileIndexWatcher(getBridgeWorkspaceDirectory());
|
|
@@ -33909,56 +34080,72 @@ async function createBridgeConnection(options) {
|
|
|
33909
34080
|
};
|
|
33910
34081
|
}
|
|
33911
34082
|
|
|
33912
|
-
// src/
|
|
33913
|
-
|
|
33914
|
-
|
|
33915
|
-
|
|
33916
|
-
|
|
33917
|
-
|
|
33918
|
-
|
|
33919
|
-
|
|
33920
|
-
|
|
33921
|
-
|
|
33922
|
-
preferredBridgeName: bridgeName,
|
|
33923
|
-
log,
|
|
33924
|
-
onAuth: (_auth) => {
|
|
33925
|
-
}
|
|
33926
|
-
});
|
|
33927
|
-
const onSignal2 = (kind) => {
|
|
33928
|
-
logImmediate(
|
|
33929
|
-
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
33930
|
-
);
|
|
33931
|
-
setImmediate(() => {
|
|
33932
|
-
handle2.close();
|
|
33933
|
-
process.exit(0);
|
|
33934
|
-
});
|
|
34083
|
+
// src/e2e-certificates/key-command.ts
|
|
34084
|
+
import * as readline3 from "node:readline";
|
|
34085
|
+
function installE2eCertificateKeyCommand({
|
|
34086
|
+
log: log2,
|
|
34087
|
+
onOpenCertificate,
|
|
34088
|
+
onInterrupt
|
|
34089
|
+
}) {
|
|
34090
|
+
if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== "function") {
|
|
34091
|
+
log2("[E2EE] Press c to import the E2EE key in a browser when running in an interactive terminal.");
|
|
34092
|
+
return () => {
|
|
33935
34093
|
};
|
|
33936
|
-
const onSigInt2 = () => onSignal2("interrupt");
|
|
33937
|
-
const onSigTerm2 = () => onSignal2("stop");
|
|
33938
|
-
process.on("SIGINT", onSigInt2);
|
|
33939
|
-
process.on("SIGTERM", onSigTerm2);
|
|
33940
|
-
const auth = await handle2.authPromise;
|
|
33941
|
-
process.off("SIGINT", onSigInt2);
|
|
33942
|
-
process.off("SIGTERM", onSigTerm2);
|
|
33943
|
-
handle2.close();
|
|
33944
|
-
if (!auth) return;
|
|
33945
|
-
writeConfigForApi(apiUrl, {
|
|
33946
|
-
workspaceId: auth.workspaceId,
|
|
33947
|
-
token: auth.token,
|
|
33948
|
-
refreshToken: auth.refreshToken
|
|
33949
|
-
});
|
|
33950
|
-
await runBridge({
|
|
33951
|
-
apiUrl,
|
|
33952
|
-
workspaceId: auth.workspaceId,
|
|
33953
|
-
authToken: auth.token,
|
|
33954
|
-
refreshToken: auth.refreshToken,
|
|
33955
|
-
firehoseServerUrl,
|
|
33956
|
-
bridgeName,
|
|
33957
|
-
justAuthenticated: true,
|
|
33958
|
-
worktreesRootAbs
|
|
33959
|
-
});
|
|
33960
|
-
return;
|
|
33961
34094
|
}
|
|
34095
|
+
readline3.emitKeypressEvents(process.stdin);
|
|
34096
|
+
process.stdin.setRawMode(true);
|
|
34097
|
+
process.stdin.resume();
|
|
34098
|
+
const onKeypress = (str, key) => {
|
|
34099
|
+
if (key?.ctrl && key.name === "c") {
|
|
34100
|
+
onInterrupt();
|
|
34101
|
+
return;
|
|
34102
|
+
}
|
|
34103
|
+
if (!key?.ctrl && !key?.meta && (key?.name === "c" || str === "c")) {
|
|
34104
|
+
onOpenCertificate();
|
|
34105
|
+
}
|
|
34106
|
+
};
|
|
34107
|
+
process.stdin.on("keypress", onKeypress);
|
|
34108
|
+
log2("[E2EE] Press c to import the active E2EE key into the browser.");
|
|
34109
|
+
return () => {
|
|
34110
|
+
process.stdin.off("keypress", onKeypress);
|
|
34111
|
+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
34112
|
+
process.stdin.setRawMode(false);
|
|
34113
|
+
}
|
|
34114
|
+
};
|
|
34115
|
+
}
|
|
34116
|
+
|
|
34117
|
+
// src/e2e-certificates/open-import-url.ts
|
|
34118
|
+
async function openE2eCertificateImportUrl({
|
|
34119
|
+
apiUrl,
|
|
34120
|
+
workspaceId,
|
|
34121
|
+
certificate,
|
|
34122
|
+
log: log2
|
|
34123
|
+
}) {
|
|
34124
|
+
const appUrl = appUrlForApiUrl(apiUrl);
|
|
34125
|
+
const payload = encodeURIComponent(certificate.pemBundle);
|
|
34126
|
+
const url2 = `${appUrl.replace(/\/$/, "")}/w/${encodeURIComponent(workspaceId)}/settings/e2e-encryption?certificate=${payload}`;
|
|
34127
|
+
log2(`[E2EE] Opening browser to import key "${certificate.name}" (${certificate.id})...`);
|
|
34128
|
+
try {
|
|
34129
|
+
await open_default(url2, { wait: false });
|
|
34130
|
+
} catch {
|
|
34131
|
+
log2("[E2EE] Could not open browser. Open this URL manually:");
|
|
34132
|
+
log2(url2);
|
|
34133
|
+
}
|
|
34134
|
+
}
|
|
34135
|
+
|
|
34136
|
+
// src/run-bridge-connected.ts
|
|
34137
|
+
async function runConnectedBridge(options, restartWithoutAuth) {
|
|
34138
|
+
const {
|
|
34139
|
+
apiUrl,
|
|
34140
|
+
workspaceId,
|
|
34141
|
+
authToken,
|
|
34142
|
+
refreshToken,
|
|
34143
|
+
justAuthenticated,
|
|
34144
|
+
worktreesRootAbs,
|
|
34145
|
+
e2eCertificate
|
|
34146
|
+
} = options;
|
|
34147
|
+
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
34148
|
+
let cleanupKeyCommand;
|
|
33962
34149
|
const handle = await createBridgeConnection({
|
|
33963
34150
|
apiUrl,
|
|
33964
34151
|
workspaceId,
|
|
@@ -33967,6 +34154,7 @@ async function runBridge(options) {
|
|
|
33967
34154
|
firehoseServerUrl,
|
|
33968
34155
|
justAuthenticated,
|
|
33969
34156
|
worktreesRootAbs,
|
|
34157
|
+
e2eCertificate,
|
|
33970
34158
|
log,
|
|
33971
34159
|
persistTokens: (t) => {
|
|
33972
34160
|
writeConfigForApi(apiUrl, {
|
|
@@ -33976,14 +34164,16 @@ async function runBridge(options) {
|
|
|
33976
34164
|
});
|
|
33977
34165
|
},
|
|
33978
34166
|
onAuthInvalid: () => {
|
|
34167
|
+
cleanupKeyCommand?.();
|
|
33979
34168
|
log("[Bridge service] Access token invalid or revoked; re-authenticating\u2026");
|
|
33980
34169
|
clearConfigForApi(apiUrl);
|
|
33981
34170
|
void handle.close().then(() => {
|
|
33982
|
-
void
|
|
34171
|
+
void restartWithoutAuth({ apiUrl, firehoseServerUrl, worktreesRootAbs, e2eCertificate });
|
|
33983
34172
|
});
|
|
33984
34173
|
}
|
|
33985
34174
|
});
|
|
33986
34175
|
const onSignal = (kind) => {
|
|
34176
|
+
cleanupKeyCommand?.();
|
|
33987
34177
|
logImmediate(
|
|
33988
34178
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
33989
34179
|
);
|
|
@@ -33997,6 +34187,86 @@ async function runBridge(options) {
|
|
|
33997
34187
|
const onSigTerm = () => onSignal("stop");
|
|
33998
34188
|
process.on("SIGINT", onSigInt);
|
|
33999
34189
|
process.on("SIGTERM", onSigTerm);
|
|
34190
|
+
if (e2eCertificate) {
|
|
34191
|
+
let openingCertificate = false;
|
|
34192
|
+
cleanupKeyCommand = installE2eCertificateKeyCommand({
|
|
34193
|
+
log,
|
|
34194
|
+
onInterrupt: onSigInt,
|
|
34195
|
+
onOpenCertificate: () => {
|
|
34196
|
+
if (openingCertificate) return;
|
|
34197
|
+
openingCertificate = true;
|
|
34198
|
+
void openE2eCertificateImportUrl({
|
|
34199
|
+
apiUrl,
|
|
34200
|
+
workspaceId,
|
|
34201
|
+
certificate: e2eCertificate,
|
|
34202
|
+
log
|
|
34203
|
+
}).finally(() => {
|
|
34204
|
+
openingCertificate = false;
|
|
34205
|
+
});
|
|
34206
|
+
}
|
|
34207
|
+
});
|
|
34208
|
+
}
|
|
34209
|
+
}
|
|
34210
|
+
|
|
34211
|
+
// src/run-bridge.ts
|
|
34212
|
+
async function runBridge(options) {
|
|
34213
|
+
installBridgeProcessResilience();
|
|
34214
|
+
const {
|
|
34215
|
+
apiUrl,
|
|
34216
|
+
workspaceId,
|
|
34217
|
+
authToken,
|
|
34218
|
+
bridgeName,
|
|
34219
|
+
worktreesRootAbs,
|
|
34220
|
+
e2eCertificate
|
|
34221
|
+
} = options;
|
|
34222
|
+
const firehoseServerUrl = options.firehoseServerUrl ?? options.proxyServerUrl;
|
|
34223
|
+
const hasAuth = workspaceId && authToken;
|
|
34224
|
+
if (!hasAuth) {
|
|
34225
|
+
const handle = runPendingAuth({
|
|
34226
|
+
apiUrl,
|
|
34227
|
+
initialWorkspaceId: workspaceId,
|
|
34228
|
+
preferredBridgeName: bridgeName,
|
|
34229
|
+
log,
|
|
34230
|
+
onAuth: (_auth) => {
|
|
34231
|
+
}
|
|
34232
|
+
});
|
|
34233
|
+
const onSignal = (kind) => {
|
|
34234
|
+
logImmediate(
|
|
34235
|
+
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|
|
34236
|
+
);
|
|
34237
|
+
setImmediate(() => {
|
|
34238
|
+
handle.close();
|
|
34239
|
+
process.exit(0);
|
|
34240
|
+
});
|
|
34241
|
+
};
|
|
34242
|
+
const onSigInt = () => onSignal("interrupt");
|
|
34243
|
+
const onSigTerm = () => onSignal("stop");
|
|
34244
|
+
process.on("SIGINT", onSigInt);
|
|
34245
|
+
process.on("SIGTERM", onSigTerm);
|
|
34246
|
+
const auth = await handle.authPromise;
|
|
34247
|
+
process.off("SIGINT", onSigInt);
|
|
34248
|
+
process.off("SIGTERM", onSigTerm);
|
|
34249
|
+
handle.close();
|
|
34250
|
+
if (!auth) return;
|
|
34251
|
+
writeConfigForApi(apiUrl, {
|
|
34252
|
+
workspaceId: auth.workspaceId,
|
|
34253
|
+
token: auth.token,
|
|
34254
|
+
refreshToken: auth.refreshToken
|
|
34255
|
+
});
|
|
34256
|
+
await runBridge({
|
|
34257
|
+
apiUrl,
|
|
34258
|
+
workspaceId: auth.workspaceId,
|
|
34259
|
+
authToken: auth.token,
|
|
34260
|
+
refreshToken: auth.refreshToken,
|
|
34261
|
+
firehoseServerUrl,
|
|
34262
|
+
bridgeName,
|
|
34263
|
+
justAuthenticated: true,
|
|
34264
|
+
worktreesRootAbs,
|
|
34265
|
+
e2eCertificate
|
|
34266
|
+
});
|
|
34267
|
+
return;
|
|
34268
|
+
}
|
|
34269
|
+
await runConnectedBridge(options, runBridge);
|
|
34000
34270
|
}
|
|
34001
34271
|
export {
|
|
34002
34272
|
callSkill,
|