@buildautomaton/cli 0.1.61 → 0.1.63
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/cli.js +497 -656
- package/dist/cli.js.map +4 -4
- package/dist/index.js +467 -626
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25631,7 +25631,7 @@ var {
|
|
|
25631
25631
|
} = import_index.default;
|
|
25632
25632
|
|
|
25633
25633
|
// src/cli-version.ts
|
|
25634
|
-
var CLI_VERSION = "0.1.
|
|
25634
|
+
var CLI_VERSION = "0.1.63".length > 0 ? "0.1.63" : "0.0.0-dev";
|
|
25635
25635
|
|
|
25636
25636
|
// src/cli/defaults.ts
|
|
25637
25637
|
var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
|
|
@@ -26151,18 +26151,6 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
26151
26151
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
26152
26152
|
var wrapper_default = import_websocket.default;
|
|
26153
26153
|
|
|
26154
|
-
// src/net/apply-cli-outbound-network-prefs.ts
|
|
26155
|
-
import dns from "node:dns";
|
|
26156
|
-
var applied = false;
|
|
26157
|
-
function applyCliOutboundNetworkPreferences() {
|
|
26158
|
-
if (applied) return;
|
|
26159
|
-
applied = true;
|
|
26160
|
-
try {
|
|
26161
|
-
dns.setDefaultResultOrder("ipv4first");
|
|
26162
|
-
} catch {
|
|
26163
|
-
}
|
|
26164
|
-
}
|
|
26165
|
-
|
|
26166
26154
|
// src/connection/cli-ws-client.ts
|
|
26167
26155
|
import https from "node:https";
|
|
26168
26156
|
var CLI_WEBSOCKET_CLIENT_PING_MS = 25e3;
|
|
@@ -26264,7 +26252,6 @@ function createWsBridge(options) {
|
|
|
26264
26252
|
onCompactHeartbeatAck,
|
|
26265
26253
|
isActiveSocket
|
|
26266
26254
|
} = options;
|
|
26267
|
-
applyCliOutboundNetworkPreferences();
|
|
26268
26255
|
const ws = new wrapper_default(url2, buildCliWebSocketClientOptions(url2));
|
|
26269
26256
|
let clearClientPing = null;
|
|
26270
26257
|
function disposeClientPing() {
|
|
@@ -27706,38 +27693,38 @@ function readCodeNavCacheMigrationSql(filename) {
|
|
|
27706
27693
|
}
|
|
27707
27694
|
|
|
27708
27695
|
// src/sqlite/run-sqlite-migrations.ts
|
|
27709
|
-
function checkpointSatisfiedByLegacyChain(
|
|
27710
|
-
return Array.isArray(replacesLegacyMigrations) && replacesLegacyMigrations.length > 0 && replacesLegacyMigrations.every((n) =>
|
|
27696
|
+
function checkpointSatisfiedByLegacyChain(applied, replacesLegacyMigrations) {
|
|
27697
|
+
return Array.isArray(replacesLegacyMigrations) && replacesLegacyMigrations.length > 0 && replacesLegacyMigrations.every((n) => applied.has(n));
|
|
27711
27698
|
}
|
|
27712
|
-
function recordMigrationAndPruneCheckpointLegacy(db, migration,
|
|
27699
|
+
function recordMigrationAndPruneCheckpointLegacy(db, migration, applied) {
|
|
27713
27700
|
db.run("INSERT INTO __migrations (name) VALUES (?)", [migration.name]);
|
|
27714
|
-
|
|
27701
|
+
applied.add(migration.name);
|
|
27715
27702
|
if (migration.checkpoint !== true) return;
|
|
27716
27703
|
const legacy = migration.replacesLegacyMigrations;
|
|
27717
27704
|
if (!legacy?.length) return;
|
|
27718
27705
|
for (const legacyName of legacy) {
|
|
27719
27706
|
if (legacyName === migration.name) continue;
|
|
27720
27707
|
db.run("DELETE FROM __migrations WHERE name = ?", [legacyName]);
|
|
27721
|
-
|
|
27708
|
+
applied.delete(legacyName);
|
|
27722
27709
|
}
|
|
27723
27710
|
}
|
|
27724
27711
|
function runSqliteMigrations(db, migrations, bootstrapSql, label) {
|
|
27725
27712
|
db.exec(bootstrapSql);
|
|
27726
27713
|
const appliedRows = db.all("SELECT name FROM __migrations");
|
|
27727
|
-
const
|
|
27714
|
+
const applied = new Set(appliedRows.map((r) => r.name));
|
|
27728
27715
|
for (const migration of migrations) {
|
|
27729
|
-
if (
|
|
27730
|
-
if (migration.checkpoint === true && checkpointSatisfiedByLegacyChain(
|
|
27731
|
-
recordMigrationAndPruneCheckpointLegacy(db, migration,
|
|
27716
|
+
if (applied.has(migration.name)) continue;
|
|
27717
|
+
if (migration.checkpoint === true && checkpointSatisfiedByLegacyChain(applied, migration.replacesLegacyMigrations)) {
|
|
27718
|
+
recordMigrationAndPruneCheckpointLegacy(db, migration, applied);
|
|
27732
27719
|
continue;
|
|
27733
27720
|
}
|
|
27734
27721
|
if (migration.alreadyApplied?.(db)) {
|
|
27735
|
-
recordMigrationAndPruneCheckpointLegacy(db, migration,
|
|
27722
|
+
recordMigrationAndPruneCheckpointLegacy(db, migration, applied);
|
|
27736
27723
|
continue;
|
|
27737
27724
|
}
|
|
27738
27725
|
try {
|
|
27739
27726
|
migration.migrate(db);
|
|
27740
|
-
recordMigrationAndPruneCheckpointLegacy(db, migration,
|
|
27727
|
+
recordMigrationAndPruneCheckpointLegacy(db, migration, applied);
|
|
27741
27728
|
} catch (e) {
|
|
27742
27729
|
console.error(`[${label}] Migration failed: ${migration.name}`, e);
|
|
27743
27730
|
throw e;
|
|
@@ -31968,7 +31955,6 @@ var StoryCheckpointSummarySchema = CheckpointSummarySchema.extend({
|
|
|
31968
31955
|
|
|
31969
31956
|
// ../types/src/sessions.ts
|
|
31970
31957
|
init_zod();
|
|
31971
|
-
var BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID = "__builtin_change_summary__";
|
|
31972
31958
|
var SessionMetaSchema = external_exports.object({
|
|
31973
31959
|
sessionId: external_exports.string(),
|
|
31974
31960
|
workspaceId: external_exports.string(),
|
|
@@ -32101,146 +32087,6 @@ function buildPlanningSessionFollowUpAgentPrompt(userPrompt, existingTodos) {
|
|
|
32101
32087
|
].join("\n");
|
|
32102
32088
|
}
|
|
32103
32089
|
|
|
32104
|
-
// ../types/src/change-summary-path.ts
|
|
32105
|
-
function normalizeRepoRelativePath(p) {
|
|
32106
|
-
let t = p.trim().replace(/\\/g, "/");
|
|
32107
|
-
while (t.startsWith("./")) t = t.slice(2);
|
|
32108
|
-
return t.replace(/\/+/g, "/");
|
|
32109
|
-
}
|
|
32110
|
-
function resolveChangeSummaryPathAgainstAllowed(rawPath, allowed) {
|
|
32111
|
-
const trimmed2 = rawPath.trim();
|
|
32112
|
-
if (!trimmed2) return null;
|
|
32113
|
-
if (allowed.has(trimmed2)) return trimmed2;
|
|
32114
|
-
const n = normalizeRepoRelativePath(trimmed2);
|
|
32115
|
-
if (allowed.has(n)) return n;
|
|
32116
|
-
for (const a of allowed) {
|
|
32117
|
-
if (normalizeRepoRelativePath(a) === n) return a;
|
|
32118
|
-
}
|
|
32119
|
-
return null;
|
|
32120
|
-
}
|
|
32121
|
-
|
|
32122
|
-
// ../types/src/parse-change-summary-json.ts
|
|
32123
|
-
function clampSummaryToAtMostTwoLines(summary) {
|
|
32124
|
-
const lines = summary.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
32125
|
-
return lines.slice(0, 2).join("\n");
|
|
32126
|
-
}
|
|
32127
|
-
function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
32128
|
-
if (raw == null || raw.trim() === "") return [];
|
|
32129
|
-
let text = raw.trim();
|
|
32130
|
-
const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
32131
|
-
if (fence?.[1]) text = fence[1].trim();
|
|
32132
|
-
let parsed;
|
|
32133
|
-
try {
|
|
32134
|
-
parsed = JSON.parse(text);
|
|
32135
|
-
} catch {
|
|
32136
|
-
const start = text.indexOf("[");
|
|
32137
|
-
const end = text.lastIndexOf("]");
|
|
32138
|
-
if (start < 0 || end <= start) return [];
|
|
32139
|
-
try {
|
|
32140
|
-
parsed = JSON.parse(text.slice(start, end + 1));
|
|
32141
|
-
} catch {
|
|
32142
|
-
return [];
|
|
32143
|
-
}
|
|
32144
|
-
}
|
|
32145
|
-
const rows = [];
|
|
32146
|
-
let arr = [];
|
|
32147
|
-
if (Array.isArray(parsed)) {
|
|
32148
|
-
arr = parsed;
|
|
32149
|
-
} else if (parsed && typeof parsed === "object" && Array.isArray(parsed.files)) {
|
|
32150
|
-
arr = parsed.files;
|
|
32151
|
-
}
|
|
32152
|
-
const skip = options?.skipPathAllowlist === true;
|
|
32153
|
-
for (const item of arr) {
|
|
32154
|
-
if (!item || typeof item !== "object") continue;
|
|
32155
|
-
const o = item;
|
|
32156
|
-
const rawPath = typeof o.path === "string" ? o.path.trim() : "";
|
|
32157
|
-
const summary = typeof o.summary === "string" ? o.summary.trim() : "";
|
|
32158
|
-
if (!rawPath || !summary) continue;
|
|
32159
|
-
const path81 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
|
|
32160
|
-
if (!path81) continue;
|
|
32161
|
-
rows.push({ path: path81, summary: clampSummaryToAtMostTwoLines(summary) });
|
|
32162
|
-
}
|
|
32163
|
-
return rows;
|
|
32164
|
-
}
|
|
32165
|
-
|
|
32166
|
-
// ../types/src/build-change-summary-prompt.ts
|
|
32167
|
-
var PATCH_PREVIEW_MAX = 12e3;
|
|
32168
|
-
function clip(s, max) {
|
|
32169
|
-
if (s.length <= max) return s;
|
|
32170
|
-
return `${s.slice(0, max)}
|
|
32171
|
-
|
|
32172
|
-
\u2026(truncated, ${s.length - max} more characters)`;
|
|
32173
|
-
}
|
|
32174
|
-
function buildSessionChangeSummaryPrompt(files) {
|
|
32175
|
-
const lines = [
|
|
32176
|
-
"You are the same agent that produced the changes below. Summarize **your own** edits so a reader can scan them quickly.",
|
|
32177
|
-
"",
|
|
32178
|
-
"Write in second person (you / your): what you changed in each path and why it matters.",
|
|
32179
|
-
"",
|
|
32180
|
-
"Each summary must be **very concise**: **one line** of plain text, or **at most two short lines** (use a single line break between the two if needed). No bullets, no paragraphs.",
|
|
32181
|
-
"",
|
|
32182
|
-
"## How to format your reply (machine parsing)",
|
|
32183
|
-
"",
|
|
32184
|
-
"- Put the machine-readable part **only** inside a **markdown fenced code block** whose opening fence is exactly ```json on its own line. Close the block with ``` on its own line after the JSON.",
|
|
32185
|
-
"- Inside that fence: **nothing except** one valid JSON value \u2014 the array described below. No trailing commentary inside the fence.",
|
|
32186
|
-
"- **Do not** attach prose to the JSON on the same line (wrong: `Only one file\u2026page.tsx.[{\u2026}]`). Wrong: any sentence that ends with `.` immediately before `[`. Put a blank line before the ```json line.",
|
|
32187
|
-
"- If you add optional plain English before the fence (e.g. one short sentence), keep it **separate**: end that sentence, blank line, then ```json.",
|
|
32188
|
-
'- In each `"summary"` string, avoid raw double-quote characters, or escape them as `\\"` so the JSON parses.',
|
|
32189
|
-
"",
|
|
32190
|
-
"JSON shape **inside the fence** (array only):",
|
|
32191
|
-
'[{"path":"<file path exactly as given>","summary":"<one line, or two short lines separated by \\n>"}]',
|
|
32192
|
-
"",
|
|
32193
|
-
"Rules:",
|
|
32194
|
-
"- Include **exactly one** object per file path listed below (same path strings).",
|
|
32195
|
-
"- If a path is a removed directory, state briefly what you removed.",
|
|
32196
|
-
"- Do not invent paths; use only the paths provided.",
|
|
32197
|
-
"",
|
|
32198
|
-
"## Files you changed",
|
|
32199
|
-
""
|
|
32200
|
-
];
|
|
32201
|
-
for (const f of files) {
|
|
32202
|
-
lines.push(`### ${f.path}`);
|
|
32203
|
-
if (f.directoryRemoved) {
|
|
32204
|
-
lines.push("(directory removed)");
|
|
32205
|
-
lines.push("");
|
|
32206
|
-
continue;
|
|
32207
|
-
}
|
|
32208
|
-
if (f.patchContent && f.patchContent.trim() !== "") {
|
|
32209
|
-
lines.push("```diff");
|
|
32210
|
-
lines.push(clip(f.patchContent.trim(), PATCH_PREVIEW_MAX));
|
|
32211
|
-
lines.push("```");
|
|
32212
|
-
} else if (f.oldText != null || f.newText != null) {
|
|
32213
|
-
const oldT = (f.oldText ?? "").trim();
|
|
32214
|
-
const newT = (f.newText ?? "").trim();
|
|
32215
|
-
lines.push("Previous snippet:", clip(oldT, 6e3));
|
|
32216
|
-
lines.push("New snippet:", clip(newT, 6e3));
|
|
32217
|
-
} else {
|
|
32218
|
-
lines.push("(no diff body stored for this path)");
|
|
32219
|
-
}
|
|
32220
|
-
lines.push("");
|
|
32221
|
-
}
|
|
32222
|
-
return lines.join("\n");
|
|
32223
|
-
}
|
|
32224
|
-
|
|
32225
|
-
// ../types/src/dedupe-session-file-changes-by-path.ts
|
|
32226
|
-
function defaultRichness(c) {
|
|
32227
|
-
const patch = typeof c.patchContent === "string" ? c.patchContent.length : 0;
|
|
32228
|
-
const nt = typeof c.newText === "string" ? c.newText.length : 0;
|
|
32229
|
-
const ot = typeof c.oldText === "string" ? c.oldText.length : 0;
|
|
32230
|
-
const dir = c.directoryRemoved === true ? 8 : 0;
|
|
32231
|
-
return (patch > 0 ? 4 : 0) + (nt > 0 ? 2 : 0) + (ot > 0 ? 1 : 0) + dir;
|
|
32232
|
-
}
|
|
32233
|
-
function dedupeSessionFileChangesByPath(items, richness = (item) => defaultRichness(item)) {
|
|
32234
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
32235
|
-
for (const item of items) {
|
|
32236
|
-
const p = typeof item.path === "string" ? item.path.trim() : "";
|
|
32237
|
-
if (!p) continue;
|
|
32238
|
-
const prev = byPath.get(p);
|
|
32239
|
-
if (!prev || richness(item) >= richness(prev)) byPath.set(p, item);
|
|
32240
|
-
}
|
|
32241
|
-
return Array.from(byPath.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);
|
|
32242
|
-
}
|
|
32243
|
-
|
|
32244
32090
|
// ../types/src/diff/line-diff.ts
|
|
32245
32091
|
function normalizeDiffLineText(line) {
|
|
32246
32092
|
return line.replace(/\r$/, "").replace(/\s*\\s*$/, "");
|
|
@@ -35849,102 +35695,6 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
35849
35695
|
});
|
|
35850
35696
|
}
|
|
35851
35697
|
|
|
35852
|
-
// src/agents/acp/put-summarize-change-summaries.ts
|
|
35853
|
-
async function putEncryptedChangeSummaryRows(params) {
|
|
35854
|
-
const base = params.apiBaseUrl.replace(/\/+$/, "");
|
|
35855
|
-
const entries = params.rows.map(({ path: path81, summary }) => {
|
|
35856
|
-
const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
|
|
35857
|
-
return { path: path81, summary: JSON.stringify(enc) };
|
|
35858
|
-
});
|
|
35859
|
-
const res = await fetch(
|
|
35860
|
-
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
35861
|
-
{
|
|
35862
|
-
method: "PUT",
|
|
35863
|
-
headers: {
|
|
35864
|
-
Authorization: `Bearer ${params.authToken}`,
|
|
35865
|
-
"Content-Type": "application/json"
|
|
35866
|
-
},
|
|
35867
|
-
body: JSON.stringify({ entries })
|
|
35868
|
-
}
|
|
35869
|
-
);
|
|
35870
|
-
if (!res.ok) {
|
|
35871
|
-
const t = await res.text();
|
|
35872
|
-
throw new Error(`PUT summarize-changes summaries failed ${res.status}: ${t.slice(0, 500)}`);
|
|
35873
|
-
}
|
|
35874
|
-
}
|
|
35875
|
-
|
|
35876
|
-
// src/agents/acp/maybe-upload-e2ee-session-change-summaries.ts
|
|
35877
|
-
async function maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess(params) {
|
|
35878
|
-
const {
|
|
35879
|
-
sessionId,
|
|
35880
|
-
runId,
|
|
35881
|
-
resultSuccess,
|
|
35882
|
-
output,
|
|
35883
|
-
e2ee,
|
|
35884
|
-
cloudApiBaseUrl,
|
|
35885
|
-
getCloudAccessToken,
|
|
35886
|
-
followUpCatalogPromptId,
|
|
35887
|
-
sessionChangeSummaryFilePaths,
|
|
35888
|
-
log: log2
|
|
35889
|
-
} = params;
|
|
35890
|
-
const outputStr = typeof output === "string" ? output : "";
|
|
35891
|
-
if (!sessionId) {
|
|
35892
|
-
return;
|
|
35893
|
-
}
|
|
35894
|
-
if (!runId) {
|
|
35895
|
-
return;
|
|
35896
|
-
}
|
|
35897
|
-
if (!resultSuccess) {
|
|
35898
|
-
return;
|
|
35899
|
-
}
|
|
35900
|
-
if (!e2ee) {
|
|
35901
|
-
return;
|
|
35902
|
-
}
|
|
35903
|
-
if (!cloudApiBaseUrl) {
|
|
35904
|
-
return;
|
|
35905
|
-
}
|
|
35906
|
-
if (!getCloudAccessToken) {
|
|
35907
|
-
return;
|
|
35908
|
-
}
|
|
35909
|
-
if (followUpCatalogPromptId !== BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID) {
|
|
35910
|
-
return;
|
|
35911
|
-
}
|
|
35912
|
-
if (!sessionChangeSummaryFilePaths || sessionChangeSummaryFilePaths.length === 0) {
|
|
35913
|
-
return;
|
|
35914
|
-
}
|
|
35915
|
-
if (outputStr.trim() === "") {
|
|
35916
|
-
return;
|
|
35917
|
-
}
|
|
35918
|
-
const allowed = /* @__PURE__ */ new Set();
|
|
35919
|
-
for (const p of sessionChangeSummaryFilePaths) {
|
|
35920
|
-
const t = p.trim();
|
|
35921
|
-
if (!t) continue;
|
|
35922
|
-
allowed.add(t);
|
|
35923
|
-
allowed.add(normalizeRepoRelativePath(t));
|
|
35924
|
-
}
|
|
35925
|
-
const rows = parseChangeSummaryJson(outputStr, allowed);
|
|
35926
|
-
if (rows.length === 0) {
|
|
35927
|
-
return;
|
|
35928
|
-
}
|
|
35929
|
-
const token = getCloudAccessToken();
|
|
35930
|
-
if (!token) {
|
|
35931
|
-
return;
|
|
35932
|
-
}
|
|
35933
|
-
try {
|
|
35934
|
-
await putEncryptedChangeSummaryRows({
|
|
35935
|
-
apiBaseUrl: cloudApiBaseUrl,
|
|
35936
|
-
authToken: token,
|
|
35937
|
-
sessionId,
|
|
35938
|
-
e2ee,
|
|
35939
|
-
rows
|
|
35940
|
-
});
|
|
35941
|
-
} catch (uploadErr) {
|
|
35942
|
-
log2(
|
|
35943
|
-
`[Agent] Encrypted change summary upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
|
|
35944
|
-
);
|
|
35945
|
-
}
|
|
35946
|
-
}
|
|
35947
|
-
|
|
35948
35698
|
// src/agents/planning/submit-planning-todos-for-turn.ts
|
|
35949
35699
|
async function submitPlanningTodosForTurn(params) {
|
|
35950
35700
|
const token = params.getCloudAccessToken();
|
|
@@ -36013,10 +35763,8 @@ async function finalizeAndSendPromptResult(params) {
|
|
|
36013
35763
|
agentCwd,
|
|
36014
35764
|
isPlanningSession,
|
|
36015
35765
|
followUpCatalogPromptId,
|
|
36016
|
-
sessionChangeSummaryFilePaths,
|
|
36017
35766
|
cloudApiBaseUrl,
|
|
36018
35767
|
getCloudAccessToken,
|
|
36019
|
-
e2ee,
|
|
36020
35768
|
sendResult,
|
|
36021
35769
|
sendSessionUpdate,
|
|
36022
35770
|
log: log2
|
|
@@ -36030,18 +35778,6 @@ async function finalizeAndSendPromptResult(params) {
|
|
|
36030
35778
|
log: log2
|
|
36031
35779
|
});
|
|
36032
35780
|
}
|
|
36033
|
-
await maybeUploadE2eeSessionChangeSummariesAfterAgentSuccess({
|
|
36034
|
-
sessionId,
|
|
36035
|
-
runId,
|
|
36036
|
-
resultSuccess: result.success === true,
|
|
36037
|
-
output: result.output,
|
|
36038
|
-
e2ee,
|
|
36039
|
-
cloudApiBaseUrl,
|
|
36040
|
-
getCloudAccessToken,
|
|
36041
|
-
followUpCatalogPromptId,
|
|
36042
|
-
sessionChangeSummaryFilePaths,
|
|
36043
|
-
log: log2
|
|
36044
|
-
});
|
|
36045
35781
|
const planningTodosSubmit = await maybeSubmitPlanningTodosAfterAgentSuccess({
|
|
36046
35782
|
sessionId,
|
|
36047
35783
|
runId,
|
|
@@ -36485,7 +36221,6 @@ async function sendPromptToAgent(options) {
|
|
|
36485
36221
|
sendSessionUpdate,
|
|
36486
36222
|
log: log2,
|
|
36487
36223
|
followUpCatalogPromptId,
|
|
36488
|
-
sessionChangeSummaryFilePaths,
|
|
36489
36224
|
cloudApiBaseUrl,
|
|
36490
36225
|
getCloudAccessToken,
|
|
36491
36226
|
e2ee,
|
|
@@ -36529,7 +36264,6 @@ async function sendPromptToAgent(options) {
|
|
|
36529
36264
|
agentCwd,
|
|
36530
36265
|
isPlanningSession,
|
|
36531
36266
|
followUpCatalogPromptId,
|
|
36532
|
-
sessionChangeSummaryFilePaths,
|
|
36533
36267
|
cloudApiBaseUrl,
|
|
36534
36268
|
getCloudAccessToken,
|
|
36535
36269
|
e2ee,
|
|
@@ -36600,7 +36334,6 @@ async function runAcpPrompt(ctx, runCtx, opts) {
|
|
|
36600
36334
|
sendResult,
|
|
36601
36335
|
sendSessionUpdate,
|
|
36602
36336
|
followUpCatalogPromptId,
|
|
36603
|
-
sessionChangeSummaryFilePaths,
|
|
36604
36337
|
cloudApiBaseUrl,
|
|
36605
36338
|
getCloudAccessToken,
|
|
36606
36339
|
e2ee,
|
|
@@ -36666,7 +36399,6 @@ async function runAcpPrompt(ctx, runCtx, opts) {
|
|
|
36666
36399
|
sendSessionUpdate,
|
|
36667
36400
|
log: ctx.log,
|
|
36668
36401
|
followUpCatalogPromptId,
|
|
36669
|
-
sessionChangeSummaryFilePaths,
|
|
36670
36402
|
cloudApiBaseUrl,
|
|
36671
36403
|
getCloudAccessToken,
|
|
36672
36404
|
e2ee,
|
|
@@ -37713,6 +37445,9 @@ function envForSpawn(base, userEnv, ports) {
|
|
|
37713
37445
|
});
|
|
37714
37446
|
if (ports.length > 0) {
|
|
37715
37447
|
out.PORTS = ports.join(",");
|
|
37448
|
+
if (!userKeys.has("PORT")) {
|
|
37449
|
+
out.PORT = String(ports[0]);
|
|
37450
|
+
}
|
|
37716
37451
|
}
|
|
37717
37452
|
if (!userKeys.has("NO_COLOR")) {
|
|
37718
37453
|
delete out.NO_COLOR;
|
|
@@ -38615,172 +38350,189 @@ function createBridgePreviewStack(options) {
|
|
|
38615
38350
|
return { previewEnvironmentManager, scheduleInitialIndexBuildsOnce, identifyReportedPaths };
|
|
38616
38351
|
}
|
|
38617
38352
|
|
|
38618
|
-
// src/
|
|
38619
|
-
|
|
38620
|
-
|
|
38621
|
-
return
|
|
38622
|
-
}
|
|
38623
|
-
function collectErrorText(err) {
|
|
38624
|
-
const parts = [];
|
|
38625
|
-
let e = err;
|
|
38626
|
-
for (let depth = 0; depth < 6 && e != null; depth += 1) {
|
|
38627
|
-
if (e instanceof Error) {
|
|
38628
|
-
parts.push(e.message);
|
|
38629
|
-
e = e.cause;
|
|
38630
|
-
} else {
|
|
38631
|
-
parts.push(String(e));
|
|
38632
|
-
break;
|
|
38633
|
-
}
|
|
38634
|
-
}
|
|
38635
|
-
return parts.join(" ").toLowerCase();
|
|
38636
|
-
}
|
|
38637
|
-
function isTransientLocalServiceError(err) {
|
|
38638
|
-
const text = collectErrorText(err);
|
|
38639
|
-
if (!text) return false;
|
|
38640
|
-
return text.includes("fetch failed") || text.includes("econnrefused") || text.includes("econnreset") || text.includes("epipe") || text.includes("etimedout") || text.includes("socket hang up") || text.includes("network connection lost") || text.includes("ecanceled") || text.includes("aborted") || text.includes("und_err_socket") || text.includes("other side closed") || text.includes("eai_again");
|
|
38353
|
+
// src/firehose/build-cli-ws-url.ts
|
|
38354
|
+
function buildFirehoseCliWsUrl(baseUrl) {
|
|
38355
|
+
const base = baseUrl.startsWith("https") ? baseUrl.replace(/^https/, "wss") : baseUrl.replace(/^http/, "ws");
|
|
38356
|
+
return `${base.replace(/\/$/, "")}/ws`;
|
|
38641
38357
|
}
|
|
38642
|
-
|
|
38643
|
-
|
|
38644
|
-
|
|
38358
|
+
|
|
38359
|
+
// src/firehose/proxy/prepare-local-proxy-request-headers.ts
|
|
38360
|
+
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
38361
|
+
"connection",
|
|
38362
|
+
"keep-alive",
|
|
38363
|
+
"proxy-authenticate",
|
|
38364
|
+
"proxy-authorization",
|
|
38365
|
+
"te",
|
|
38366
|
+
"trailers",
|
|
38367
|
+
"transfer-encoding",
|
|
38368
|
+
"upgrade"
|
|
38369
|
+
]);
|
|
38370
|
+
var BODY_HEADERS = /* @__PURE__ */ new Set(["content-length", "content-type", "content-encoding"]);
|
|
38371
|
+
function methodAllowsRequestBody(method) {
|
|
38372
|
+
const normalized = method.toUpperCase();
|
|
38373
|
+
return normalized !== "GET" && normalized !== "HEAD";
|
|
38645
38374
|
}
|
|
38646
|
-
|
|
38647
|
-
const
|
|
38648
|
-
const
|
|
38649
|
-
|
|
38650
|
-
|
|
38651
|
-
|
|
38652
|
-
|
|
38653
|
-
|
|
38654
|
-
|
|
38655
|
-
} catch (e) {
|
|
38656
|
-
lastErr = e;
|
|
38657
|
-
const canRetry = allowRetry && attempt < maxAttempts - 1 && isTransientLocalServiceError(e);
|
|
38658
|
-
if (!canRetry) throw e;
|
|
38659
|
-
const waitMs = delays[Math.min(attempt, delays.length - 1)] ?? 100;
|
|
38660
|
-
await sleepMs(waitMs);
|
|
38661
|
-
}
|
|
38375
|
+
function prepareLocalProxyRequestHeaders(method, headers, sendingBody) {
|
|
38376
|
+
const out = {};
|
|
38377
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
38378
|
+
const lower = key.toLowerCase();
|
|
38379
|
+
if (lower === "host") continue;
|
|
38380
|
+
if (HOP_BY_HOP_HEADERS.has(lower)) continue;
|
|
38381
|
+
if (lower === "origin" || lower === "referer") continue;
|
|
38382
|
+
if (!sendingBody && BODY_HEADERS.has(lower)) continue;
|
|
38383
|
+
out[key] = value;
|
|
38662
38384
|
}
|
|
38663
|
-
|
|
38385
|
+
return out;
|
|
38664
38386
|
}
|
|
38665
38387
|
|
|
38666
|
-
// src/firehose/proxy/local-proxy.ts
|
|
38667
|
-
var ALLOWED_HOSTS = ["localhost", "127.0.0.1", "::1"];
|
|
38668
|
-
function
|
|
38669
|
-
|
|
38670
|
-
return
|
|
38671
|
-
}
|
|
38672
|
-
function isIdempotentProxyMethod(method) {
|
|
38673
|
-
const m = method.toUpperCase();
|
|
38674
|
-
return m === "GET" || m === "HEAD" || m === "OPTIONS";
|
|
38388
|
+
// src/firehose/proxy/build-local-proxy-http-request.ts
|
|
38389
|
+
var ALLOWED_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
38390
|
+
function willSendLocalProxyBody(request) {
|
|
38391
|
+
if (!methodAllowsRequestBody(request.method)) return false;
|
|
38392
|
+
return request.body !== void 0 && request.body !== null;
|
|
38675
38393
|
}
|
|
38676
|
-
function
|
|
38394
|
+
function buildLocalProxyHttpRequest(request) {
|
|
38677
38395
|
let url2;
|
|
38678
38396
|
try {
|
|
38679
38397
|
url2 = new URL(request.url);
|
|
38680
38398
|
} catch {
|
|
38681
38399
|
return { error: `Invalid URL: ${request.url}` };
|
|
38682
38400
|
}
|
|
38683
|
-
|
|
38401
|
+
const hostname2 = url2.hostname.replace(/^\[|\]$/g, "");
|
|
38402
|
+
if (!ALLOWED_HOSTS.has(url2.hostname) && !ALLOWED_HOSTS.has(hostname2)) {
|
|
38684
38403
|
return { error: "Only localhost requests are allowed" };
|
|
38685
38404
|
}
|
|
38686
|
-
|
|
38687
|
-
|
|
38688
|
-
|
|
38689
|
-
const checked = checkUrlAndHost(request);
|
|
38690
|
-
if ("error" in checked) {
|
|
38691
|
-
return { id: request.id, statusCode: 0, headers: {}, body: "", error: checked.error };
|
|
38692
|
-
}
|
|
38693
|
-
const { url: url2 } = checked;
|
|
38405
|
+
const sendingBody = willSendLocalProxyBody(request);
|
|
38406
|
+
const headers = prepareLocalProxyRequestHeaders(request.method, request.headers, sendingBody);
|
|
38407
|
+
headers.host = url2.host;
|
|
38694
38408
|
const isHttps = url2.protocol === "https:";
|
|
38695
|
-
const
|
|
38696
|
-
|
|
38409
|
+
const port = url2.port ? Number(url2.port) : isHttps ? 443 : 80;
|
|
38410
|
+
return {
|
|
38697
38411
|
method: request.method,
|
|
38698
38412
|
hostname: url2.hostname,
|
|
38699
|
-
port
|
|
38700
|
-
path: url2.pathname
|
|
38701
|
-
headers
|
|
38413
|
+
port,
|
|
38414
|
+
path: `${url2.pathname}${url2.search}`,
|
|
38415
|
+
headers,
|
|
38416
|
+
sendingBody,
|
|
38417
|
+
body: sendingBody ? request.body : void 0
|
|
38702
38418
|
};
|
|
38703
|
-
|
|
38704
|
-
|
|
38705
|
-
|
|
38706
|
-
|
|
38707
|
-
|
|
38708
|
-
|
|
38709
|
-
|
|
38710
|
-
|
|
38711
|
-
|
|
38712
|
-
|
|
38713
|
-
|
|
38714
|
-
|
|
38715
|
-
|
|
38716
|
-
|
|
38717
|
-
|
|
38718
|
-
|
|
38719
|
-
|
|
38720
|
-
|
|
38721
|
-
|
|
38419
|
+
}
|
|
38420
|
+
|
|
38421
|
+
// src/firehose/proxy/constants.ts
|
|
38422
|
+
var LOCAL_PROXY_REQUEST_TIMEOUT_MS = 3e4;
|
|
38423
|
+
|
|
38424
|
+
// src/firehose/proxy/incoming-message-headers.ts
|
|
38425
|
+
function incomingMessageHeaders(res) {
|
|
38426
|
+
const headers = {};
|
|
38427
|
+
for (const [k, v] of Object.entries(res.headers)) {
|
|
38428
|
+
if (typeof v === "string") headers[k] = v;
|
|
38429
|
+
else if (Array.isArray(v) && v[0]) headers[k] = v[0];
|
|
38430
|
+
}
|
|
38431
|
+
return headers;
|
|
38432
|
+
}
|
|
38433
|
+
|
|
38434
|
+
// src/firehose/proxy/run-local-http-request.ts
|
|
38435
|
+
async function loadHttpModule(isHttps) {
|
|
38436
|
+
return isHttps ? import("https") : import("http");
|
|
38437
|
+
}
|
|
38438
|
+
function buildLocalHttpRequestOptions(built) {
|
|
38439
|
+
return {
|
|
38440
|
+
method: built.method,
|
|
38441
|
+
hostname: built.hostname,
|
|
38442
|
+
port: built.port,
|
|
38443
|
+
path: built.path,
|
|
38444
|
+
headers: built.headers
|
|
38445
|
+
};
|
|
38446
|
+
}
|
|
38447
|
+
function runLocalHttpRequestOnce(mod, built) {
|
|
38448
|
+
const opts = buildLocalHttpRequestOptions(built);
|
|
38449
|
+
return new Promise((resolve37) => {
|
|
38450
|
+
const req = mod.request(opts, (res) => {
|
|
38451
|
+
const chunks = [];
|
|
38452
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
38453
|
+
res.on("end", () => {
|
|
38454
|
+
resolve37({
|
|
38455
|
+
statusCode: res.statusCode ?? 0,
|
|
38456
|
+
headers: incomingMessageHeaders(res),
|
|
38457
|
+
body: Buffer.concat(chunks)
|
|
38722
38458
|
});
|
|
38723
38459
|
});
|
|
38724
|
-
|
|
38460
|
+
res.on("error", (err) => {
|
|
38725
38461
|
resolve37({
|
|
38726
|
-
id: request.id,
|
|
38727
38462
|
statusCode: 0,
|
|
38728
38463
|
headers: {},
|
|
38729
|
-
body:
|
|
38464
|
+
body: Buffer.alloc(0),
|
|
38730
38465
|
error: err.message
|
|
38731
38466
|
});
|
|
38732
38467
|
});
|
|
38733
|
-
const method = request.method.toUpperCase();
|
|
38734
|
-
if (request.body && method !== "GET" && method !== "HEAD") req.write(request.body);
|
|
38735
|
-
req.end();
|
|
38736
38468
|
});
|
|
38737
|
-
|
|
38738
|
-
|
|
38739
|
-
|
|
38740
|
-
|
|
38741
|
-
|
|
38742
|
-
|
|
38743
|
-
|
|
38469
|
+
req.setTimeout(LOCAL_PROXY_REQUEST_TIMEOUT_MS, () => {
|
|
38470
|
+
req.destroy(new Error(`Local preview request timed out after ${LOCAL_PROXY_REQUEST_TIMEOUT_MS}ms`));
|
|
38471
|
+
});
|
|
38472
|
+
req.on("error", (err) => {
|
|
38473
|
+
resolve37({
|
|
38474
|
+
statusCode: 0,
|
|
38475
|
+
headers: {},
|
|
38476
|
+
body: Buffer.alloc(0),
|
|
38477
|
+
error: err.message
|
|
38478
|
+
});
|
|
38479
|
+
});
|
|
38480
|
+
if (built.sendingBody && built.body != null) {
|
|
38481
|
+
req.write(built.body);
|
|
38482
|
+
}
|
|
38483
|
+
req.end();
|
|
38484
|
+
});
|
|
38744
38485
|
}
|
|
38486
|
+
|
|
38487
|
+
// src/firehose/proxy/proxy-to-local-streaming.ts
|
|
38745
38488
|
async function proxyToLocalStreaming(request, callbacks) {
|
|
38746
|
-
const
|
|
38747
|
-
if ("error" in
|
|
38748
|
-
callbacks.onError(
|
|
38489
|
+
const built = buildLocalProxyHttpRequest(request);
|
|
38490
|
+
if ("error" in built) {
|
|
38491
|
+
callbacks.onError(built.error);
|
|
38749
38492
|
return;
|
|
38750
38493
|
}
|
|
38751
|
-
|
|
38752
|
-
|
|
38753
|
-
|
|
38754
|
-
|
|
38755
|
-
|
|
38756
|
-
|
|
38757
|
-
|
|
38758
|
-
|
|
38759
|
-
|
|
38760
|
-
|
|
38761
|
-
|
|
38762
|
-
|
|
38763
|
-
headers[key] = value;
|
|
38764
|
-
});
|
|
38765
|
-
callbacks.onStart(res.status, headers);
|
|
38766
|
-
const reader = res.body?.getReader();
|
|
38767
|
-
if (!reader) {
|
|
38768
|
-
callbacks.onEnd();
|
|
38769
|
-
return;
|
|
38770
|
-
}
|
|
38771
|
-
try {
|
|
38772
|
-
for (; ; ) {
|
|
38773
|
-
const { done, value } = await reader.read();
|
|
38774
|
-
if (done) break;
|
|
38775
|
-
callbacks.onChunk(value);
|
|
38494
|
+
const isHttps = request.url.startsWith("https:");
|
|
38495
|
+
const mod = await loadHttpModule(isHttps);
|
|
38496
|
+
const opts = buildLocalHttpRequestOptions(built);
|
|
38497
|
+
await new Promise((resolve37) => {
|
|
38498
|
+
const req = mod.request(opts, (res) => {
|
|
38499
|
+
try {
|
|
38500
|
+
callbacks.onStart(res.statusCode ?? 0, incomingMessageHeaders(res));
|
|
38501
|
+
} catch (err) {
|
|
38502
|
+
req.destroy();
|
|
38503
|
+
callbacks.onError(err instanceof Error ? err.message : String(err));
|
|
38504
|
+
resolve37();
|
|
38505
|
+
return;
|
|
38776
38506
|
}
|
|
38777
|
-
|
|
38778
|
-
|
|
38507
|
+
res.on("data", (chunk) => {
|
|
38508
|
+
try {
|
|
38509
|
+
callbacks.onChunk(new Uint8Array(chunk));
|
|
38510
|
+
} catch (err) {
|
|
38511
|
+
req.destroy();
|
|
38512
|
+
callbacks.onError(err instanceof Error ? err.message : String(err));
|
|
38513
|
+
}
|
|
38514
|
+
});
|
|
38515
|
+
res.on("end", () => {
|
|
38516
|
+
callbacks.onEnd();
|
|
38517
|
+
resolve37();
|
|
38518
|
+
});
|
|
38519
|
+
res.on("error", (err) => {
|
|
38520
|
+
callbacks.onError(err.message);
|
|
38521
|
+
resolve37();
|
|
38522
|
+
});
|
|
38523
|
+
});
|
|
38524
|
+
req.setTimeout(LOCAL_PROXY_REQUEST_TIMEOUT_MS, () => {
|
|
38525
|
+
req.destroy(new Error(`Local preview request timed out after ${LOCAL_PROXY_REQUEST_TIMEOUT_MS}ms`));
|
|
38526
|
+
});
|
|
38527
|
+
req.on("error", (err) => {
|
|
38528
|
+
callbacks.onError(err.message);
|
|
38529
|
+
resolve37();
|
|
38530
|
+
});
|
|
38531
|
+
if (built.sendingBody && built.body != null) {
|
|
38532
|
+
req.write(built.body);
|
|
38779
38533
|
}
|
|
38780
|
-
|
|
38781
|
-
}
|
|
38782
|
-
callbacks.onError(err instanceof Error ? err.message : String(err));
|
|
38783
|
-
}
|
|
38534
|
+
req.end();
|
|
38535
|
+
});
|
|
38784
38536
|
}
|
|
38785
38537
|
|
|
38786
38538
|
// src/firehose/proxy/start-streaming-proxy.ts
|
|
@@ -38790,15 +38542,7 @@ function startStreamingProxy(ws, log2, pr) {
|
|
|
38790
38542
|
if (ws.readyState !== wrapper_default.OPEN) {
|
|
38791
38543
|
throw new Error("Preview stream interrupted (firehose connection closed)");
|
|
38792
38544
|
}
|
|
38793
|
-
|
|
38794
|
-
const ce = "content-encoding";
|
|
38795
|
-
const cl = "content-length";
|
|
38796
|
-
const keys = Object.keys(forwardedHeaders).filter(
|
|
38797
|
-
(k) => k.toLowerCase() !== ce && k.toLowerCase() !== cl
|
|
38798
|
-
);
|
|
38799
|
-
const filtered = {};
|
|
38800
|
-
for (const k of keys) if (forwardedHeaders[k] != null) filtered[k] = forwardedHeaders[k];
|
|
38801
|
-
sendWsMessage(ws, { type: "proxy_result_start", id: pr.id, statusCode, headers: filtered });
|
|
38545
|
+
sendWsMessage(ws, { type: "proxy_result_start", id: pr.id, statusCode, headers });
|
|
38802
38546
|
},
|
|
38803
38547
|
onChunk: (chunk) => {
|
|
38804
38548
|
const idBuf = Buffer.from(pr.id, "utf8");
|
|
@@ -38815,10 +38559,19 @@ function startStreamingProxy(ws, log2, pr) {
|
|
|
38815
38559
|
});
|
|
38816
38560
|
}
|
|
38817
38561
|
|
|
38818
|
-
// src/firehose/
|
|
38819
|
-
function
|
|
38820
|
-
const
|
|
38821
|
-
|
|
38562
|
+
// src/firehose/create-firehose-message-deps.ts
|
|
38563
|
+
function createFirehoseMessageDeps(ws, log2, previewEnvironmentManager) {
|
|
38564
|
+
const pendingProxyBody = /* @__PURE__ */ new Map();
|
|
38565
|
+
const earlyProxyBinaryBody = /* @__PURE__ */ new Map();
|
|
38566
|
+
const deps = {
|
|
38567
|
+
ws,
|
|
38568
|
+
log: log2,
|
|
38569
|
+
previewEnvironmentManager,
|
|
38570
|
+
pendingProxyBody,
|
|
38571
|
+
earlyProxyBinaryBody,
|
|
38572
|
+
startStreamingProxy: (pr) => startStreamingProxy(ws, log2, pr)
|
|
38573
|
+
};
|
|
38574
|
+
return deps;
|
|
38822
38575
|
}
|
|
38823
38576
|
|
|
38824
38577
|
// src/firehose/routing/firehose-message-router.ts
|
|
@@ -38853,36 +38606,95 @@ var handlePreviewEnvironmentLogsViewerClose = (msg, deps) => {
|
|
|
38853
38606
|
deps.previewEnvironmentManager.handleFirehoseLogViewerClose(environmentId, viewerId);
|
|
38854
38607
|
};
|
|
38855
38608
|
|
|
38856
|
-
// src/
|
|
38857
|
-
var
|
|
38858
|
-
|
|
38859
|
-
|
|
38860
|
-
|
|
38861
|
-
|
|
38862
|
-
|
|
38863
|
-
|
|
38864
|
-
|
|
38865
|
-
|
|
38866
|
-
|
|
38867
|
-
|
|
38868
|
-
|
|
38869
|
-
|
|
38870
|
-
|
|
38871
|
-
|
|
38872
|
-
|
|
38873
|
-
|
|
38874
|
-
|
|
38875
|
-
|
|
38876
|
-
|
|
38877
|
-
|
|
38878
|
-
|
|
38879
|
-
|
|
38880
|
-
|
|
38881
|
-
|
|
38882
|
-
|
|
38883
|
-
|
|
38884
|
-
|
|
38885
|
-
|
|
38609
|
+
// src/net/transient-local-fetch-retry.ts
|
|
38610
|
+
var LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS = [30, 100, 200, 400];
|
|
38611
|
+
function sleepMs(ms) {
|
|
38612
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
38613
|
+
}
|
|
38614
|
+
function collectErrorText(err) {
|
|
38615
|
+
const parts = [];
|
|
38616
|
+
let e = err;
|
|
38617
|
+
for (let depth = 0; depth < 6 && e != null; depth += 1) {
|
|
38618
|
+
if (e instanceof Error) {
|
|
38619
|
+
parts.push(e.message);
|
|
38620
|
+
e = e.cause;
|
|
38621
|
+
} else {
|
|
38622
|
+
parts.push(String(e));
|
|
38623
|
+
break;
|
|
38624
|
+
}
|
|
38625
|
+
}
|
|
38626
|
+
return parts.join(" ").toLowerCase();
|
|
38627
|
+
}
|
|
38628
|
+
function isTransientLocalServiceError(err) {
|
|
38629
|
+
const text = collectErrorText(err);
|
|
38630
|
+
if (!text) return false;
|
|
38631
|
+
return text.includes("fetch failed") || text.includes("econnrefused") || text.includes("econnreset") || text.includes("epipe") || text.includes("etimedout") || text.includes("socket hang up") || text.includes("network connection lost") || text.includes("ecanceled") || text.includes("aborted") || text.includes("und_err_socket") || text.includes("other side closed") || text.includes("eai_again");
|
|
38632
|
+
}
|
|
38633
|
+
|
|
38634
|
+
// src/firehose/proxy/proxy-to-local.ts
|
|
38635
|
+
function isIdempotentProxyMethod(method) {
|
|
38636
|
+
const m = method.toUpperCase();
|
|
38637
|
+
return m === "GET" || m === "HEAD" || m === "OPTIONS";
|
|
38638
|
+
}
|
|
38639
|
+
async function proxyToLocal(request) {
|
|
38640
|
+
const built = buildLocalProxyHttpRequest(request);
|
|
38641
|
+
if ("error" in built) {
|
|
38642
|
+
return { id: request.id, statusCode: 0, headers: {}, body: "", error: built.error };
|
|
38643
|
+
}
|
|
38644
|
+
const isHttps = request.url.startsWith("https:");
|
|
38645
|
+
const mod = await loadHttpModule(isHttps);
|
|
38646
|
+
const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
|
|
38647
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
38648
|
+
const once = await runLocalHttpRequestOnce(mod, built);
|
|
38649
|
+
const errMsg = once.error ?? "";
|
|
38650
|
+
const canRetry = attempt < maxAttempts - 1 && errMsg !== "" && isTransientLocalServiceError(new Error(errMsg));
|
|
38651
|
+
if (!canRetry) {
|
|
38652
|
+
return {
|
|
38653
|
+
id: request.id,
|
|
38654
|
+
statusCode: once.statusCode,
|
|
38655
|
+
headers: once.headers,
|
|
38656
|
+
body: once.body.toString("utf8"),
|
|
38657
|
+
error: once.error
|
|
38658
|
+
};
|
|
38659
|
+
}
|
|
38660
|
+
const waitMs = LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS[Math.min(attempt, LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length - 1)] ?? 100;
|
|
38661
|
+
await sleepMs(waitMs);
|
|
38662
|
+
}
|
|
38663
|
+
throw new Error("Local proxy retry loop exited unexpectedly");
|
|
38664
|
+
}
|
|
38665
|
+
|
|
38666
|
+
// src/firehose/routing/handlers/proxy-message.ts
|
|
38667
|
+
var handleProxyMessage = (msg, deps) => {
|
|
38668
|
+
const proxy = msg.proxy;
|
|
38669
|
+
if (!proxy || typeof proxy !== "object") return;
|
|
38670
|
+
const pr = proxy;
|
|
38671
|
+
if (pr.streamResponse) {
|
|
38672
|
+
const bodyLength = methodAllowsRequestBody(pr.method) ? pr.bodyLength ?? 0 : 0;
|
|
38673
|
+
const streamingRequest = {
|
|
38674
|
+
id: pr.id,
|
|
38675
|
+
method: pr.method,
|
|
38676
|
+
url: pr.url,
|
|
38677
|
+
headers: pr.headers,
|
|
38678
|
+
streamResponse: true
|
|
38679
|
+
};
|
|
38680
|
+
if (bodyLength > 0) {
|
|
38681
|
+
const earlyBody = deps.earlyProxyBinaryBody.get(pr.id);
|
|
38682
|
+
if (earlyBody !== void 0) {
|
|
38683
|
+
deps.earlyProxyBinaryBody.delete(pr.id);
|
|
38684
|
+
deps.startStreamingProxy({
|
|
38685
|
+
...streamingRequest,
|
|
38686
|
+
body: earlyBody.length > 0 ? earlyBody : void 0
|
|
38687
|
+
});
|
|
38688
|
+
} else {
|
|
38689
|
+
deps.pendingProxyBody.set(pr.id, { pr: streamingRequest });
|
|
38690
|
+
}
|
|
38691
|
+
} else {
|
|
38692
|
+
deps.startStreamingProxy(streamingRequest);
|
|
38693
|
+
}
|
|
38694
|
+
return;
|
|
38695
|
+
}
|
|
38696
|
+
void proxyToLocal(pr).then((res) => {
|
|
38697
|
+
if (res.error) deps.log(`[Proxy and log service] Preview proxy failed: ${res.error}`);
|
|
38886
38698
|
sendWsMessage(deps.ws, { type: "proxy_result", ...res, id: pr.id });
|
|
38887
38699
|
}).catch((err) => {
|
|
38888
38700
|
deps.log(
|
|
@@ -38908,37 +38720,63 @@ function dispatchFirehoseJsonMessage(msg, deps) {
|
|
|
38908
38720
|
|
|
38909
38721
|
// src/firehose/routing/try-consume-binary-proxy-body.ts
|
|
38910
38722
|
var PROXY_ID_BYTES = 36;
|
|
38723
|
+
var PROXY_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
38911
38724
|
function tryConsumeBinaryProxyBody(raw, deps) {
|
|
38912
38725
|
if (raw.length < PROXY_ID_BYTES) return false;
|
|
38913
38726
|
const id = raw.slice(0, PROXY_ID_BYTES).toString("utf8");
|
|
38914
|
-
|
|
38915
|
-
if (!pending2) return false;
|
|
38916
|
-
deps.pendingProxyBody.delete(id);
|
|
38727
|
+
if (!PROXY_ID_RE.test(id)) return false;
|
|
38917
38728
|
const body = raw.slice(PROXY_ID_BYTES);
|
|
38918
|
-
|
|
38919
|
-
|
|
38920
|
-
|
|
38921
|
-
|
|
38729
|
+
const bodyBytes = body.length > 0 ? new Uint8Array(body) : new Uint8Array(0);
|
|
38730
|
+
const pending2 = deps.pendingProxyBody.get(id);
|
|
38731
|
+
if (pending2) {
|
|
38732
|
+
deps.pendingProxyBody.delete(id);
|
|
38733
|
+
deps.startStreamingProxy({
|
|
38734
|
+
...pending2.pr,
|
|
38735
|
+
body: bodyBytes.length > 0 ? bodyBytes : void 0
|
|
38736
|
+
});
|
|
38737
|
+
return true;
|
|
38738
|
+
}
|
|
38739
|
+
deps.earlyProxyBinaryBody.set(id, bodyBytes);
|
|
38922
38740
|
return true;
|
|
38923
38741
|
}
|
|
38924
38742
|
|
|
38925
|
-
// src/firehose/
|
|
38926
|
-
function
|
|
38743
|
+
// src/firehose/handle-inbound-firehose-frame.ts
|
|
38744
|
+
function handleInboundFirehoseFrame(raw, deps) {
|
|
38745
|
+
if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
|
|
38746
|
+
return;
|
|
38747
|
+
}
|
|
38748
|
+
let msg;
|
|
38749
|
+
try {
|
|
38750
|
+
const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
|
|
38751
|
+
msg = JSON.parse(text);
|
|
38752
|
+
} catch {
|
|
38753
|
+
return;
|
|
38754
|
+
}
|
|
38755
|
+
if (msg.type === "proxy") {
|
|
38756
|
+
dispatchFirehoseJsonMessage(msg, deps);
|
|
38757
|
+
return;
|
|
38758
|
+
}
|
|
38759
|
+
setImmediate(() => {
|
|
38760
|
+
dispatchFirehoseJsonMessage(msg, deps);
|
|
38761
|
+
});
|
|
38762
|
+
}
|
|
38763
|
+
|
|
38764
|
+
// src/firehose/wire-firehose-websocket.ts
|
|
38765
|
+
function wireFirehoseWebSocket(options) {
|
|
38927
38766
|
const {
|
|
38928
|
-
|
|
38767
|
+
ws,
|
|
38768
|
+
deps,
|
|
38769
|
+
log: log2,
|
|
38770
|
+
previewEnvironmentManager,
|
|
38929
38771
|
workspaceId,
|
|
38930
38772
|
bridgeName,
|
|
38931
38773
|
proxyPorts,
|
|
38932
|
-
log: log2,
|
|
38933
|
-
previewEnvironmentManager,
|
|
38934
38774
|
onOpen,
|
|
38935
38775
|
onClose,
|
|
38936
38776
|
suppressWebSocketErrors
|
|
38937
38777
|
} = options;
|
|
38938
|
-
const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
|
|
38939
|
-
applyCliOutboundNetworkPreferences();
|
|
38940
|
-
const ws = new wrapper_default(wsUrl, buildCliWebSocketClientOptions(wsUrl));
|
|
38941
38778
|
let clearClientPing = null;
|
|
38779
|
+
let identifyParams = { workspaceId, bridgeName, proxyPorts };
|
|
38942
38780
|
function disposeClientPing() {
|
|
38943
38781
|
clearClientPing?.();
|
|
38944
38782
|
clearClientPing = null;
|
|
@@ -38946,36 +38784,32 @@ function connectFirehose(options) {
|
|
|
38946
38784
|
const firehoseSend = (payload) => {
|
|
38947
38785
|
sendWsMessage(ws, payload);
|
|
38948
38786
|
};
|
|
38949
|
-
|
|
38950
|
-
|
|
38951
|
-
ws
|
|
38952
|
-
|
|
38953
|
-
|
|
38954
|
-
|
|
38955
|
-
|
|
38956
|
-
|
|
38787
|
+
function sendIdentify(params) {
|
|
38788
|
+
identifyParams = params;
|
|
38789
|
+
if (ws.readyState !== wrapper_default.OPEN) return;
|
|
38790
|
+
sendWsMessage(ws, {
|
|
38791
|
+
type: "identify",
|
|
38792
|
+
workspaceId: params.workspaceId,
|
|
38793
|
+
bridgeName: params.bridgeName,
|
|
38794
|
+
proxyPorts: params.proxyPorts
|
|
38795
|
+
});
|
|
38796
|
+
}
|
|
38797
|
+
function detachPreviewEnvironmentManager() {
|
|
38798
|
+
previewEnvironmentManager.detachFirehose();
|
|
38799
|
+
}
|
|
38957
38800
|
ws.on("open", () => {
|
|
38958
38801
|
disposeClientPing();
|
|
38959
38802
|
clearClientPing = attachWebSocketClientPing(ws, CLI_WEBSOCKET_CLIENT_PING_MS);
|
|
38960
38803
|
onOpen?.();
|
|
38961
38804
|
previewEnvironmentManager.attachFirehose(firehoseSend);
|
|
38962
|
-
|
|
38805
|
+
sendIdentify(identifyParams);
|
|
38963
38806
|
});
|
|
38964
38807
|
ws.on("message", (raw) => {
|
|
38965
|
-
|
|
38966
|
-
return;
|
|
38967
|
-
}
|
|
38968
|
-
setImmediate(() => {
|
|
38969
|
-
try {
|
|
38970
|
-
const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
|
|
38971
|
-
dispatchFirehoseJsonMessage(JSON.parse(text), deps);
|
|
38972
|
-
} catch {
|
|
38973
|
-
}
|
|
38974
|
-
});
|
|
38808
|
+
handleInboundFirehoseFrame(raw, deps);
|
|
38975
38809
|
});
|
|
38976
38810
|
ws.on("close", (code, reason) => {
|
|
38977
38811
|
disposeClientPing();
|
|
38978
|
-
|
|
38812
|
+
detachPreviewEnvironmentManager();
|
|
38979
38813
|
const reasonStr = typeof reason === "string" ? reason : reason.toString();
|
|
38980
38814
|
onClose?.(code, reasonStr);
|
|
38981
38815
|
});
|
|
@@ -38988,25 +38822,64 @@ function connectFirehose(options) {
|
|
|
38988
38822
|
safeCloseWebSocket(ws);
|
|
38989
38823
|
}
|
|
38990
38824
|
});
|
|
38825
|
+
return { disposeClientPing, sendIdentify, detachPreviewEnvironmentManager };
|
|
38826
|
+
}
|
|
38827
|
+
|
|
38828
|
+
// src/firehose/connect-firehose.ts
|
|
38829
|
+
function connectFirehose(options) {
|
|
38830
|
+
const {
|
|
38831
|
+
firehoseServerUrl,
|
|
38832
|
+
workspaceId,
|
|
38833
|
+
bridgeName,
|
|
38834
|
+
proxyPorts,
|
|
38835
|
+
log: log2,
|
|
38836
|
+
previewEnvironmentManager,
|
|
38837
|
+
onOpen,
|
|
38838
|
+
onClose,
|
|
38839
|
+
suppressWebSocketErrors
|
|
38840
|
+
} = options;
|
|
38841
|
+
const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
|
|
38842
|
+
const ws = new wrapper_default(wsUrl, buildCliWebSocketClientOptions(wsUrl));
|
|
38843
|
+
const deps = createFirehoseMessageDeps(ws, log2, previewEnvironmentManager);
|
|
38844
|
+
const wired = wireFirehoseWebSocket({
|
|
38845
|
+
ws,
|
|
38846
|
+
deps,
|
|
38847
|
+
log: log2,
|
|
38848
|
+
previewEnvironmentManager,
|
|
38849
|
+
workspaceId,
|
|
38850
|
+
bridgeName,
|
|
38851
|
+
proxyPorts,
|
|
38852
|
+
onOpen,
|
|
38853
|
+
onClose,
|
|
38854
|
+
suppressWebSocketErrors
|
|
38855
|
+
});
|
|
38991
38856
|
return {
|
|
38992
38857
|
close() {
|
|
38993
|
-
disposeClientPing();
|
|
38994
|
-
|
|
38858
|
+
wired.disposeClientPing();
|
|
38859
|
+
wired.detachPreviewEnvironmentManager();
|
|
38995
38860
|
safeCloseWebSocket(ws);
|
|
38996
38861
|
},
|
|
38997
|
-
isConnected: () => ws.readyState === wrapper_default.OPEN
|
|
38862
|
+
isConnected: () => ws.readyState === wrapper_default.OPEN,
|
|
38863
|
+
updateIdentify: wired.sendIdentify
|
|
38998
38864
|
};
|
|
38999
38865
|
}
|
|
39000
38866
|
|
|
39001
|
-
// src/connection/
|
|
39002
|
-
function
|
|
39003
|
-
|
|
39004
|
-
|
|
39005
|
-
|
|
39006
|
-
|
|
39007
|
-
|
|
39008
|
-
|
|
39009
|
-
|
|
38867
|
+
// src/connection/firehose/compare-firehose-params.ts
|
|
38868
|
+
function proxyPortsEqual(a, b) {
|
|
38869
|
+
if (a.length !== b.length) return false;
|
|
38870
|
+
const sa = [...a].sort((x, y) => x - y);
|
|
38871
|
+
const sb = [...b].sort((x, y) => x - y);
|
|
38872
|
+
return sa.every((v, i) => v === sb[i]);
|
|
38873
|
+
}
|
|
38874
|
+
function sameFirehoseBridge(a, b) {
|
|
38875
|
+
return a.firehoseServerUrl === b.firehoseServerUrl && a.workspaceId === b.workspaceId && a.bridgeName === b.bridgeName;
|
|
38876
|
+
}
|
|
38877
|
+
function shouldReuseOpenFirehoseConnection(prev, next, isConnected) {
|
|
38878
|
+
return Boolean(prev && sameFirehoseBridge(prev, next) && isConnected());
|
|
38879
|
+
}
|
|
38880
|
+
|
|
38881
|
+
// src/connection/firehose/create-firehose-connect-callbacks.ts
|
|
38882
|
+
function createFirehoseConnectCallbacks(state, myGen, logFn, scheduleFirehoseRetryAfterDrop) {
|
|
39010
38883
|
function firehoseCtx() {
|
|
39011
38884
|
return {
|
|
39012
38885
|
closedByUser: state.closedByUser,
|
|
@@ -39015,6 +38888,55 @@ function attachFirehoseAfterIdentified(ctx, params) {
|
|
|
39015
38888
|
firehoseQuiet: state.firehoseQuiet
|
|
39016
38889
|
};
|
|
39017
38890
|
}
|
|
38891
|
+
return {
|
|
38892
|
+
onOpen: () => {
|
|
38893
|
+
if (myGen !== state.firehoseGeneration) return;
|
|
38894
|
+
try {
|
|
38895
|
+
clearFirehoseReconnectQuietOnOpen(
|
|
38896
|
+
{
|
|
38897
|
+
firehoseQuiet: state.firehoseQuiet,
|
|
38898
|
+
firehoseOutage: state.firehoseOutage,
|
|
38899
|
+
lastFirehoseReconnectCloseMeta: state.lastFirehoseReconnectCloseMeta
|
|
38900
|
+
},
|
|
38901
|
+
logFn
|
|
38902
|
+
);
|
|
38903
|
+
} catch {
|
|
38904
|
+
}
|
|
38905
|
+
const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
|
|
38906
|
+
state.firehoseReconnectAttempt = 0;
|
|
38907
|
+
if (!logOpenAsFirehoseReconnect) {
|
|
38908
|
+
try {
|
|
38909
|
+
logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
|
|
38910
|
+
} catch {
|
|
38911
|
+
}
|
|
38912
|
+
}
|
|
38913
|
+
},
|
|
38914
|
+
onClose: (code, reason) => {
|
|
38915
|
+
if (myGen !== state.firehoseGeneration) return;
|
|
38916
|
+
state.firehoseHandle = null;
|
|
38917
|
+
if (state.closedByUser) return;
|
|
38918
|
+
state.lastFirehoseReconnectCloseMeta = { code, reason };
|
|
38919
|
+
try {
|
|
38920
|
+
beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
|
|
38921
|
+
} catch {
|
|
38922
|
+
}
|
|
38923
|
+
try {
|
|
38924
|
+
scheduleFirehoseRetryAfterDrop({ code, reason });
|
|
38925
|
+
} catch {
|
|
38926
|
+
}
|
|
38927
|
+
}
|
|
38928
|
+
};
|
|
38929
|
+
}
|
|
38930
|
+
|
|
38931
|
+
// src/connection/firehose/create-firehose-reconnect-scheduler.ts
|
|
38932
|
+
function createFirehoseReconnectScheduler(ctx, reconnect) {
|
|
38933
|
+
const { state, logFn } = ctx;
|
|
38934
|
+
function clearFirehoseReconnectTimer() {
|
|
38935
|
+
if (state.firehoseReconnectTimeout != null) {
|
|
38936
|
+
clearTimeout(state.firehoseReconnectTimeout);
|
|
38937
|
+
state.firehoseReconnectTimeout = null;
|
|
38938
|
+
}
|
|
38939
|
+
}
|
|
39018
38940
|
function scheduleFirehoseRetryAfterDrop(closeMeta) {
|
|
39019
38941
|
if (state.closedByUser) return;
|
|
39020
38942
|
const meta = closeMeta ?? state.lastFirehoseReconnectCloseMeta ?? void 0;
|
|
@@ -39036,30 +38958,60 @@ function attachFirehoseAfterIdentified(ctx, params) {
|
|
|
39036
38958
|
state.firehoseReconnectTimeout = id;
|
|
39037
38959
|
},
|
|
39038
38960
|
shouldAbortBeforeRun: () => state.closedByUser,
|
|
39039
|
-
run:
|
|
39040
|
-
const p = state.lastFirehoseParams;
|
|
39041
|
-
if (!p) return;
|
|
39042
|
-
attachFirehoseAfterIdentified(ctx, p);
|
|
39043
|
-
},
|
|
38961
|
+
run: reconnect,
|
|
39044
38962
|
reschedule: () => {
|
|
39045
38963
|
scheduleFirehoseRetryAfterDrop();
|
|
39046
38964
|
}
|
|
39047
38965
|
});
|
|
39048
38966
|
}
|
|
39049
|
-
|
|
39050
|
-
|
|
39051
|
-
|
|
39052
|
-
|
|
39053
|
-
|
|
39054
|
-
|
|
39055
|
-
|
|
38967
|
+
return { clearFirehoseReconnectTimer, scheduleFirehoseRetryAfterDrop };
|
|
38968
|
+
}
|
|
38969
|
+
function logInitialFirehoseConnect(state, logFn) {
|
|
38970
|
+
if (state.firehoseReconnectAttempt !== 0) return;
|
|
38971
|
+
try {
|
|
38972
|
+
logFn("Connecting to preview tunnel (local HTTP proxy and dev logs)\u2026");
|
|
38973
|
+
} catch {
|
|
39056
38974
|
}
|
|
38975
|
+
}
|
|
38976
|
+
function replaceFirehoseHandle(state) {
|
|
39057
38977
|
state.firehoseGeneration += 1;
|
|
39058
38978
|
const myGen = state.firehoseGeneration;
|
|
39059
38979
|
if (state.firehoseHandle) {
|
|
39060
38980
|
state.firehoseHandle.close();
|
|
39061
38981
|
state.firehoseHandle = null;
|
|
39062
38982
|
}
|
|
38983
|
+
return myGen;
|
|
38984
|
+
}
|
|
38985
|
+
|
|
38986
|
+
// src/connection/firehose/attach-firehose-after-identified.ts
|
|
38987
|
+
function attachFirehoseAfterIdentified(ctx, params) {
|
|
38988
|
+
const { state, previewEnvironmentManager, logFn } = ctx;
|
|
38989
|
+
const prev = state.lastFirehoseParams;
|
|
38990
|
+
if (shouldReuseOpenFirehoseConnection(prev, params, () => state.firehoseHandle?.isConnected() ?? false)) {
|
|
38991
|
+
state.lastFirehoseParams = params;
|
|
38992
|
+
if (prev && !proxyPortsEqual(prev.proxyPorts, params.proxyPorts)) {
|
|
38993
|
+
state.firehoseHandle?.updateIdentify(params);
|
|
38994
|
+
}
|
|
38995
|
+
return;
|
|
38996
|
+
}
|
|
38997
|
+
const { clearFirehoseReconnectTimer, scheduleFirehoseRetryAfterDrop } = createFirehoseReconnectScheduler(
|
|
38998
|
+
ctx,
|
|
38999
|
+
() => {
|
|
39000
|
+
const p = state.lastFirehoseParams;
|
|
39001
|
+
if (!p) return;
|
|
39002
|
+
attachFirehoseAfterIdentified(ctx, p);
|
|
39003
|
+
}
|
|
39004
|
+
);
|
|
39005
|
+
state.lastFirehoseParams = params;
|
|
39006
|
+
clearFirehoseReconnectTimer();
|
|
39007
|
+
logInitialFirehoseConnect(state, logFn);
|
|
39008
|
+
const myGen = replaceFirehoseHandle(state);
|
|
39009
|
+
const { onOpen, onClose } = createFirehoseConnectCallbacks(
|
|
39010
|
+
state,
|
|
39011
|
+
myGen,
|
|
39012
|
+
logFn,
|
|
39013
|
+
scheduleFirehoseRetryAfterDrop
|
|
39014
|
+
);
|
|
39063
39015
|
state.firehoseHandle = connectFirehose({
|
|
39064
39016
|
firehoseServerUrl: params.firehoseServerUrl,
|
|
39065
39017
|
workspaceId: params.workspaceId,
|
|
@@ -39068,45 +39020,17 @@ function attachFirehoseAfterIdentified(ctx, params) {
|
|
|
39068
39020
|
log: logFn,
|
|
39069
39021
|
previewEnvironmentManager,
|
|
39070
39022
|
suppressWebSocketErrors: () => !state.closedByUser && state.firehoseOutage.startedAt != null,
|
|
39071
|
-
onOpen
|
|
39072
|
-
|
|
39073
|
-
try {
|
|
39074
|
-
clearFirehoseReconnectQuietOnOpen(
|
|
39075
|
-
{
|
|
39076
|
-
firehoseQuiet: state.firehoseQuiet,
|
|
39077
|
-
firehoseOutage: state.firehoseOutage,
|
|
39078
|
-
lastFirehoseReconnectCloseMeta: state.lastFirehoseReconnectCloseMeta
|
|
39079
|
-
},
|
|
39080
|
-
logFn
|
|
39081
|
-
);
|
|
39082
|
-
} catch {
|
|
39083
|
-
}
|
|
39084
|
-
const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
|
|
39085
|
-
state.firehoseReconnectAttempt = 0;
|
|
39086
|
-
if (!logOpenAsFirehoseReconnect) {
|
|
39087
|
-
try {
|
|
39088
|
-
logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
|
|
39089
|
-
} catch {
|
|
39090
|
-
}
|
|
39091
|
-
}
|
|
39092
|
-
},
|
|
39093
|
-
onClose: (code, reason) => {
|
|
39094
|
-
if (myGen !== state.firehoseGeneration) return;
|
|
39095
|
-
state.firehoseHandle = null;
|
|
39096
|
-
if (state.closedByUser) return;
|
|
39097
|
-
state.lastFirehoseReconnectCloseMeta = { code, reason };
|
|
39098
|
-
try {
|
|
39099
|
-
beginFirehoseDeferredDisconnect(firehoseCtx(), code, reason, logFn);
|
|
39100
|
-
} catch {
|
|
39101
|
-
}
|
|
39102
|
-
try {
|
|
39103
|
-
scheduleFirehoseRetryAfterDrop({ code, reason });
|
|
39104
|
-
} catch {
|
|
39105
|
-
}
|
|
39106
|
-
}
|
|
39023
|
+
onOpen,
|
|
39024
|
+
onClose
|
|
39107
39025
|
});
|
|
39108
39026
|
}
|
|
39109
39027
|
|
|
39028
|
+
// src/connection/firehose/update-firehose-identify-on-open-connection.ts
|
|
39029
|
+
function updateFirehoseIdentifyOnOpenConnection(state, params) {
|
|
39030
|
+
state.lastFirehoseParams = params;
|
|
39031
|
+
state.firehoseHandle?.updateIdentify(params);
|
|
39032
|
+
}
|
|
39033
|
+
|
|
39110
39034
|
// src/connection/create-bridge-identified-handler.ts
|
|
39111
39035
|
function createOnBridgeIdentified(opts) {
|
|
39112
39036
|
const { previewEnvironmentManager, firehoseServerUrl, workspaceId, state, logFn } = opts;
|
|
@@ -39143,6 +39067,7 @@ function createBridgeMessageDeps(params) {
|
|
|
39143
39067
|
reportAutoDetectedAgents,
|
|
39144
39068
|
warmupAgentCapabilitiesOnConnect: warmupAgentCapabilitiesOnConnect2,
|
|
39145
39069
|
previewEnvironmentManager,
|
|
39070
|
+
updateFirehoseProxyPorts,
|
|
39146
39071
|
e2ee,
|
|
39147
39072
|
cloudApiBaseUrl,
|
|
39148
39073
|
getCloudAccessToken
|
|
@@ -39161,6 +39086,7 @@ function createBridgeMessageDeps(params) {
|
|
|
39161
39086
|
reportAutoDetectedAgents,
|
|
39162
39087
|
warmupAgentCapabilitiesOnConnect: warmupAgentCapabilitiesOnConnect2,
|
|
39163
39088
|
previewEnvironmentManager,
|
|
39089
|
+
updateFirehoseProxyPorts,
|
|
39164
39090
|
e2ee,
|
|
39165
39091
|
cloudApiBaseUrl,
|
|
39166
39092
|
getCloudAccessToken
|
|
@@ -39421,6 +39347,11 @@ function createBridgeRuntimeMessageSetup(options) {
|
|
|
39421
39347
|
getWs
|
|
39422
39348
|
}),
|
|
39423
39349
|
previewEnvironmentManager,
|
|
39350
|
+
updateFirehoseProxyPorts: (proxyPorts) => {
|
|
39351
|
+
const p = state.lastFirehoseParams;
|
|
39352
|
+
if (!p) return;
|
|
39353
|
+
updateFirehoseIdentifyOnOpenConnection(state, { ...p, proxyPorts });
|
|
39354
|
+
},
|
|
39424
39355
|
e2ee,
|
|
39425
39356
|
cloudApiBaseUrl: apiUrl,
|
|
39426
39357
|
getCloudAccessToken: () => tokens.accessToken
|
|
@@ -46690,16 +46621,16 @@ async function applySessionWipToPreviewCheckouts(options) {
|
|
|
46690
46621
|
return;
|
|
46691
46622
|
}
|
|
46692
46623
|
await gitResetWorktreeToBranch(previewCheckout, deployBranch);
|
|
46693
|
-
const
|
|
46624
|
+
const applied = await applyWorkingTreeWip(
|
|
46694
46625
|
previewCheckout,
|
|
46695
46626
|
state.sessionCheckout,
|
|
46696
46627
|
state.wip,
|
|
46697
46628
|
log2
|
|
46698
46629
|
);
|
|
46699
|
-
if (!
|
|
46630
|
+
if (!applied.ok) {
|
|
46700
46631
|
failure = {
|
|
46701
46632
|
ok: false,
|
|
46702
|
-
error:
|
|
46633
|
+
error: applied.error ?? `Failed to apply session WIP to preview (${state.repoPathRelativeToWorkspaceRoot})`
|
|
46703
46634
|
};
|
|
46704
46635
|
}
|
|
46705
46636
|
});
|
|
@@ -46798,7 +46729,7 @@ async function deploySessionToPreviewEnvironment(options) {
|
|
|
46798
46729
|
return { ok: false, error: "Failed to create preview environment worktrees" };
|
|
46799
46730
|
}
|
|
46800
46731
|
await yieldToEventLoop2();
|
|
46801
|
-
const
|
|
46732
|
+
const applied = await applySessionWipToPreviewCheckouts({
|
|
46802
46733
|
previewWorktreeManager,
|
|
46803
46734
|
environmentId: eid,
|
|
46804
46735
|
deployBranch,
|
|
@@ -46806,8 +46737,8 @@ async function deploySessionToPreviewEnvironment(options) {
|
|
|
46806
46737
|
repoStates,
|
|
46807
46738
|
log: log2
|
|
46808
46739
|
});
|
|
46809
|
-
if (!
|
|
46810
|
-
return { ok: false, error:
|
|
46740
|
+
if (!applied.ok) {
|
|
46741
|
+
return { ok: false, error: applied.error };
|
|
46811
46742
|
}
|
|
46812
46743
|
log2(
|
|
46813
46744
|
`[worktrees] Deployed session ${sid.slice(0, 8)}\u2026 to preview ${eid.slice(0, 8)}\u2026 on branch ${deployBranch}.`
|
|
@@ -47129,8 +47060,6 @@ function dispatchLocalPrompt(next, deps) {
|
|
|
47129
47060
|
...sessionParentPath ? { sessionParentPath } : {},
|
|
47130
47061
|
...worktreeBaseBranches && Object.keys(worktreeBaseBranches).length > 0 ? { worktreeBaseBranches } : {},
|
|
47131
47062
|
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
47132
|
-
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
47133
|
-
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
47134
47063
|
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
|
|
47135
47064
|
...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
|
|
47136
47065
|
...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
|
|
@@ -47393,8 +47322,7 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
47393
47322
|
return true;
|
|
47394
47323
|
};
|
|
47395
47324
|
const sendResult = (result) => {
|
|
47396
|
-
const
|
|
47397
|
-
const encryptedFields = result.type === "prompt_result" && !skipEncryptForChangeSummaryFollowUp ? ["output", "error"] : [];
|
|
47325
|
+
const encryptedFields = result.type === "prompt_result" ? ["output", "error"] : [];
|
|
47398
47326
|
sendBridgeMessage(result, encryptedFields);
|
|
47399
47327
|
if (result.type === "prompt_result") {
|
|
47400
47328
|
const pr = result;
|
|
@@ -47458,61 +47386,6 @@ function parseWorktreeBaseBranches(msg) {
|
|
|
47458
47386
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
47459
47387
|
}
|
|
47460
47388
|
|
|
47461
|
-
// src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
|
|
47462
|
-
function decryptChangeSummaryFileInput(row, e2ee) {
|
|
47463
|
-
if (!e2ee) return row;
|
|
47464
|
-
for (const field of ["path", "patchContent", "oldText", "newText"]) {
|
|
47465
|
-
const raw = row[field];
|
|
47466
|
-
if (typeof raw !== "string" || raw.trim() === "") continue;
|
|
47467
|
-
let o;
|
|
47468
|
-
try {
|
|
47469
|
-
o = JSON.parse(raw);
|
|
47470
|
-
} catch {
|
|
47471
|
-
continue;
|
|
47472
|
-
}
|
|
47473
|
-
if (!isE2eeEnvelope(o.ee)) continue;
|
|
47474
|
-
try {
|
|
47475
|
-
const d = e2ee.decryptMessage(o);
|
|
47476
|
-
const out = {
|
|
47477
|
-
path: typeof d.path === "string" ? d.path : row.path
|
|
47478
|
-
};
|
|
47479
|
-
if (d.directoryRemoved === true) out.directoryRemoved = true;
|
|
47480
|
-
else if (row.directoryRemoved === true) out.directoryRemoved = true;
|
|
47481
|
-
if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
|
|
47482
|
-
else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
|
|
47483
|
-
if (typeof d.oldText === "string") out.oldText = d.oldText;
|
|
47484
|
-
else if (typeof row.oldText === "string") out.oldText = row.oldText;
|
|
47485
|
-
if (typeof d.newText === "string") out.newText = d.newText;
|
|
47486
|
-
else if (typeof row.newText === "string") out.newText = row.newText;
|
|
47487
|
-
return out;
|
|
47488
|
-
} catch {
|
|
47489
|
-
return row;
|
|
47490
|
-
}
|
|
47491
|
-
}
|
|
47492
|
-
return row;
|
|
47493
|
-
}
|
|
47494
|
-
|
|
47495
|
-
// src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
|
|
47496
|
-
function hasSummarizePayload(f) {
|
|
47497
|
-
return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
|
|
47498
|
-
}
|
|
47499
|
-
function resolveChangeSummaryPromptForAgent(params) {
|
|
47500
|
-
const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
|
|
47501
|
-
const snaps = params.sessionChangeSummaryFileSnapshots;
|
|
47502
|
-
if (!isBuiltin || !snaps || snaps.length === 0) {
|
|
47503
|
-
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
47504
|
-
}
|
|
47505
|
-
const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
|
|
47506
|
-
const withPayload = decrypted.filter(hasSummarizePayload);
|
|
47507
|
-
if (withPayload.length === 0) {
|
|
47508
|
-
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
47509
|
-
}
|
|
47510
|
-
return {
|
|
47511
|
-
promptText: buildSessionChangeSummaryPrompt(withPayload),
|
|
47512
|
-
sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
|
|
47513
|
-
};
|
|
47514
|
-
}
|
|
47515
|
-
|
|
47516
47389
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
47517
47390
|
import { execFile as execFile8 } from "node:child_process";
|
|
47518
47391
|
import { promisify as promisify9 } from "node:util";
|
|
@@ -47559,29 +47432,9 @@ async function runBridgePromptPreamble(params) {
|
|
|
47559
47432
|
});
|
|
47560
47433
|
}
|
|
47561
47434
|
}
|
|
47562
|
-
function parseChangeSummarySnapshots(raw) {
|
|
47563
|
-
if (!Array.isArray(raw) || raw.length === 0) return void 0;
|
|
47564
|
-
const out = [];
|
|
47565
|
-
for (const item of raw) {
|
|
47566
|
-
if (!item || typeof item !== "object") continue;
|
|
47567
|
-
const o = item;
|
|
47568
|
-
const path81 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
|
|
47569
|
-
if (!path81) continue;
|
|
47570
|
-
const row = { path: path81 };
|
|
47571
|
-
if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
|
|
47572
|
-
if (typeof o.oldText === "string") row.oldText = o.oldText;
|
|
47573
|
-
if (typeof o.newText === "string") row.newText = o.newText;
|
|
47574
|
-
if (o.directoryRemoved === true) row.directoryRemoved = true;
|
|
47575
|
-
out.push(row);
|
|
47576
|
-
}
|
|
47577
|
-
return out.length > 0 ? out : void 0;
|
|
47578
|
-
}
|
|
47579
47435
|
function parseFollowUpFieldsFromPromptMessage(msg) {
|
|
47580
47436
|
const followUpCatalogPromptId = typeof msg.followUpCatalogPromptId === "string" && msg.followUpCatalogPromptId.trim() !== "" ? msg.followUpCatalogPromptId.trim() : null;
|
|
47581
|
-
|
|
47582
|
-
const sessionChangeSummaryFilePaths = Array.isArray(rawPaths) ? rawPaths.filter((p) => typeof p === "string" && p.trim() !== "").map((p) => p.trim()) : void 0;
|
|
47583
|
-
const sessionChangeSummaryFileSnapshots = parseChangeSummarySnapshots(msg.sessionChangeSummaryFileSnapshots);
|
|
47584
|
-
return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
|
|
47437
|
+
return { followUpCatalogPromptId };
|
|
47585
47438
|
}
|
|
47586
47439
|
|
|
47587
47440
|
// src/agents/acp/from-bridge/handle-bridge-prompt/run-preamble-and-prompt.ts
|
|
@@ -47612,25 +47465,9 @@ async function runPreambleAndPrompt(params) {
|
|
|
47612
47465
|
runId,
|
|
47613
47466
|
effectiveCwd
|
|
47614
47467
|
});
|
|
47615
|
-
const {
|
|
47616
|
-
followUpCatalogPromptId,
|
|
47617
|
-
sessionChangeSummaryFilePaths: pathsFromBridge,
|
|
47618
|
-
sessionChangeSummaryFileSnapshots
|
|
47619
|
-
} = parseFollowUpFieldsFromPromptMessage(msg);
|
|
47620
|
-
const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
|
|
47621
|
-
followUpCatalogPromptId,
|
|
47622
|
-
sessionChangeSummaryFileSnapshots,
|
|
47623
|
-
bridgePromptText: promptText,
|
|
47624
|
-
e2ee: deps.e2ee
|
|
47625
|
-
});
|
|
47626
|
-
if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
|
|
47627
|
-
deps.log(
|
|
47628
|
-
"[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
|
|
47629
|
-
);
|
|
47630
|
-
}
|
|
47631
|
-
const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
|
|
47468
|
+
const { followUpCatalogPromptId } = parseFollowUpFieldsFromPromptMessage(msg);
|
|
47632
47469
|
deps.acpManager.handlePrompt({
|
|
47633
|
-
promptText
|
|
47470
|
+
promptText,
|
|
47634
47471
|
promptId: msg.id,
|
|
47635
47472
|
sessionId,
|
|
47636
47473
|
runId,
|
|
@@ -47642,7 +47479,6 @@ async function runPreambleAndPrompt(params) {
|
|
|
47642
47479
|
sendResult,
|
|
47643
47480
|
sendSessionUpdate,
|
|
47644
47481
|
followUpCatalogPromptId,
|
|
47645
|
-
sessionChangeSummaryFilePaths: pathsForUpload,
|
|
47646
47482
|
cloudApiBaseUrl: deps.cloudApiBaseUrl,
|
|
47647
47483
|
getCloudAccessToken: deps.getCloudAccessToken,
|
|
47648
47484
|
e2ee: deps.e2ee,
|
|
@@ -49402,12 +49238,17 @@ var handlePreviewEnvironmentControl = (msg, deps) => {
|
|
|
49402
49238
|
};
|
|
49403
49239
|
|
|
49404
49240
|
// src/routing/handlers/preview-environments-config.ts
|
|
49241
|
+
var VALID_PORT = (p) => typeof p === "number" && Number.isInteger(p) && p > 0 && p < 65536;
|
|
49405
49242
|
var handlePreviewEnvironmentsConfig = (msg, deps) => {
|
|
49406
49243
|
const previewEnvironments = msg.previewEnvironments;
|
|
49244
|
+
const proxyPorts = Array.isArray(msg.proxyPorts) ? msg.proxyPorts.filter(VALID_PORT) : void 0;
|
|
49407
49245
|
setImmediate(() => {
|
|
49408
49246
|
deps.previewEnvironmentManager?.applyConfig(previewEnvironments ?? []);
|
|
49409
49247
|
const environmentIds = parsePreviewEnvironmentDefs(previewEnvironments ?? []).map((d) => d.environmentId);
|
|
49410
49248
|
void deps.previewWorktreeManager?.syncConfiguredEnvironments(environmentIds);
|
|
49249
|
+
if (proxyPorts !== void 0) {
|
|
49250
|
+
deps.updateFirehoseProxyPorts?.(proxyPorts);
|
|
49251
|
+
}
|
|
49411
49252
|
});
|
|
49412
49253
|
};
|
|
49413
49254
|
|