ds-harness-remote 0.5.11 → 0.5.13
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/acp/adapters/isolated-process.d.ts.map +1 -1
- package/dist/acp/adapters/isolated-process.js +12 -3
- package/dist/acp/adapters/isolated-process.js.map +1 -1
- package/dist/acp/adapters/opencode-native-api.d.ts +4 -2
- package/dist/acp/adapters/opencode-native-api.d.ts.map +1 -1
- package/dist/acp/adapters/opencode-native-api.js +29 -3
- package/dist/acp/adapters/opencode-native-api.js.map +1 -1
- package/dist/acp/adapters/opencode-process.d.ts +2 -0
- package/dist/acp/adapters/opencode-process.d.ts.map +1 -1
- package/dist/acp/adapters/opencode-process.js +64 -24
- package/dist/acp/adapters/opencode-process.js.map +1 -1
- package/dist/acp/gateway.d.ts +3 -0
- package/dist/acp/gateway.d.ts.map +1 -1
- package/dist/acp/gateway.js +77 -13
- package/dist/acp/gateway.js.map +1 -1
- package/dist/acp/peer-bridge.d.ts.map +1 -1
- package/dist/acp/peer-bridge.js +2 -1
- package/dist/acp/peer-bridge.js.map +1 -1
- package/dist/acp/virtual-contract.d.ts +1 -0
- package/dist/acp/virtual-contract.d.ts.map +1 -1
- package/dist/acp/virtual-contract.js.map +1 -1
- package/dist/acp/virtual-harness.d.ts +1 -2
- package/dist/acp/virtual-harness.d.ts.map +1 -1
- package/dist/acp/virtual-harness.js +15 -23
- package/dist/acp/virtual-harness.js.map +1 -1
- package/dist/acp/virtual-live.d.ts +1 -0
- package/dist/acp/virtual-live.d.ts.map +1 -1
- package/dist/acp/virtual-live.js +6 -0
- package/dist/acp/virtual-live.js.map +1 -1
- package/dist/acp/virtual-titles.d.ts +17 -0
- package/dist/acp/virtual-titles.d.ts.map +1 -0
- package/dist/acp/virtual-titles.js +99 -0
- package/dist/acp/virtual-titles.js.map +1 -0
- package/dist/codex/app-server.d.ts +10 -0
- package/dist/codex/app-server.d.ts.map +1 -1
- package/dist/codex/app-server.js +24 -10
- package/dist/codex/app-server.js.map +1 -1
- package/dist/codex/domain.d.ts +3 -0
- package/dist/codex/domain.d.ts.map +1 -1
- package/dist/codex/domain.js +93 -17
- package/dist/codex/domain.js.map +1 -1
- package/dist/codex/peer-bridge.d.ts.map +1 -1
- package/dist/codex/peer-bridge.js +7 -5
- package/dist/codex/peer-bridge.js.map +1 -1
- package/dist/codex/virtual-harness.d.ts.map +1 -1
- package/dist/codex/virtual-harness.js +16 -3
- package/dist/codex/virtual-harness.js.map +1 -1
- package/dist/index.js +691 -146
- package/dist/index.js.map +4 -4
- package/dist/rpc-router.d.ts +4 -2
- package/dist/rpc-router.d.ts.map +1 -1
- package/dist/rpc-router.js +19 -2
- package/dist/rpc-router.js.map +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +4 -2
- package/dist/service.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace-bridge.d.ts.map +1 -1
- package/dist/workspace-bridge.js +26 -4
- package/dist/workspace-bridge.js.map +1 -1
- package/dist/workspace-upload-authority.d.ts +4 -0
- package/dist/workspace-upload-authority.d.ts.map +1 -0
- package/dist/workspace-upload-authority.js +25 -0
- package/dist/workspace-upload-authority.js.map +1 -0
- package/dist/workspace-upload.d.ts +27 -0
- package/dist/workspace-upload.d.ts.map +1 -0
- package/dist/workspace-upload.js +226 -0
- package/dist/workspace-upload.js.map +1 -0
- package/dsh-plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9574,6 +9574,10 @@ var controlFrameTypes = [
|
|
|
9574
9574
|
"error"
|
|
9575
9575
|
];
|
|
9576
9576
|
var rpcMethods = [
|
|
9577
|
+
"workspace.upload.open",
|
|
9578
|
+
"workspace.upload.chunk",
|
|
9579
|
+
"workspace.upload.commit",
|
|
9580
|
+
"workspace.upload.abort",
|
|
9577
9581
|
"harness.transport.describe",
|
|
9578
9582
|
"harness.api.call",
|
|
9579
9583
|
"harness.api.transfer.open",
|
|
@@ -11747,6 +11751,12 @@ var AcpLiveProjection = class {
|
|
|
11747
11751
|
const update = isRecord3(params.update) ? params.update : params;
|
|
11748
11752
|
const kind = string(update.sessionUpdate) ?? string(update.type);
|
|
11749
11753
|
const session = this.context.sessions.get(follow.sessionId);
|
|
11754
|
+
if (kind === "session_info_update") {
|
|
11755
|
+
if (session && params.sessionId === follow.acpSessionId && typeof update.title === "string") {
|
|
11756
|
+
this.context.applySessionTitle(session, update.title);
|
|
11757
|
+
}
|
|
11758
|
+
return;
|
|
11759
|
+
}
|
|
11750
11760
|
if (kind === "model_selection" && isRecord3(update.selected) && update.selected.provider === this.context.backend && typeof update.selected.model === "string") {
|
|
11751
11761
|
this.context.selectedModels.set(follow.sessionId, update.selected);
|
|
11752
11762
|
this.context.publishProjection(follow.sessionId, "modelSelection", this.context.modelSelectionProjection(follow.sessionId), this.context.nextProjectionSeq());
|
|
@@ -12111,6 +12121,86 @@ var AcpLiveProjection = class {
|
|
|
12111
12121
|
}
|
|
12112
12122
|
};
|
|
12113
12123
|
|
|
12124
|
+
// src/acp/virtual-titles.ts
|
|
12125
|
+
var OPENCODE_DEFAULT_TITLE = /^(?:New|Child) session - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
12126
|
+
var AcpSessionTitles = class {
|
|
12127
|
+
constructor(backend, client, sessions, publish) {
|
|
12128
|
+
this.backend = backend;
|
|
12129
|
+
this.client = client;
|
|
12130
|
+
this.sessions = sessions;
|
|
12131
|
+
this.publish = publish;
|
|
12132
|
+
}
|
|
12133
|
+
pending = /* @__PURE__ */ new Map();
|
|
12134
|
+
closed = false;
|
|
12135
|
+
nativeTitle(title) {
|
|
12136
|
+
const next = title?.trim();
|
|
12137
|
+
if (!next || next === "Untitled Session" || this.backend === "opencode" && OPENCODE_DEFAULT_TITLE.test(next)) return void 0;
|
|
12138
|
+
return next;
|
|
12139
|
+
}
|
|
12140
|
+
applyNative(session, title) {
|
|
12141
|
+
const next = this.nativeTitle(title);
|
|
12142
|
+
if (this.closed || session.titleOverridden || !next || this.sessions.get(session.sessionId) !== session) return;
|
|
12143
|
+
session.titleFromPrompt = false;
|
|
12144
|
+
this.pending.get(session.sessionId)?.abort();
|
|
12145
|
+
if (next === session.title) return;
|
|
12146
|
+
session.title = next;
|
|
12147
|
+
this.publish(session.sessionId, next);
|
|
12148
|
+
}
|
|
12149
|
+
applyPrompt(session, text) {
|
|
12150
|
+
if (this.backend !== "opencode" || this.closed || session.titleOverridden || session.titleFromPrompt || !session.blank && this.nativeTitle(session.title) !== void 0) return;
|
|
12151
|
+
const next = Array.from(text?.replace(/\s+/g, " ").trim() ?? "").slice(0, 60).join("");
|
|
12152
|
+
if (!next) return;
|
|
12153
|
+
session.titleFromPrompt = true;
|
|
12154
|
+
session.title = next;
|
|
12155
|
+
this.publish(session.sessionId, next);
|
|
12156
|
+
}
|
|
12157
|
+
async refresh(session) {
|
|
12158
|
+
if (this.backend === "cursor" || this.closed || session.titleOverridden || !this.client.listSessions) return;
|
|
12159
|
+
this.pending.get(session.sessionId)?.abort();
|
|
12160
|
+
const controller = new AbortController();
|
|
12161
|
+
const signal = controller.signal;
|
|
12162
|
+
this.pending.set(session.sessionId, controller);
|
|
12163
|
+
const deadline = this.backend === "opencode" ? setTimeout(() => controller.abort(), 2e4) : void 0;
|
|
12164
|
+
try {
|
|
12165
|
+
for (const delay of this.backend === "opencode" ? [0, 1e3, 3e3, 6e3, 9e3] : [0]) {
|
|
12166
|
+
if (delay) await waitForTitle(delay, signal);
|
|
12167
|
+
if (signal.aborted || this.closed || session.titleOverridden) return;
|
|
12168
|
+
const previous = session.title;
|
|
12169
|
+
try {
|
|
12170
|
+
const items = await this.client.listSessions(session.cwd, this.backend, 30, signal);
|
|
12171
|
+
if (signal.aborted || this.sessions.get(session.sessionId) !== session || session.title !== previous) return;
|
|
12172
|
+
const current = items.find((item) => item.conversationId === session.acpSessionId);
|
|
12173
|
+
if (current && this.nativeTitle(current.title) !== void 0) {
|
|
12174
|
+
this.applyNative(session, current.title);
|
|
12175
|
+
return;
|
|
12176
|
+
}
|
|
12177
|
+
} catch {
|
|
12178
|
+
}
|
|
12179
|
+
}
|
|
12180
|
+
} finally {
|
|
12181
|
+
if (deadline !== void 0) clearTimeout(deadline);
|
|
12182
|
+
if (this.pending.get(session.sessionId) === controller) this.pending.delete(session.sessionId);
|
|
12183
|
+
}
|
|
12184
|
+
}
|
|
12185
|
+
close() {
|
|
12186
|
+
this.closed = true;
|
|
12187
|
+
for (const controller of this.pending.values()) controller.abort();
|
|
12188
|
+
this.pending.clear();
|
|
12189
|
+
}
|
|
12190
|
+
};
|
|
12191
|
+
function waitForTitle(delay, signal) {
|
|
12192
|
+
return new Promise((resolve6) => {
|
|
12193
|
+
const finish = () => {
|
|
12194
|
+
clearTimeout(timer);
|
|
12195
|
+
signal.removeEventListener("abort", finish);
|
|
12196
|
+
resolve6();
|
|
12197
|
+
};
|
|
12198
|
+
const timer = setTimeout(finish, delay);
|
|
12199
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
12200
|
+
if (signal.aborted) finish();
|
|
12201
|
+
});
|
|
12202
|
+
}
|
|
12203
|
+
|
|
12114
12204
|
// src/acp/virtual-harness.ts
|
|
12115
12205
|
var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
12116
12206
|
constructor(client, host, backend = "cursor", hostCarrier) {
|
|
@@ -12142,7 +12232,8 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12142
12232
|
},
|
|
12143
12233
|
publishProjection: (...args) => owner.events.publishProjection(...args),
|
|
12144
12234
|
nextProjectionSeq: (...args) => owner.nextProjectionSeq(...args),
|
|
12145
|
-
refreshSessionTitle: (...args) => owner.
|
|
12235
|
+
refreshSessionTitle: (...args) => owner.titles.refresh(...args),
|
|
12236
|
+
applySessionTitle: (...args) => owner.titles.applyNative(...args),
|
|
12146
12237
|
publishWorkspaceBaseline: () => owner.publishWorkspaceBaseline(),
|
|
12147
12238
|
get pendingApprovals() {
|
|
12148
12239
|
return owner.pendingApprovals;
|
|
@@ -12190,6 +12281,12 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12190
12281
|
},
|
|
12191
12282
|
nextProjectionSeq: (...args) => owner.nextProjectionSeq(...args)
|
|
12192
12283
|
});
|
|
12284
|
+
this.titles = new AcpSessionTitles(
|
|
12285
|
+
backend,
|
|
12286
|
+
client,
|
|
12287
|
+
this.sessions,
|
|
12288
|
+
(sessionId, title) => this.events.publishProjection(sessionId, "title", title)
|
|
12289
|
+
);
|
|
12193
12290
|
this.api = this.events.createApiProxy();
|
|
12194
12291
|
this.unsubscribeArchived = client.onSessionArchived?.((data2) => {
|
|
12195
12292
|
if (this.closed || data2.backend !== this.backend) return;
|
|
@@ -12198,6 +12295,7 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12198
12295
|
}
|
|
12199
12296
|
live;
|
|
12200
12297
|
events;
|
|
12298
|
+
titles;
|
|
12201
12299
|
api;
|
|
12202
12300
|
workspaceById = /* @__PURE__ */ new Map();
|
|
12203
12301
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -12408,6 +12506,7 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12408
12506
|
async close() {
|
|
12409
12507
|
if (this.closed) return;
|
|
12410
12508
|
this.closed = true;
|
|
12509
|
+
this.titles.close();
|
|
12411
12510
|
this.unsubscribeArchived?.();
|
|
12412
12511
|
for (const stream of this.workspaceStreams) stream.close();
|
|
12413
12512
|
for (const stream of this.controlStreams) stream.close();
|
|
@@ -12563,6 +12662,7 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12563
12662
|
source: requestId ? { kind: "user", rpcId: requestId } : { kind: "user" }
|
|
12564
12663
|
});
|
|
12565
12664
|
}
|
|
12665
|
+
this.titles.applyPrompt(session, text);
|
|
12566
12666
|
session.blank = false;
|
|
12567
12667
|
session.running = true;
|
|
12568
12668
|
session.updatedAt = Date.now();
|
|
@@ -12639,6 +12739,8 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12639
12739
|
}
|
|
12640
12740
|
if (events.length > 0) {
|
|
12641
12741
|
session.events = events;
|
|
12742
|
+
const firstUser = session.events.find((entry) => entry.event.type === "user/message");
|
|
12743
|
+
this.titles.applyPrompt(session, extractPromptText(array(record(firstUser?.event.data).content)));
|
|
12642
12744
|
session.blank = false;
|
|
12643
12745
|
for (const event of session.events) this.cacheImageAttachments(session.sessionId, array(record(event.event.data).content ?? record(record(event.event.data).message).content));
|
|
12644
12746
|
}
|
|
@@ -12729,11 +12831,12 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12729
12831
|
return success(await this.client.listDirectory(path, signal));
|
|
12730
12832
|
}
|
|
12731
12833
|
registerSession(acpSessionId, cwd2, title, createdAt = Date.now(), updatedAt = Date.now(), blank = true) {
|
|
12834
|
+
const nativeTitle = this.titles.nativeTitle(title);
|
|
12732
12835
|
const session = {
|
|
12733
12836
|
sessionId: `${sessionPrefix(this.backend)}${acpSessionId}`,
|
|
12734
12837
|
acpSessionId,
|
|
12735
12838
|
cwd: cwd2,
|
|
12736
|
-
...
|
|
12839
|
+
...nativeTitle === void 0 ? {} : { title: nativeTitle },
|
|
12737
12840
|
blank,
|
|
12738
12841
|
running: false,
|
|
12739
12842
|
createdAt,
|
|
@@ -12743,23 +12846,9 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12743
12846
|
this.sessions.set(session.sessionId, session);
|
|
12744
12847
|
return session;
|
|
12745
12848
|
}
|
|
12746
|
-
applyDiscoveredTitle(session, title) {
|
|
12747
|
-
const next = title.trim();
|
|
12748
|
-
if (this.closed || session.titleOverridden || !next || next === "Untitled Session" || next === session.title) return;
|
|
12749
|
-
session.title = next;
|
|
12750
|
-
this.events.publishProjection(session.sessionId, "title", next);
|
|
12751
|
-
}
|
|
12752
|
-
async refreshSessionTitle(session) {
|
|
12753
|
-
if (this.backend === "cursor" || this.closed || session.titleOverridden || this.client.listSessions === void 0) return;
|
|
12754
|
-
try {
|
|
12755
|
-
const items = await this.client.listSessions(session.cwd, this.backend, 30);
|
|
12756
|
-
const current = items.find((item) => item.conversationId === session.acpSessionId);
|
|
12757
|
-
if (current !== void 0 && this.sessions.get(session.sessionId) === session) this.applyDiscoveredTitle(session, current.title);
|
|
12758
|
-
} catch {
|
|
12759
|
-
}
|
|
12760
|
-
}
|
|
12761
12849
|
async discoverAndAttachSessions(workspace) {
|
|
12762
12850
|
if (this.backend === "cursor") return;
|
|
12851
|
+
const previousTitles = new Map([...this.sessions].map(([id5, session]) => [id5, session.title]));
|
|
12763
12852
|
try {
|
|
12764
12853
|
let discovered = [];
|
|
12765
12854
|
if (this.client.listSessions !== void 0) {
|
|
@@ -12777,7 +12866,9 @@ var AcpVirtualHarness = class _AcpVirtualHarness {
|
|
|
12777
12866
|
const existing = this.sessions.get(sessionId);
|
|
12778
12867
|
if (existing !== void 0) {
|
|
12779
12868
|
if (Number.isFinite(item.updatedAt)) existing.updatedAt = Math.max(existing.updatedAt, item.updatedAt);
|
|
12780
|
-
|
|
12869
|
+
if (!previousTitles.has(sessionId) || existing.title === previousTitles.get(sessionId)) {
|
|
12870
|
+
this.titles.applyNative(existing, item.title);
|
|
12871
|
+
}
|
|
12781
12872
|
}
|
|
12782
12873
|
if (!this.archivedSessionIds.has(sessionId) && !workspace.sessionIds.includes(sessionId)) {
|
|
12783
12874
|
workspace.sessionIds.push(sessionId);
|
|
@@ -14103,11 +14194,11 @@ function isLanCandidatePair(localType, remoteType, localScope, remoteScope) {
|
|
|
14103
14194
|
return true;
|
|
14104
14195
|
return localType === "host" && isLocalNetworkScope(localScope) && remoteType === "prflx" && remoteScope === "unknown" || remoteType === "host" && isLocalNetworkScope(remoteScope) && localType === "prflx" && localScope === "unknown";
|
|
14105
14196
|
}
|
|
14106
|
-
function isLocalNetworkScope(
|
|
14107
|
-
return
|
|
14197
|
+
function isLocalNetworkScope(scope2) {
|
|
14198
|
+
return scope2 === "private" || scope2 === "link-local" || scope2 === "loopback";
|
|
14108
14199
|
}
|
|
14109
|
-
function isExplicitlyNonLanScope(
|
|
14110
|
-
return
|
|
14200
|
+
function isExplicitlyNonLanScope(scope2) {
|
|
14201
|
+
return scope2 === "public" || scope2 === "cgnat" || scope2 === "reserved";
|
|
14111
14202
|
}
|
|
14112
14203
|
function increment(map, key) {
|
|
14113
14204
|
map[key] = (map[key] ?? 0) + 1;
|
|
@@ -25962,8 +26053,15 @@ var CodexVirtualHarness = class _CodexVirtualHarness {
|
|
|
25962
26053
|
}, signal));
|
|
25963
26054
|
} catch (error) {
|
|
25964
26055
|
if (!isLegacySessionHistoryUnsupported(error)) throw error;
|
|
25965
|
-
|
|
25966
|
-
|
|
26056
|
+
let thread;
|
|
26057
|
+
try {
|
|
26058
|
+
const result = record4(await this.client.request("thread/read", { threadId, includeTurns: true }, signal));
|
|
26059
|
+
thread = record4(result.thread);
|
|
26060
|
+
} catch (historyError) {
|
|
26061
|
+
const code = historyError instanceof Error ? errorCode(historyError) : void 0;
|
|
26062
|
+
if (!this.blankThreads.has(threadId) || code !== "CODEX_UPSTREAM_ERROR") throw historyError;
|
|
26063
|
+
thread = { id: threadId, turns: [] };
|
|
26064
|
+
}
|
|
25967
26065
|
if (string2(thread.id) !== threadId) throw new Error("CodeX returned an invalid Thread history.");
|
|
25968
26066
|
value = paginateCodexNativeHistory(
|
|
25969
26067
|
projectCodexNativeHistory(thread, `codex:${threadId}`, this.sessionGeneration),
|
|
@@ -26949,11 +27047,11 @@ function routeStreamEvent2(event, streamId, queue) {
|
|
|
26949
27047
|
const data2 = event.data;
|
|
26950
27048
|
if (data2.streamId !== streamId) return;
|
|
26951
27049
|
if (data2.reason === "failed") {
|
|
26952
|
-
const
|
|
27050
|
+
const failure4 = data2.failure;
|
|
26953
27051
|
queue.fail(remoteFailure({
|
|
26954
|
-
code: typeof
|
|
26955
|
-
message: typeof
|
|
26956
|
-
details: isRecord9(
|
|
27052
|
+
code: typeof failure4?.code === "string" ? failure4.code : "internal",
|
|
27053
|
+
message: typeof failure4?.message === "string" ? failure4.message : "The remote Harness stream failed.",
|
|
27054
|
+
details: isRecord9(failure4?.details) ? failure4.details : {}
|
|
26957
27055
|
}));
|
|
26958
27056
|
} else {
|
|
26959
27057
|
queue.close();
|
|
@@ -26970,11 +27068,11 @@ function parseRpcResult(value) {
|
|
|
26970
27068
|
error: { code: value.error.code, message: value.error.message, details: value.error.details }
|
|
26971
27069
|
};
|
|
26972
27070
|
}
|
|
26973
|
-
function remoteFailure(
|
|
26974
|
-
return Object.assign(new Error(
|
|
27071
|
+
function remoteFailure(failure4) {
|
|
27072
|
+
return Object.assign(new Error(failure4.message), {
|
|
26975
27073
|
isDSHRemoteError: true,
|
|
26976
|
-
code:
|
|
26977
|
-
details:
|
|
27074
|
+
code: failure4.code,
|
|
27075
|
+
details: failure4.details
|
|
26978
27076
|
});
|
|
26979
27077
|
}
|
|
26980
27078
|
function bytesToBase643(bytes) {
|
|
@@ -27306,7 +27404,7 @@ function normalizeServerUrl(value) {
|
|
|
27306
27404
|
}
|
|
27307
27405
|
|
|
27308
27406
|
// src/version.ts
|
|
27309
|
-
var PLUGIN_VERSION = "0.5.
|
|
27407
|
+
var PLUGIN_VERSION = "0.5.13";
|
|
27310
27408
|
|
|
27311
27409
|
// src/server-api.ts
|
|
27312
27410
|
var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
|
|
@@ -28072,7 +28170,7 @@ var TypertGatewaySwitch = class {
|
|
|
28072
28170
|
return { ok: false, error: this.failure(error) };
|
|
28073
28171
|
}
|
|
28074
28172
|
}) : (endpoint, payload, signal) => this.localDispatch(endpoint, payload, signal, peer);
|
|
28075
|
-
const
|
|
28173
|
+
const open3 = this.localOpen === void 0 ? (async (endpoint, payload, signal) => {
|
|
28076
28174
|
if (this.localStream === void 0) throw new Error("The local Harness Gateway does not support Remote streams.");
|
|
28077
28175
|
return this.localStream({ ...requestFromCarrier(endpoint, payload, signal), ...peer === void 0 ? {} : { peer } });
|
|
28078
28176
|
}) : (endpoint, payload, signal, uplink) => this.localOpen(endpoint, payload, signal, uplink, peer);
|
|
@@ -28081,7 +28179,7 @@ var TypertGatewaySwitch = class {
|
|
|
28081
28179
|
peer,
|
|
28082
28180
|
...this.localStream === void 0 ? {} : { stream: (request) => this.localStream({ ...request, ...peer === void 0 ? {} : { peer } }) },
|
|
28083
28181
|
dispatch,
|
|
28084
|
-
open,
|
|
28182
|
+
open: open3,
|
|
28085
28183
|
failure: (error) => this.failure(error),
|
|
28086
28184
|
supportsCarrier: this.localDispatch !== void 0 && this.localOpen !== void 0
|
|
28087
28185
|
};
|
|
@@ -28108,12 +28206,12 @@ var TypertGatewaySwitch = class {
|
|
|
28108
28206
|
};
|
|
28109
28207
|
}
|
|
28110
28208
|
if (this.originalOpen !== void 0) {
|
|
28111
|
-
const
|
|
28112
|
-
const rc1 = usesRc1Arity(
|
|
28209
|
+
const open3 = this.originalOpen;
|
|
28210
|
+
const rc1 = usesRc1Arity(open3);
|
|
28113
28211
|
this.runtime.openWireStream = (...callArgs) => {
|
|
28114
28212
|
const endpoint = callArgs[0];
|
|
28115
28213
|
if (this.remoteTarget === void 0 || isLocalOnlyEndpoint(endpoint)) {
|
|
28116
|
-
return rc1 ? Reflect.apply(
|
|
28214
|
+
return rc1 ? Reflect.apply(open3, this.runtime, callArgs) : open3.call(this.runtime, endpoint, callArgs[1], callArgs[2]);
|
|
28117
28215
|
}
|
|
28118
28216
|
const signal = rc1 ? callArgs[4] : callArgs[2];
|
|
28119
28217
|
return this.openRemote(endpoint, callArgs[1], signal ?? new AbortController().signal, rc1 ? callArgs[2] : void 0);
|
|
@@ -28197,8 +28295,8 @@ function workspaceConnectionResult(endpoint, result) {
|
|
|
28197
28295
|
]
|
|
28198
28296
|
};
|
|
28199
28297
|
}
|
|
28200
|
-
function usesRc1Arity(
|
|
28201
|
-
return
|
|
28298
|
+
function usesRc1Arity(open3) {
|
|
28299
|
+
return open3.length >= 5;
|
|
28202
28300
|
}
|
|
28203
28301
|
function endedUplink() {
|
|
28204
28302
|
return {
|
|
@@ -28214,9 +28312,9 @@ function linkControl(signal) {
|
|
|
28214
28312
|
return control;
|
|
28215
28313
|
}
|
|
28216
28314
|
function createLocalOpen(gateway, runtime) {
|
|
28217
|
-
const
|
|
28218
|
-
if (
|
|
28219
|
-
return usesRc1Arity(
|
|
28315
|
+
const open3 = runtime.openWireStream;
|
|
28316
|
+
if (open3 !== void 0) {
|
|
28317
|
+
return usesRc1Arity(open3) ? (endpoint, payload, signal, uplink, peer) => open3.call(runtime, endpoint, payload, uplink ?? endedUplink(), peer, signal, linkControl(signal)) : (endpoint, payload, signal) => open3.call(runtime, endpoint, payload, signal);
|
|
28220
28318
|
}
|
|
28221
28319
|
const wire = gateway.wireStream;
|
|
28222
28320
|
if (wire === void 0) return void 0;
|
|
@@ -28359,10 +28457,10 @@ function isUsableExternalNode(candidate, requireFrom) {
|
|
|
28359
28457
|
}
|
|
28360
28458
|
function isExecutableFile(candidate) {
|
|
28361
28459
|
try {
|
|
28362
|
-
const
|
|
28363
|
-
if (!
|
|
28460
|
+
const stat9 = statSync(candidate);
|
|
28461
|
+
if (!stat9.isFile()) return false;
|
|
28364
28462
|
if (process.platform === "win32") return true;
|
|
28365
|
-
return (
|
|
28463
|
+
return (stat9.mode & 73) !== 0;
|
|
28366
28464
|
} catch {
|
|
28367
28465
|
return false;
|
|
28368
28466
|
}
|
|
@@ -30179,8 +30277,8 @@ var IdentityStore = class {
|
|
|
30179
30277
|
};
|
|
30180
30278
|
function serverStorageDirectory(root, serverUrl, role) {
|
|
30181
30279
|
const origin = new URL(serverUrl).origin;
|
|
30182
|
-
const
|
|
30183
|
-
return join4(root, "servers",
|
|
30280
|
+
const scope2 = createHash("sha256").update(origin).digest("hex").slice(0, 24);
|
|
30281
|
+
return join4(root, "servers", scope2, role);
|
|
30184
30282
|
}
|
|
30185
30283
|
function fingerprint(publicKey) {
|
|
30186
30284
|
const compact = createHash("sha256").update(fromBase64Url2(publicKey)).digest("hex").slice(0, 12).toUpperCase();
|
|
@@ -30563,7 +30661,7 @@ var CodexAppServerClient = class {
|
|
|
30563
30661
|
const pending = this.takePending(value.id);
|
|
30564
30662
|
if (pending === void 0) return;
|
|
30565
30663
|
if ("error" in value && value.error !== void 0) {
|
|
30566
|
-
pending.reject(
|
|
30664
|
+
pending.reject(safeUpstreamError(value.error));
|
|
30567
30665
|
} else {
|
|
30568
30666
|
pending.resolve(value.result);
|
|
30569
30667
|
}
|
|
@@ -30599,12 +30697,26 @@ var CodexAppServerClient = class {
|
|
|
30599
30697
|
for (const id5 of [...this.pending.keys()]) this.takePending(id5)?.reject(error);
|
|
30600
30698
|
}
|
|
30601
30699
|
};
|
|
30700
|
+
var CODEX_THREAD_UNMATERIALIZED = "CODEX_THREAD_UNMATERIALIZED";
|
|
30701
|
+
var CODEX_UNMATERIALIZED_THREAD_MESSAGE = "Codex thread has no turns yet.";
|
|
30602
30702
|
function safeUpstreamError(value) {
|
|
30603
|
-
if (!isRecord12(value) || typeof value.message !== "string")
|
|
30703
|
+
if (!isRecord12(value) || typeof value.message !== "string") {
|
|
30704
|
+
return new CodexAppServerError("CODEX_UPSTREAM_ERROR", "Codex App Server rejected the request.");
|
|
30705
|
+
}
|
|
30604
30706
|
const message = value.message.toLowerCase();
|
|
30605
|
-
if (message.includes("active writer"))
|
|
30606
|
-
|
|
30607
|
-
|
|
30707
|
+
if (message.includes("active writer")) {
|
|
30708
|
+
return new CodexAppServerError("CODEX_UPSTREAM_ERROR", "Codex thread already has an active writer.");
|
|
30709
|
+
}
|
|
30710
|
+
if (message.includes("not materialized yet") || message.includes("before first user message")) {
|
|
30711
|
+
return new CodexAppServerError(CODEX_THREAD_UNMATERIALIZED, CODEX_UNMATERIALIZED_THREAD_MESSAGE);
|
|
30712
|
+
}
|
|
30713
|
+
if (message.includes("no rollout found")) {
|
|
30714
|
+
return new CodexAppServerError("CODEX_UPSTREAM_ERROR", "Codex thread state is not persisted yet.");
|
|
30715
|
+
}
|
|
30716
|
+
return new CodexAppServerError(
|
|
30717
|
+
"CODEX_UPSTREAM_ERROR",
|
|
30718
|
+
message.includes("not initialized") ? "Codex App Server is not initialized." : "Codex App Server rejected the request."
|
|
30719
|
+
);
|
|
30608
30720
|
}
|
|
30609
30721
|
function isRecord12(value) {
|
|
30610
30722
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -30788,6 +30900,7 @@ var CodexPeerBridge = class {
|
|
|
30788
30900
|
method: "thread/read",
|
|
30789
30901
|
params: { threadId: params.threadId, includeTurns: false }
|
|
30790
30902
|
});
|
|
30903
|
+
this.requireOpen();
|
|
30791
30904
|
this.streams.set(params.streamId, params.threadId);
|
|
30792
30905
|
return { opened: true, streamId: params.streamId, threadId: params.threadId };
|
|
30793
30906
|
}
|
|
@@ -30868,6 +30981,7 @@ var CodexPeerBridge = class {
|
|
|
30868
30981
|
});
|
|
30869
30982
|
throw error;
|
|
30870
30983
|
}
|
|
30984
|
+
this.requireOpen();
|
|
30871
30985
|
const responseBytes = new TextEncoder().encode(JSON.stringify(response));
|
|
30872
30986
|
if (responseBytes.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES) return { kind: "inline", response };
|
|
30873
30987
|
if (responseBytes.byteLength > MAX_CODEX_APP_TRANSFER_BYTES) {
|
|
@@ -30950,11 +31064,11 @@ var CodexPeerBridge = class {
|
|
|
30950
31064
|
this.streams.clear();
|
|
30951
31065
|
this.incomingTransfers.clear();
|
|
30952
31066
|
this.outgoingTransfers.clear();
|
|
30953
|
-
|
|
31067
|
+
const detach = this.domain.detachPeer(this.context.connectionId);
|
|
31068
|
+
await Promise.all([detach, ...streamIds.map((streamId) => this.publish("codex.app.stream.closed", {
|
|
30954
31069
|
streamId,
|
|
30955
31070
|
reason: "peer-disconnected"
|
|
30956
|
-
}).catch(() => void 0)));
|
|
30957
|
-
await this.domain.detachPeer(this.context.connectionId);
|
|
31071
|
+
}).catch(() => void 0))]);
|
|
30958
31072
|
}
|
|
30959
31073
|
pruneTransfers() {
|
|
30960
31074
|
const staleBefore = Date.now() - TRANSFER_IDLE_MS;
|
|
@@ -31066,7 +31180,9 @@ var CodexRemoteDomain = class {
|
|
|
31066
31180
|
async resolveThreadWorkspace(connectionId, threadId) {
|
|
31067
31181
|
if (!this.peers.has(connectionId)) throw new RpcError("CODEX_THREAD_UNAVAILABLE", "The CodeX thread is not available on this connection.");
|
|
31068
31182
|
try {
|
|
31183
|
+
const assertCurrent = this.captureCallScope(connectionId);
|
|
31069
31184
|
const known = await this.readKnownThread(threadId);
|
|
31185
|
+
assertCurrent();
|
|
31070
31186
|
return known.cwd;
|
|
31071
31187
|
} catch (error) {
|
|
31072
31188
|
if (error instanceof RpcError && error.code === "CODEX_THREAD_NOT_ALLOWED") {
|
|
@@ -31084,7 +31200,25 @@ var CodexRemoteDomain = class {
|
|
|
31084
31200
|
async call(connectionId, input3) {
|
|
31085
31201
|
const envelope = parseCallEnvelope(input3);
|
|
31086
31202
|
const call = parseCodexCall(envelope.method, envelope.params);
|
|
31087
|
-
this.
|
|
31203
|
+
const assertCurrent = this.captureCallScope(connectionId);
|
|
31204
|
+
const result = await this.callInternal(connectionId, call, assertCurrent);
|
|
31205
|
+
assertCurrent();
|
|
31206
|
+
return result;
|
|
31207
|
+
}
|
|
31208
|
+
captureCallScope(connectionId) {
|
|
31209
|
+
const appServer = this.requireAppServer();
|
|
31210
|
+
const peer = this.peers.get(connectionId);
|
|
31211
|
+
return () => {
|
|
31212
|
+
if (this.closed || this.appServer !== appServer || !this.isAvailable()) {
|
|
31213
|
+
throw new RpcError("CODEX_UNAVAILABLE", "The Codex App Server changed while handling the request.");
|
|
31214
|
+
}
|
|
31215
|
+
if (peer !== void 0 && this.peers.get(connectionId) !== peer) {
|
|
31216
|
+
throw new RpcError("CODEX_CONNECTION_CLOSED", "The Codex connection is closed.");
|
|
31217
|
+
}
|
|
31218
|
+
};
|
|
31219
|
+
}
|
|
31220
|
+
async callInternal(connectionId, call, assertCurrent) {
|
|
31221
|
+
assertCurrent();
|
|
31088
31222
|
if (call.method === "account/read") {
|
|
31089
31223
|
return sanitizeAccount(await this.callUpstream(call.method, call.params));
|
|
31090
31224
|
}
|
|
@@ -31094,6 +31228,7 @@ var CodexRemoteDomain = class {
|
|
|
31094
31228
|
if (call.method === "project/create") {
|
|
31095
31229
|
const roots = call.params.roots;
|
|
31096
31230
|
const path = await this.requireNewCodexProjectPath(roots[0].path);
|
|
31231
|
+
assertCurrent();
|
|
31097
31232
|
return sanitizeProjectCreate(await this.callUpstream(call.method, {
|
|
31098
31233
|
name: call.params.name,
|
|
31099
31234
|
roots: [{ path }],
|
|
@@ -31109,6 +31244,7 @@ var CodexRemoteDomain = class {
|
|
|
31109
31244
|
}
|
|
31110
31245
|
if (call.method === "thread/start") {
|
|
31111
31246
|
const cwd2 = await this.requireCodexWorkspacePath(call.params.cwd);
|
|
31247
|
+
assertCurrent();
|
|
31112
31248
|
const permission = codexPermission(call.params, "workspace-write");
|
|
31113
31249
|
const result = await this.callUpstream(call.method, {
|
|
31114
31250
|
...permission.params,
|
|
@@ -31116,6 +31252,7 @@ var CodexRemoteDomain = class {
|
|
|
31116
31252
|
...codexThreadPermissionParams(permission),
|
|
31117
31253
|
serviceName: "deepseek_harness_remote"
|
|
31118
31254
|
});
|
|
31255
|
+
assertCurrent();
|
|
31119
31256
|
if (extractThread(result)?.id === void 0) {
|
|
31120
31257
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid thread.");
|
|
31121
31258
|
}
|
|
@@ -31154,12 +31291,13 @@ var CodexRemoteDomain = class {
|
|
|
31154
31291
|
return this.listCodexDirectory(call.params.path);
|
|
31155
31292
|
}
|
|
31156
31293
|
const allowedThread = threadId === void 0 ? void 0 : await this.readKnownThread(threadId);
|
|
31294
|
+
assertCurrent();
|
|
31157
31295
|
if (call.method === "thread/resume" && call.params.permissionPreset !== void 0) {
|
|
31158
31296
|
const owner = this.turnOwners.get(threadId);
|
|
31159
31297
|
if (owner !== void 0 && owner.connectionId !== connectionId) {
|
|
31160
31298
|
throw new RpcError("CODEX_THREAD_BUSY", "Another Remote client is writing to this CodeX thread.");
|
|
31161
31299
|
}
|
|
31162
|
-
return this.changeThreadPermission(threadId, call.params, allowedThread);
|
|
31300
|
+
return this.changeThreadPermission(threadId, call.params, allowedThread, assertCurrent);
|
|
31163
31301
|
}
|
|
31164
31302
|
if (call.method === "thread/read") {
|
|
31165
31303
|
return this.callUpstream(call.method, call.params);
|
|
@@ -31172,6 +31310,7 @@ var CodexRemoteDomain = class {
|
|
|
31172
31310
|
}
|
|
31173
31311
|
let claimed = false;
|
|
31174
31312
|
let previousOwner;
|
|
31313
|
+
let claimedOwner;
|
|
31175
31314
|
if (isThreadMutation(call.method) && threadId !== void 0) {
|
|
31176
31315
|
const claim = this.claimTurn(
|
|
31177
31316
|
threadId,
|
|
@@ -31181,6 +31320,7 @@ var CodexRemoteDomain = class {
|
|
|
31181
31320
|
);
|
|
31182
31321
|
claimed = claim.claimed;
|
|
31183
31322
|
previousOwner = claim.previous;
|
|
31323
|
+
claimedOwner = this.turnOwners.get(threadId);
|
|
31184
31324
|
}
|
|
31185
31325
|
try {
|
|
31186
31326
|
const permission = codexPermission(call.params);
|
|
@@ -31202,13 +31342,16 @@ var CodexRemoteDomain = class {
|
|
|
31202
31342
|
try {
|
|
31203
31343
|
result = await this.callUpstream(call.method, upstreamParams);
|
|
31204
31344
|
} catch (error) {
|
|
31205
|
-
|
|
31345
|
+
assertCurrent();
|
|
31346
|
+
if (call.method === "thread/resume" && allowedThread !== void 0 && error instanceof RpcError && (error.code === "CODEX_UPSTREAM_ERROR" || error.code === CODEX_THREAD_UNMATERIALIZED) && call.params.permissionPreset === void 0) {
|
|
31206
31347
|
return { thread: allowedThread.thread };
|
|
31207
31348
|
}
|
|
31208
31349
|
throw error;
|
|
31209
31350
|
}
|
|
31351
|
+
assertCurrent();
|
|
31210
31352
|
if (call.method === "thread/resume" || call.method === "thread/fork" || call.method === "thread/unarchive") {
|
|
31211
31353
|
await this.assertResultThreadAllowed(result);
|
|
31354
|
+
assertCurrent();
|
|
31212
31355
|
}
|
|
31213
31356
|
if (call.method === "thread/resume" || call.method === "thread/fork") {
|
|
31214
31357
|
const resultId = extractThread(result)?.id;
|
|
@@ -31225,7 +31368,7 @@ var CodexRemoteDomain = class {
|
|
|
31225
31368
|
}
|
|
31226
31369
|
return result;
|
|
31227
31370
|
} catch (error) {
|
|
31228
|
-
if (claimed && threadId !== void 0) {
|
|
31371
|
+
if (claimed && threadId !== void 0 && this.turnOwners.get(threadId) === claimedOwner) {
|
|
31229
31372
|
if (previousOwner === void 0) this.turnOwners.delete(threadId);
|
|
31230
31373
|
else this.turnOwners.set(threadId, previousOwner);
|
|
31231
31374
|
}
|
|
@@ -31319,6 +31462,7 @@ var CodexRemoteDomain = class {
|
|
|
31319
31462
|
const appServer = this.createAppServer(binary, this.logger);
|
|
31320
31463
|
this.appServer = appServer;
|
|
31321
31464
|
this.unsubscribeInbound = appServer.onInbound((message) => {
|
|
31465
|
+
if (this.appServer !== appServer) return;
|
|
31322
31466
|
void this.handleInbound(message).catch((error) => {
|
|
31323
31467
|
this.logger.warn("Codex inbound handling failed", { code: errorCode2(error) });
|
|
31324
31468
|
});
|
|
@@ -31434,7 +31578,7 @@ var CodexRemoteDomain = class {
|
|
|
31434
31578
|
if (preset === void 0) this.permissionPresets.delete(threadId);
|
|
31435
31579
|
else this.permissionPresets.set(threadId, preset);
|
|
31436
31580
|
}
|
|
31437
|
-
async changeThreadPermission(threadId, params, allowed) {
|
|
31581
|
+
async changeThreadPermission(threadId, params, allowed, assertCurrent) {
|
|
31438
31582
|
const permission = codexPermission(params);
|
|
31439
31583
|
const settings = {
|
|
31440
31584
|
...codexTurnPermissionParams(permission, allowed.cwd),
|
|
@@ -31443,8 +31587,10 @@ var CodexRemoteDomain = class {
|
|
|
31443
31587
|
let result;
|
|
31444
31588
|
try {
|
|
31445
31589
|
await this.callUpstream("thread/settings/update", { threadId, ...settings });
|
|
31590
|
+
assertCurrent();
|
|
31446
31591
|
result = { thread: allowed.thread, ...settings, sandbox: settings.sandboxPolicy };
|
|
31447
31592
|
} catch (error) {
|
|
31593
|
+
assertCurrent();
|
|
31448
31594
|
if (isUnpersistedThreadError(error)) {
|
|
31449
31595
|
return { thread: allowed.thread, ...settings, sandbox: settings.sandboxPolicy };
|
|
31450
31596
|
}
|
|
@@ -31455,15 +31601,19 @@ var CodexRemoteDomain = class {
|
|
|
31455
31601
|
...codexThreadPermissionParams(permission),
|
|
31456
31602
|
excludeTurns: true
|
|
31457
31603
|
});
|
|
31604
|
+
assertCurrent();
|
|
31458
31605
|
} catch (resumeError) {
|
|
31606
|
+
assertCurrent();
|
|
31459
31607
|
if (isUnpersistedThreadError(resumeError)) {
|
|
31460
31608
|
return { thread: allowed.thread, ...settings, sandbox: settings.sandboxPolicy };
|
|
31461
31609
|
}
|
|
31462
31610
|
throw resumeError;
|
|
31463
31611
|
}
|
|
31464
31612
|
await this.assertResultThreadAllowed(result);
|
|
31613
|
+
assertCurrent();
|
|
31465
31614
|
if (codexPermissionPresetFromResponse(result) !== params.permissionPreset) {
|
|
31466
31615
|
await this.callUpstream("thread/settings/update", { threadId, ...settings });
|
|
31616
|
+
assertCurrent();
|
|
31467
31617
|
result = { ...isRecord14(result) ? result : {}, ...settings, sandbox: settings.sandboxPolicy };
|
|
31468
31618
|
}
|
|
31469
31619
|
}
|
|
@@ -31478,6 +31628,7 @@ var CodexRemoteDomain = class {
|
|
|
31478
31628
|
async handleServerRequest(message) {
|
|
31479
31629
|
const appServer = this.requireAppServer();
|
|
31480
31630
|
await this.expireApprovals();
|
|
31631
|
+
if (this.appServer !== appServer || !this.isAvailable()) return;
|
|
31481
31632
|
if (message.method !== "item/commandExecution/requestApproval" && message.method !== "item/fileChange/requestApproval") {
|
|
31482
31633
|
await appServer.respondError(message.id, -32601, "This Remote client does not support the server request.");
|
|
31483
31634
|
return;
|
|
@@ -31532,13 +31683,13 @@ var CodexRemoteDomain = class {
|
|
|
31532
31683
|
async readThreadForHistory(connectionId, threadId) {
|
|
31533
31684
|
const { thread: metadata } = await this.readKnownThread(threadId);
|
|
31534
31685
|
try {
|
|
31535
|
-
return { ...metadata, turns: await this.readThreadTurns(threadId, "full") };
|
|
31686
|
+
return { ...metadata, turns: await this.readThreadTurns(connectionId, threadId, "full") };
|
|
31536
31687
|
} catch (fullError) {
|
|
31537
31688
|
if (!isHistoryFormatFallback(fullError)) throw fullError;
|
|
31538
31689
|
this.logHistoryFallback(connectionId, "turns-full", fullError);
|
|
31539
31690
|
}
|
|
31540
31691
|
try {
|
|
31541
|
-
return { ...metadata, turns: await this.readThreadTurns(threadId, "summary") };
|
|
31692
|
+
return { ...metadata, turns: await this.readThreadTurns(connectionId, threadId, "summary") };
|
|
31542
31693
|
} catch (summaryError) {
|
|
31543
31694
|
if (!isHistoryFormatFallback(summaryError)) throw summaryError;
|
|
31544
31695
|
this.logHistoryFallback(connectionId, "turns-summary", summaryError);
|
|
@@ -31554,22 +31705,35 @@ var CodexRemoteDomain = class {
|
|
|
31554
31705
|
}
|
|
31555
31706
|
return legacyThread;
|
|
31556
31707
|
} catch (legacyError) {
|
|
31708
|
+
if (isUnmaterializedThreadError(legacyError)) {
|
|
31709
|
+
this.logEmptyUnmaterializedHistory(connectionId, "thread-read-full");
|
|
31710
|
+
return { ...metadata, turns: [] };
|
|
31711
|
+
}
|
|
31557
31712
|
if (!isHistoryReadRecoverable(legacyError)) throw legacyError;
|
|
31558
31713
|
this.logHistoryFallback(connectionId, "thread-read-full", legacyError);
|
|
31559
31714
|
throw legacyError;
|
|
31560
31715
|
}
|
|
31561
31716
|
}
|
|
31562
|
-
async readThreadTurns(threadId, itemsView) {
|
|
31717
|
+
async readThreadTurns(connectionId, threadId, itemsView) {
|
|
31563
31718
|
const turns = [];
|
|
31564
31719
|
let cursor2;
|
|
31565
31720
|
for (let page = 0; page < MAX_CODEX_HISTORY_PAGES; page += 1) {
|
|
31566
|
-
|
|
31567
|
-
|
|
31568
|
-
|
|
31569
|
-
|
|
31570
|
-
|
|
31571
|
-
|
|
31572
|
-
|
|
31721
|
+
let result;
|
|
31722
|
+
try {
|
|
31723
|
+
result = await this.callUpstream("thread/turns/list", {
|
|
31724
|
+
threadId,
|
|
31725
|
+
limit: CODEX_HISTORY_PAGE_LIMIT,
|
|
31726
|
+
sortDirection: "asc",
|
|
31727
|
+
itemsView,
|
|
31728
|
+
...cursor2 === void 0 ? {} : { cursor: cursor2 }
|
|
31729
|
+
});
|
|
31730
|
+
} catch (error) {
|
|
31731
|
+
if (page === 0 && isUnmaterializedThreadError(error)) {
|
|
31732
|
+
this.logEmptyUnmaterializedHistory(connectionId, `turns-${itemsView}`);
|
|
31733
|
+
return [];
|
|
31734
|
+
}
|
|
31735
|
+
throw error;
|
|
31736
|
+
}
|
|
31573
31737
|
if (!isRecord14(result) || !Array.isArray(result.data)) {
|
|
31574
31738
|
throw new RpcError("CODEX_INVALID_RESPONSE", "Codex App Server returned an invalid Turn page.");
|
|
31575
31739
|
}
|
|
@@ -31622,6 +31786,12 @@ var CodexRemoteDomain = class {
|
|
|
31622
31786
|
code: errorCode2(error)
|
|
31623
31787
|
});
|
|
31624
31788
|
}
|
|
31789
|
+
logEmptyUnmaterializedHistory(connectionId, stage) {
|
|
31790
|
+
this.logger.debug("Codex history projected as empty unmaterialized thread", {
|
|
31791
|
+
connectionId: maskId(connectionId),
|
|
31792
|
+
stage
|
|
31793
|
+
});
|
|
31794
|
+
}
|
|
31625
31795
|
async assertResultThreadAllowed(result) {
|
|
31626
31796
|
const thread = extractThread(result);
|
|
31627
31797
|
if (thread === void 0 || typeof thread.id !== "string") {
|
|
@@ -31702,8 +31872,13 @@ var CodexRemoteDomain = class {
|
|
|
31702
31872
|
return this.appServer;
|
|
31703
31873
|
}
|
|
31704
31874
|
async callUpstream(method, params) {
|
|
31875
|
+
const appServer = this.requireAppServer();
|
|
31705
31876
|
try {
|
|
31706
|
-
|
|
31877
|
+
const result = await appServer.call(method, params);
|
|
31878
|
+
if (this.closed || this.appServer !== appServer || !this.isAvailable()) {
|
|
31879
|
+
throw new RpcError("CODEX_UNAVAILABLE", "The Codex App Server changed while handling the request.");
|
|
31880
|
+
}
|
|
31881
|
+
return result;
|
|
31707
31882
|
} catch (error) {
|
|
31708
31883
|
throw mapAppServerError(error);
|
|
31709
31884
|
}
|
|
@@ -32164,6 +32339,9 @@ function isActiveWriterMessage(message) {
|
|
|
32164
32339
|
function isUnpersistedThreadError(error) {
|
|
32165
32340
|
return error instanceof RpcError && error.code === "CODEX_UPSTREAM_ERROR" && error.message.toLowerCase().includes("not persisted yet");
|
|
32166
32341
|
}
|
|
32342
|
+
function isUnmaterializedThreadError(error) {
|
|
32343
|
+
return error instanceof RpcError && error.code === CODEX_THREAD_UNMATERIALIZED;
|
|
32344
|
+
}
|
|
32167
32345
|
|
|
32168
32346
|
// src/acp/adapters/session-models.ts
|
|
32169
32347
|
var AcpSessionModelState = class {
|
|
@@ -32803,6 +32981,9 @@ var IsolatedAcpClient = class {
|
|
|
32803
32981
|
if (typeof id6 !== "string" || !/^[A-Za-z0-9_-]{1,240}$/.test(id6) || this.clients.has(id6) || this.closed || method === "session/load" && typeof result.sessionId === "string" && result.sessionId !== id6) throw new CursorAcpError("ACP_INVALID_SESSION", `${this.label} returned an invalid session.`);
|
|
32804
32982
|
this.clients.set(id6, client2);
|
|
32805
32983
|
const config = { ...result };
|
|
32984
|
+
this.updateConfiguration(id6, { kind: "notification", method: "session/update", params: {
|
|
32985
|
+
update: { sessionUpdate: "config_option_update", configOptions: config.configOptions }
|
|
32986
|
+
} }, config);
|
|
32806
32987
|
for (const key of ["models", "modes"]) {
|
|
32807
32988
|
if (replayConfiguration[key]) config[key] = { ...config[key], ...replayConfiguration[key] };
|
|
32808
32989
|
}
|
|
@@ -32831,10 +33012,12 @@ var IsolatedAcpClient = class {
|
|
|
32831
33012
|
try {
|
|
32832
33013
|
const modelResult = await client.call("session/set_config_option", { sessionId: id5, configId: "model", value: p.model });
|
|
32833
33014
|
this.updateConfiguration(id5, { kind: "notification", method: "session/update", params: { update: { sessionUpdate: "config_option_update", configOptions: modelResult.configOptions } } });
|
|
33015
|
+
if (this.configurations.get(id5)?.models?.currentModelId !== p.model) throw new CursorAcpError("ACP_MODEL_NOT_CONFIRMED", `${this.label} did not confirm the model change.`);
|
|
32834
33016
|
if (p.reasoningEffort !== void 0) {
|
|
32835
33017
|
if (!this.configurations.get(id5)?.modes?.availableModes?.some((item) => item.id === p.reasoningEffort)) throw new CursorAcpError("INVALID_MESSAGE", "The selected model does not support this thinking level.");
|
|
32836
33018
|
const thinkingResult = await client.call("session/set_config_option", { sessionId: id5, configId: "thought_level", value: p.reasoningEffort });
|
|
32837
33019
|
this.updateConfiguration(id5, { kind: "notification", method: "session/update", params: { update: { sessionUpdate: "config_option_update", configOptions: thinkingResult.configOptions } } });
|
|
33020
|
+
if (this.configurations.get(id5)?.modes?.currentModeId !== p.reasoningEffort) throw new CursorAcpError("ACP_MODEL_NOT_CONFIRMED", `${this.label} did not confirm the thinking change.`);
|
|
32838
33021
|
}
|
|
32839
33022
|
return { selected: this.models(id5).current };
|
|
32840
33023
|
} finally {
|
|
@@ -32860,14 +33043,15 @@ var IsolatedAcpClient = class {
|
|
|
32860
33043
|
if (update.sessionUpdate === "current_mode_update" && typeof update.currentModeId === "string") config.modes = { ...config.modes, currentModeId: update.currentModeId };
|
|
32861
33044
|
if (update.sessionUpdate === "config_option_update" && Array.isArray(update.configOptions)) {
|
|
32862
33045
|
for (const option of update.configOptions) {
|
|
32863
|
-
if (option
|
|
33046
|
+
if (option?.type !== "select" || !Array.isArray(option.options)) continue;
|
|
33047
|
+
const options = option.options.flatMap((item) => Array.isArray(item?.options) ? item.options : [item]).filter((item) => typeof item?.value === "string" && typeof item?.name === "string");
|
|
32864
33048
|
if (option.id === "model") config.models = {
|
|
32865
33049
|
currentModelId: option.currentValue,
|
|
32866
|
-
availableModels:
|
|
33050
|
+
availableModels: options.map((item) => ({ modelId: item.value, name: item.name }))
|
|
32867
33051
|
};
|
|
32868
33052
|
if (option.id === "thought_level") config.modes = {
|
|
32869
33053
|
currentModeId: option.currentValue,
|
|
32870
|
-
availableModes:
|
|
33054
|
+
availableModes: options.map((item) => ({ id: item.value, name: item.name }))
|
|
32871
33055
|
};
|
|
32872
33056
|
}
|
|
32873
33057
|
}
|
|
@@ -32956,7 +33140,7 @@ var IsolatedAcpClient = class {
|
|
|
32956
33140
|
};
|
|
32957
33141
|
|
|
32958
33142
|
// src/workspace-bridge.ts
|
|
32959
|
-
import { realpath as realpath2, lstat, readdir as readdir2,
|
|
33143
|
+
import { realpath as realpath2, lstat, readdir as readdir2, open, stat as stat4, watch } from "node:fs/promises";
|
|
32960
33144
|
import { isAbsolute as isAbsolute4, join as join7, relative as relative2, resolve as resolve2 } from "node:path";
|
|
32961
33145
|
|
|
32962
33146
|
// src/remote-terminal-controller.ts
|
|
@@ -33577,11 +33761,11 @@ var AsyncQueue = class {
|
|
|
33577
33761
|
}
|
|
33578
33762
|
};
|
|
33579
33763
|
var WorkspaceBridge = class {
|
|
33580
|
-
constructor(resolveCwd, terminalEnabled, state = new WorkspaceBridgeState(), spawnTerminal = pipeTerminalSpawner(),
|
|
33764
|
+
constructor(resolveCwd, terminalEnabled, state = new WorkspaceBridgeState(), spawnTerminal = pipeTerminalSpawner(), scope2) {
|
|
33581
33765
|
this.resolveCwd = resolveCwd;
|
|
33582
33766
|
this.terminalEnabled = terminalEnabled;
|
|
33583
33767
|
this.state = state;
|
|
33584
|
-
this.scope =
|
|
33768
|
+
this.scope = scope2;
|
|
33585
33769
|
this.terminal = new RemoteTerminalController(state, spawnTerminal, terminalEnabled, (code, message) => this.scopeError(code, message));
|
|
33586
33770
|
}
|
|
33587
33771
|
terminal;
|
|
@@ -33652,10 +33836,26 @@ var WorkspaceBridge = class {
|
|
|
33652
33836
|
return { ok: true, value: { absolutePath, version, bytes: info.size } };
|
|
33653
33837
|
}
|
|
33654
33838
|
if (!info.isFile()) throw this.scopeError("WORKSPACE_INVALID_PATH", "The requested workspace path is not a file.");
|
|
33839
|
+
if (info.size > MAX_READ_BYTES) throw this.scopeError("WORKSPACE_TOO_LARGE", "The requested workspace file is too large.");
|
|
33655
33840
|
const offset = readOffset(args);
|
|
33656
33841
|
const limit = readLimit(args);
|
|
33657
|
-
const
|
|
33658
|
-
|
|
33842
|
+
const file = await open(target2, "r");
|
|
33843
|
+
let bytes;
|
|
33844
|
+
try {
|
|
33845
|
+
const buffer = Buffer.allocUnsafe(info.size + 1);
|
|
33846
|
+
let length = 0;
|
|
33847
|
+
while (length < buffer.length) {
|
|
33848
|
+
signal.throwIfAborted();
|
|
33849
|
+
const read = await file.read(buffer, length, buffer.length - length, length);
|
|
33850
|
+
if (read.bytesRead === 0) break;
|
|
33851
|
+
length += read.bytesRead;
|
|
33852
|
+
}
|
|
33853
|
+
if (length > MAX_READ_BYTES) throw this.scopeError("WORKSPACE_TOO_LARGE", "The requested workspace file is too large.");
|
|
33854
|
+
if (length !== info.size) throw this.scopeError("WORKSPACE_CHANGED", "The workspace file changed while reading it. Retry the request.");
|
|
33855
|
+
bytes = buffer.subarray(0, length);
|
|
33856
|
+
} finally {
|
|
33857
|
+
await file.close();
|
|
33858
|
+
}
|
|
33659
33859
|
if (endpoint === "workspaceFiles/readBytes") {
|
|
33660
33860
|
const slice2 = bytes.subarray(offset, Math.min(offset + limit, bytes.length));
|
|
33661
33861
|
return {
|
|
@@ -34363,16 +34563,16 @@ var TranscriptWatcher = class extends EventEmitter {
|
|
|
34363
34563
|
await this.readNewLines();
|
|
34364
34564
|
}
|
|
34365
34565
|
async readNewLines() {
|
|
34366
|
-
let
|
|
34566
|
+
let stat9;
|
|
34367
34567
|
try {
|
|
34368
|
-
|
|
34568
|
+
stat9 = await fs2.stat(this.transcriptPath);
|
|
34369
34569
|
} catch {
|
|
34370
34570
|
return;
|
|
34371
34571
|
}
|
|
34372
|
-
if (
|
|
34572
|
+
if (stat9.size <= this.offset) return;
|
|
34373
34573
|
const handle = await fs2.open(this.transcriptPath, "r");
|
|
34374
34574
|
try {
|
|
34375
|
-
const bytesToRead =
|
|
34575
|
+
const bytesToRead = stat9.size - this.offset;
|
|
34376
34576
|
const buffer = Buffer.alloc(bytesToRead);
|
|
34377
34577
|
const { bytesRead } = await handle.read(buffer, 0, bytesToRead, this.offset);
|
|
34378
34578
|
this.offset += bytesRead;
|
|
@@ -35047,7 +35247,32 @@ import { realpath as realpath4 } from "node:fs/promises";
|
|
|
35047
35247
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
35048
35248
|
import { createServer as createServer2 } from "node:net";
|
|
35049
35249
|
import { realpath as realpath3 } from "node:fs/promises";
|
|
35250
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
35251
|
+
import { promisify as promisify2, stripVTControlCharacters } from "node:util";
|
|
35050
35252
|
var MAX_RESPONSE_BYTES = 64 * 1024;
|
|
35253
|
+
var execFileAsync = promisify2(execFile2);
|
|
35254
|
+
async function opencodeHasNativeApi(binary, args, cwd2, signal) {
|
|
35255
|
+
try {
|
|
35256
|
+
const { stdout, stderr } = await execFileAsync(binary, [...opencodePrivateArgs(args), "--help"], {
|
|
35257
|
+
cwd: cwd2,
|
|
35258
|
+
signal,
|
|
35259
|
+
timeout: 2e4,
|
|
35260
|
+
maxBuffer: MAX_RESPONSE_BYTES,
|
|
35261
|
+
windowsHide: true,
|
|
35262
|
+
killSignal: "SIGKILL"
|
|
35263
|
+
});
|
|
35264
|
+
const help = stripVTControlCharacters(`${stdout}
|
|
35265
|
+
${stderr}`);
|
|
35266
|
+
const hasFlag = (name2) => new RegExp(`^\\s*(?:-[A-Za-z],?\\s+)?--${name2}(?=[\\s=,\\[]|$)`, "m").test(help);
|
|
35267
|
+
const network = ["hostname", "port", "mdns"].map(hasFlag);
|
|
35268
|
+
if (!/\bacp\b/i.test(help) || !hasFlag("help") || network.some(Boolean) && !network.every(Boolean)) throw new Error("Invalid ACP help");
|
|
35269
|
+
return network.every(Boolean);
|
|
35270
|
+
} catch (error) {
|
|
35271
|
+
if (signal.aborted) throw new RpcError("OPENCODE_CLOSED", "OpenCode ACP was closed.");
|
|
35272
|
+
if (error?.code === "ENOENT") throw new RpcError("OPENCODE_BINARY_UNAVAILABLE", "The Host OpenCode executable is unavailable.");
|
|
35273
|
+
throw new RpcError("OPENCODE_CLI_PROBE_FAILED", "Could not inspect OpenCode ACP options. Check opencode acp --help on the Host.");
|
|
35274
|
+
}
|
|
35275
|
+
}
|
|
35051
35276
|
var OpencodeNativeApi = class {
|
|
35052
35277
|
constructor(origin, request = fetch) {
|
|
35053
35278
|
this.origin = origin;
|
|
@@ -35124,10 +35349,10 @@ function opencodePrivateArgs(args, port) {
|
|
|
35124
35349
|
for (let i = 0; i < args.length; i++) {
|
|
35125
35350
|
const arg = args[i];
|
|
35126
35351
|
if (/^--(?:port|hostname|mdns|no-mdns)(?:=|$)/.test(arg)) {
|
|
35127
|
-
if (arg === "--port" || arg === "--hostname" || arg === "--mdns" && /^(true|false)$/.test(args[i + 1] ?? "")) i++;
|
|
35352
|
+
if (arg === "--port" || arg === "--hostname" || (arg === "--mdns" || arg === "--no-mdns") && /^(true|false)$/.test(args[i + 1] ?? "")) i++;
|
|
35128
35353
|
} else kept.push(arg);
|
|
35129
35354
|
}
|
|
35130
|
-
return [...kept, `--hostname=127.0.0.1`, `--port=${port}`, "--mdns=false"];
|
|
35355
|
+
return port === void 0 ? kept : [...kept, `--hostname=127.0.0.1`, `--port=${port}`, "--mdns=false"];
|
|
35131
35356
|
}
|
|
35132
35357
|
function unavailable() {
|
|
35133
35358
|
return new RpcError("OPENCODE_ARCHIVE_UNAVAILABLE", "OpenCode did not confirm the archive operation. Check the Host OpenCode version and retry after refreshing its session list.");
|
|
@@ -35165,6 +35390,8 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35165
35390
|
failureNotified = false;
|
|
35166
35391
|
startPromise;
|
|
35167
35392
|
historyBusy = false;
|
|
35393
|
+
historyStop;
|
|
35394
|
+
startupAbort = new AbortController();
|
|
35168
35395
|
nativeApi;
|
|
35169
35396
|
archivedSessions = /* @__PURE__ */ new Set();
|
|
35170
35397
|
archivingSessions = /* @__PURE__ */ new Set();
|
|
@@ -35206,7 +35433,10 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35206
35433
|
if (this.closed) throw new OpencodeAcpError("OPENCODE_CLOSED", "The OpenCode domain is closed.");
|
|
35207
35434
|
if (this.historyBusy) throw new OpencodeAcpError("OPENCODE_HISTORY_BUSY", "An OpenCode history read is already in progress.");
|
|
35208
35435
|
this.historyBusy = true;
|
|
35209
|
-
const client = new _OpencodeAcpClient(this.binary, this.logger,
|
|
35436
|
+
const client = new _OpencodeAcpClient(this.binary, this.logger, this.spawnAcp, { ...this.options, historyReader: true });
|
|
35437
|
+
let stopping;
|
|
35438
|
+
const stop = () => stopping ??= client.close();
|
|
35439
|
+
this.historyStop = stop;
|
|
35210
35440
|
const updates = [];
|
|
35211
35441
|
let bytes = 0;
|
|
35212
35442
|
let count = 0;
|
|
@@ -35219,18 +35449,29 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35219
35449
|
if (message.method !== "session/update" || message.params?.sessionId !== sessionId || overflow) return;
|
|
35220
35450
|
count++;
|
|
35221
35451
|
bytes += Buffer7.byteLength(JSON.stringify(message));
|
|
35222
|
-
if (bytes > 8 * 1024 * 1024 || count > 1e4)
|
|
35223
|
-
|
|
35452
|
+
if (bytes > 8 * 1024 * 1024 || count > 1e4) {
|
|
35453
|
+
overflow = true;
|
|
35454
|
+
void stop().catch(() => void 0);
|
|
35455
|
+
} else updates.push(message);
|
|
35224
35456
|
});
|
|
35225
35457
|
try {
|
|
35226
35458
|
await client.start();
|
|
35459
|
+
if (this.closed) throw new OpencodeAcpError("OPENCODE_CLOSED", "OpenCode ACP was closed.");
|
|
35227
35460
|
await client.call("session/load", { sessionId, cwd: cwd2, mcpServers: [] });
|
|
35461
|
+
if (this.closed) throw new OpencodeAcpError("OPENCODE_CLOSED", "OpenCode ACP was closed.");
|
|
35228
35462
|
if (overflow) throw new OpencodeAcpError("OPENCODE_HISTORY_TOO_LARGE", "OpenCode history exceeds the transfer limit.");
|
|
35229
35463
|
return acpHistoryEvents(updates, sessionId, "opencode");
|
|
35464
|
+
} catch (error) {
|
|
35465
|
+
if (overflow) throw new OpencodeAcpError("OPENCODE_HISTORY_TOO_LARGE", "OpenCode history exceeds the transfer limit.");
|
|
35466
|
+
throw error;
|
|
35230
35467
|
} finally {
|
|
35231
35468
|
unsubscribe();
|
|
35232
|
-
|
|
35233
|
-
|
|
35469
|
+
try {
|
|
35470
|
+
await stop();
|
|
35471
|
+
} finally {
|
|
35472
|
+
this.historyStop = void 0;
|
|
35473
|
+
this.historyBusy = false;
|
|
35474
|
+
}
|
|
35234
35475
|
}
|
|
35235
35476
|
}
|
|
35236
35477
|
models = /* @__PURE__ */ new Map();
|
|
@@ -35340,14 +35581,19 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35340
35581
|
async close() {
|
|
35341
35582
|
if (this.closed) return;
|
|
35342
35583
|
this.closed = true;
|
|
35584
|
+
this.startupAbort.abort();
|
|
35343
35585
|
this.ready = false;
|
|
35344
35586
|
this.models.clear();
|
|
35345
35587
|
this.nativeApi?.close();
|
|
35346
35588
|
this.failPending(new OpencodeAcpError("OPENCODE_CLOSED", "OpenCode ACP was closed."));
|
|
35589
|
+
const history = this.historyStop?.();
|
|
35347
35590
|
const child = this.process;
|
|
35348
35591
|
this.process = void 0;
|
|
35349
|
-
if (child === void 0 || child.exitCode !== null || child.killed)
|
|
35350
|
-
|
|
35592
|
+
if (child === void 0 || child.exitCode !== null || child.killed) {
|
|
35593
|
+
await history;
|
|
35594
|
+
return;
|
|
35595
|
+
}
|
|
35596
|
+
await Promise.all([history, new Promise((resolve6) => {
|
|
35351
35597
|
const timer = setTimeout(() => {
|
|
35352
35598
|
child.kill("SIGKILL");
|
|
35353
35599
|
resolve6();
|
|
@@ -35358,7 +35604,7 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35358
35604
|
resolve6();
|
|
35359
35605
|
});
|
|
35360
35606
|
child.kill("SIGTERM");
|
|
35361
|
-
});
|
|
35607
|
+
})]);
|
|
35362
35608
|
}
|
|
35363
35609
|
async startOnce() {
|
|
35364
35610
|
if (this.process !== void 0) {
|
|
@@ -35367,25 +35613,32 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35367
35613
|
let child;
|
|
35368
35614
|
if (this.spawnAcp) child = this.spawnAcp(this.binary);
|
|
35369
35615
|
else {
|
|
35370
|
-
const
|
|
35616
|
+
const args = this.options.args ?? ["acp"];
|
|
35617
|
+
const nativeApiSupported = await opencodeHasNativeApi(this.binary, args, this.options.cwd, this.startupAbort.signal);
|
|
35618
|
+
const port = nativeApiSupported ? await opencodeLoopbackPort() : void 0;
|
|
35371
35619
|
if (this.closed) throw new OpencodeAcpError("OPENCODE_CLOSED", "OpenCode ACP was closed.");
|
|
35372
|
-
this.nativeApi = new OpencodeNativeApi(`http://127.0.0.1:${port}`);
|
|
35373
|
-
child = spawn6(this.binary, opencodePrivateArgs(
|
|
35620
|
+
if (port !== void 0) this.nativeApi = new OpencodeNativeApi(`http://127.0.0.1:${port}`);
|
|
35621
|
+
child = spawn6(this.binary, opencodePrivateArgs(args, port), {
|
|
35374
35622
|
cwd: this.options.cwd,
|
|
35375
35623
|
stdio: ["pipe", "pipe", "pipe"],
|
|
35376
35624
|
windowsHide: true,
|
|
35377
|
-
env: { ...process.env, OPENCODE_SERVER_USERNAME: "opencode", OPENCODE_SERVER_PASSWORD: this.nativeApi.password }
|
|
35625
|
+
env: { ...process.env, ...this.nativeApi ? { OPENCODE_SERVER_USERNAME: "opencode", OPENCODE_SERVER_PASSWORD: this.nativeApi.password } : {} }
|
|
35378
35626
|
});
|
|
35379
35627
|
}
|
|
35380
35628
|
this.process = child;
|
|
35381
35629
|
this.failureNotified = false;
|
|
35382
35630
|
this.stdoutBuffer = Buffer7.alloc(0);
|
|
35383
35631
|
this.stderrBytes = 0;
|
|
35384
|
-
child.stdout.on("data", (chunk) =>
|
|
35632
|
+
child.stdout.on("data", (chunk) => {
|
|
35633
|
+
if (this.process === child) this.consumeStdout(Buffer7.from(chunk));
|
|
35634
|
+
});
|
|
35385
35635
|
child.stderr.on("data", (chunk) => {
|
|
35636
|
+
if (this.process !== child) return;
|
|
35386
35637
|
this.stderrBytes = Math.min(MAX_STDERR_CAPTURE_BYTES4, this.stderrBytes + Buffer7.byteLength(chunk));
|
|
35387
35638
|
});
|
|
35388
|
-
child.on("error", (error) =>
|
|
35639
|
+
child.on("error", (error) => {
|
|
35640
|
+
if (this.process === child) this.handleProcessFailure("OPENCODE_BINARY_UNAVAILABLE", error);
|
|
35641
|
+
});
|
|
35389
35642
|
child.on("exit", (code, signal) => {
|
|
35390
35643
|
if (this.process !== child) return;
|
|
35391
35644
|
this.process = void 0;
|
|
@@ -35393,6 +35646,7 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35393
35646
|
this.nativeApi?.close();
|
|
35394
35647
|
this.nativeApi = void 0;
|
|
35395
35648
|
this.models.clear();
|
|
35649
|
+
void this.historyStop?.().catch(() => void 0);
|
|
35396
35650
|
this.failPending(new OpencodeAcpError("OPENCODE_ACP_EXITED", "OpenCode ACP exited unexpectedly."));
|
|
35397
35651
|
if (!this.closed) {
|
|
35398
35652
|
this.logger?.warn("OpenCode ACP exited", {
|
|
@@ -35415,6 +35669,7 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35415
35669
|
version: PLUGIN_VERSION
|
|
35416
35670
|
}
|
|
35417
35671
|
}, ACP_START_TIMEOUT_MS3);
|
|
35672
|
+
if (this.closed || this.process !== child) throw new OpencodeAcpError("OPENCODE_CLOSED", "OpenCode ACP was closed.");
|
|
35418
35673
|
this.ready = true;
|
|
35419
35674
|
this.logger?.info("OpenCode ACP ready");
|
|
35420
35675
|
} catch (error) {
|
|
@@ -35502,6 +35757,7 @@ var OpencodeAcpClient = class _OpencodeAcpClient {
|
|
|
35502
35757
|
this.publish(inbound);
|
|
35503
35758
|
}
|
|
35504
35759
|
handleProcessFailure(code, cause) {
|
|
35760
|
+
void this.historyStop?.().catch(() => void 0);
|
|
35505
35761
|
this.nativeApi?.close();
|
|
35506
35762
|
this.nativeApi = void 0;
|
|
35507
35763
|
this.ready = false;
|
|
@@ -35778,15 +36034,15 @@ async function querySqliteJson(dbPath, sql, params = []) {
|
|
|
35778
36034
|
} catch {
|
|
35779
36035
|
}
|
|
35780
36036
|
try {
|
|
35781
|
-
const { execFile:
|
|
35782
|
-
const { promisify:
|
|
35783
|
-
const
|
|
36037
|
+
const { execFile: execFile3 } = await import("node:child_process");
|
|
36038
|
+
const { promisify: promisify3 } = await import("node:util");
|
|
36039
|
+
const execFileAsync2 = promisify3(execFile3);
|
|
35784
36040
|
let formattedSql = sql;
|
|
35785
36041
|
for (const p of params) {
|
|
35786
36042
|
const val = typeof p === "number" ? String(p) : `'${String(p).replace(/'/g, "''")}'`;
|
|
35787
36043
|
formattedSql = formattedSql.replace("?", val);
|
|
35788
36044
|
}
|
|
35789
|
-
const { stdout } = await
|
|
36045
|
+
const { stdout } = await execFileAsync2("sqlite3", [dbPath, "-json", formattedSql]);
|
|
35790
36046
|
return JSON.parse(stdout || "[]");
|
|
35791
36047
|
} catch {
|
|
35792
36048
|
return [];
|
|
@@ -35836,7 +36092,7 @@ async function readTranscriptSummary(baseDir, conversationId) {
|
|
|
35836
36092
|
try {
|
|
35837
36093
|
const path = await transcriptPath(baseDir, conversationId);
|
|
35838
36094
|
if (path === void 0) return void 0;
|
|
35839
|
-
const
|
|
36095
|
+
const stat9 = await fs3.stat(path);
|
|
35840
36096
|
const handle = await fs3.open(path, "r");
|
|
35841
36097
|
try {
|
|
35842
36098
|
const buffer = Buffer.alloc(4096);
|
|
@@ -35847,7 +36103,7 @@ async function readTranscriptSummary(baseDir, conversationId) {
|
|
|
35847
36103
|
if (record9.type !== "USER_INPUT") return void 0;
|
|
35848
36104
|
const title = cleanUserPrompt(record9.content).split("\n")[0]?.trim().slice(0, 40) || (agyImageReferences(record9.content ?? "").length > 0 ? "Image" : void 0);
|
|
35849
36105
|
if (!title) return void 0;
|
|
35850
|
-
return { conversationId, title, createdAt: record9.created_at ? new Date(record9.created_at).getTime() :
|
|
36106
|
+
return { conversationId, title, createdAt: record9.created_at ? new Date(record9.created_at).getTime() : stat9.mtimeMs, updatedAt: stat9.mtimeMs };
|
|
35851
36107
|
} finally {
|
|
35852
36108
|
await handle.close();
|
|
35853
36109
|
}
|
|
@@ -36150,6 +36406,7 @@ var AcpPeerBridge = class {
|
|
|
36150
36406
|
});
|
|
36151
36407
|
throw error;
|
|
36152
36408
|
}
|
|
36409
|
+
this.requireOpen();
|
|
36153
36410
|
const responseBytes = new TextEncoder().encode(JSON.stringify(response));
|
|
36154
36411
|
if (responseBytes.byteLength <= INLINE_TRANSFER_RESPONSE_BYTES2) return { kind: "inline", response };
|
|
36155
36412
|
if (responseBytes.byteLength > MAX_AGENT_ACP_TRANSFER_BYTES) {
|
|
@@ -36243,11 +36500,11 @@ var AcpPeerBridge = class {
|
|
|
36243
36500
|
this.toolControllers.clear();
|
|
36244
36501
|
this.incomingTransfers.clear();
|
|
36245
36502
|
this.outgoingTransfers.clear();
|
|
36503
|
+
this.domain.dropPeer(this.context.connectionId);
|
|
36246
36504
|
await Promise.all(streamIds.map((streamId) => this.publish("agent.acp.stream.closed", {
|
|
36247
36505
|
streamId,
|
|
36248
36506
|
reason: "peer-disconnected"
|
|
36249
36507
|
}).catch(() => void 0)));
|
|
36250
|
-
this.domain.dropPeer(this.context.connectionId);
|
|
36251
36508
|
}
|
|
36252
36509
|
pruneTransfers() {
|
|
36253
36510
|
const staleBefore = Date.now() - TRANSFER_IDLE_MS2;
|
|
@@ -36348,7 +36605,7 @@ var AcpRemoteGateway = class {
|
|
|
36348
36605
|
/** Catch-up buffer for turns that finish while the Client is reconnecting. */
|
|
36349
36606
|
recentFrames = /* @__PURE__ */ new Map();
|
|
36350
36607
|
/** Per-prompt live updates; attached to prompt_completed when streaming was lossy. */
|
|
36351
|
-
promptsInProgress = /* @__PURE__ */ new
|
|
36608
|
+
promptsInProgress = /* @__PURE__ */ new Map();
|
|
36352
36609
|
cancelledSessions = /* @__PURE__ */ new Set();
|
|
36353
36610
|
/** Identity fences asynchronous Pi preparation from a later retry of the same session. */
|
|
36354
36611
|
preparingPrompts = /* @__PURE__ */ new Map();
|
|
@@ -36436,13 +36693,16 @@ var AcpRemoteGateway = class {
|
|
|
36436
36693
|
const call = parseAcpCall(envelope.method, envelope.params);
|
|
36437
36694
|
const sessionId = sessionIdFromParams(call.method, call.params);
|
|
36438
36695
|
const backend = call.params.backend ?? (sessionId ? this.sessionBackends.get(sessionId) ?? this.defaultBackend() : void 0);
|
|
36696
|
+
const assertCurrent = this.captureCallScope(connectionId, typeof backend === "string" ? backend : this.defaultBackend());
|
|
36439
36697
|
const reserve = backend === "opencode" && sessionId !== void 0 && ["session/load", "session/prompt", "dsh/selectModel"].includes(call.method);
|
|
36440
36698
|
if (reserve) {
|
|
36441
36699
|
if (this.archivingSessions.has(sessionId)) throw new RpcError("ACP_SESSION_BUSY", "The OpenCode session is being archived.");
|
|
36442
36700
|
this.opencodeOperations.set(sessionId, (this.opencodeOperations.get(sessionId) ?? 0) + 1);
|
|
36443
36701
|
}
|
|
36444
36702
|
try {
|
|
36445
|
-
|
|
36703
|
+
const result = await this.callInternal(connectionId, call, assertCurrent);
|
|
36704
|
+
assertCurrent();
|
|
36705
|
+
return result;
|
|
36446
36706
|
} finally {
|
|
36447
36707
|
if (reserve) {
|
|
36448
36708
|
const remaining = (this.opencodeOperations.get(sessionId) ?? 1) - 1;
|
|
@@ -36451,7 +36711,20 @@ var AcpRemoteGateway = class {
|
|
|
36451
36711
|
}
|
|
36452
36712
|
}
|
|
36453
36713
|
}
|
|
36454
|
-
|
|
36714
|
+
captureCallScope(connectionId, backend) {
|
|
36715
|
+
const peer = this.peers.get(connectionId);
|
|
36716
|
+
const instance = this.backendInstances.get(backend);
|
|
36717
|
+
return () => {
|
|
36718
|
+
if (this.closed || this.backendInstances.get(backend) !== instance || instance !== void 0 && this.requireAcp(backend) !== instance.client) {
|
|
36719
|
+
throw new RpcError("CURSOR_UNAVAILABLE", "The ACP backend changed while handling the request.");
|
|
36720
|
+
}
|
|
36721
|
+
if (peer !== void 0 && this.peers.get(connectionId) !== peer) {
|
|
36722
|
+
throw new RpcError("ACP_CONNECTION_CLOSED", "The ACP connection is closed.");
|
|
36723
|
+
}
|
|
36724
|
+
};
|
|
36725
|
+
}
|
|
36726
|
+
async callInternal(connectionId, call, assertCurrent) {
|
|
36727
|
+
assertCurrent();
|
|
36455
36728
|
this.logger.info("ACP call received", {
|
|
36456
36729
|
method: call.method,
|
|
36457
36730
|
sessionId: typeof call.params?.sessionId === "string" ? shortSessionId(call.params.sessionId) : void 0
|
|
@@ -36482,6 +36755,7 @@ var AcpRemoteGateway = class {
|
|
|
36482
36755
|
const client = this.requireAcp(backend);
|
|
36483
36756
|
const instance = this.backendInstances.get(backend);
|
|
36484
36757
|
const cwd2 = await this.catalogSessionCwd(backend, sessionId2);
|
|
36758
|
+
assertCurrent();
|
|
36485
36759
|
if (this.closed || this.requireAcp(backend) !== client || this.backendInstances.get(backend) !== instance) throw new RpcError("OPENCODE_ARCHIVE_UNAVAILABLE", "The OpenCode backend changed. Refresh the session list.");
|
|
36486
36760
|
if (this.promptsInProgress.has(sessionId2) || [...this.approvals.values()].some((item) => item.sessionId === sessionId2)) throw new RpcError("ACP_SESSION_BUSY", "Stop the OpenCode reply and resolve its approvals before archiving.");
|
|
36487
36761
|
if (!client.archiveSession) throw new RpcError("OPENCODE_ARCHIVE_UNAVAILABLE", "The Host OpenCode adapter does not support native archiving.");
|
|
@@ -36511,6 +36785,7 @@ var AcpRemoteGateway = class {
|
|
|
36511
36785
|
if (archived) throw new RpcError("ACP_SESSION_ARCHIVED", "This OpenCode session is archived.");
|
|
36512
36786
|
}
|
|
36513
36787
|
}
|
|
36788
|
+
assertCurrent();
|
|
36514
36789
|
if (call.method === "dsh/directoryList") {
|
|
36515
36790
|
return this.listDirectory(String(call.params.path));
|
|
36516
36791
|
}
|
|
@@ -36576,11 +36851,13 @@ var AcpRemoteGateway = class {
|
|
|
36576
36851
|
if (backend === "antigravity") {
|
|
36577
36852
|
if (path.trim() !== "" && call.params.prewarm !== false) {
|
|
36578
36853
|
const cwd2 = await this.requireExistingDirectory(path);
|
|
36854
|
+
assertCurrent();
|
|
36579
36855
|
this.requireAcp("antigravity").prewarmSession?.(cwd2);
|
|
36580
36856
|
}
|
|
36581
36857
|
const items = await discoverAntigravitySessions(path, limit);
|
|
36582
36858
|
if (path) {
|
|
36583
36859
|
const cwd2 = await this.requireExistingDirectory(path);
|
|
36860
|
+
assertCurrent();
|
|
36584
36861
|
for (const item of items) this.sessionCwds.set(item.conversationId, { backend, cwd: cwd2 });
|
|
36585
36862
|
}
|
|
36586
36863
|
this.logger.info("ACP session list fetched", { count: items.length });
|
|
@@ -36619,6 +36896,7 @@ var AcpRemoteGateway = class {
|
|
|
36619
36896
|
}
|
|
36620
36897
|
if (call.method === "session/new") {
|
|
36621
36898
|
const cwd2 = await this.requireExistingDirectory(String(call.params.cwd));
|
|
36899
|
+
assertCurrent();
|
|
36622
36900
|
const requestedBackend = typeof call.params.backend === "string" ? call.params.backend : this.defaultBackend();
|
|
36623
36901
|
const acp = this.requireAcp(requestedBackend);
|
|
36624
36902
|
const result = await acp.call("session/new", {
|
|
@@ -36626,6 +36904,7 @@ var AcpRemoteGateway = class {
|
|
|
36626
36904
|
mcpServers: [],
|
|
36627
36905
|
...typeof call.params.mode === "string" ? { mode: call.params.mode } : {}
|
|
36628
36906
|
});
|
|
36907
|
+
assertCurrent();
|
|
36629
36908
|
const sessionId2 = readSessionId(result);
|
|
36630
36909
|
if (sessionId2 !== void 0) {
|
|
36631
36910
|
const bound = this.sessionBackends.get(sessionId2);
|
|
@@ -36661,9 +36940,12 @@ var AcpRemoteGateway = class {
|
|
|
36661
36940
|
throw new RpcError("INVALID_MESSAGE", "The ACP session backend does not match.");
|
|
36662
36941
|
}
|
|
36663
36942
|
if (targetBackend === "pi" || targetBackend === "claude" || targetBackend === "opencode") await this.catalogSessionCwd(targetBackend, sessionId);
|
|
36943
|
+
assertCurrent();
|
|
36664
36944
|
this.requireSessionOwner(connectionId, sessionId);
|
|
36665
36945
|
const acp = this.requireAcp(targetBackend);
|
|
36946
|
+
this.sessionBackends.set(sessionId, targetBackend);
|
|
36666
36947
|
const result = await acp.call(call.method, { ...call.params, ...sessionId && this.sessionCwds.get(sessionId) ? { cwd: this.sessionCwds.get(sessionId).cwd, mcpServers: [] } : {} });
|
|
36948
|
+
assertCurrent();
|
|
36667
36949
|
const loadedId = readSessionId(result) ?? sessionId;
|
|
36668
36950
|
if (loadedId !== void 0) {
|
|
36669
36951
|
if (loadedId !== sessionId || this.sessionBackends.has(loadedId) && targetBackend !== void 0 && this.sessionBackends.get(loadedId) !== targetBackend) {
|
|
@@ -36681,7 +36963,9 @@ var AcpRemoteGateway = class {
|
|
|
36681
36963
|
this.requireSessionOwner(connectionId, sessionId);
|
|
36682
36964
|
const backend = call.params.backend;
|
|
36683
36965
|
const cwd2 = await this.catalogSessionCwd(backend, sessionId);
|
|
36966
|
+
assertCurrent();
|
|
36684
36967
|
await this.requireAcp(backend).call("session/load", { sessionId, cwd: cwd2, mcpServers: [] });
|
|
36968
|
+
assertCurrent();
|
|
36685
36969
|
}
|
|
36686
36970
|
if (call.method === "dsh/selectModel") {
|
|
36687
36971
|
if (this.promptsInProgress.has(sessionId)) throw new RpcError("PROMPT_IN_PROGRESS", "Stop the ACP reply before changing its model.");
|
|
@@ -36690,12 +36974,15 @@ var AcpRemoteGateway = class {
|
|
|
36690
36974
|
throw new RpcError("ACP_WORKSPACE_UNAVAILABLE", "The ACP session has no trusted workspace.");
|
|
36691
36975
|
}
|
|
36692
36976
|
const result = await this.requireAcp(session.backend).call(call.method, { ...call.params, cwd: session.cwd });
|
|
36977
|
+
assertCurrent();
|
|
36693
36978
|
this.sessionBackends.set(sessionId, session.backend);
|
|
36694
36979
|
return sanitizeSessionResult(result);
|
|
36695
36980
|
}
|
|
36696
36981
|
if (call.method === "session/prompt" && sessionId !== void 0) {
|
|
36697
36982
|
if (this.promptsInProgress.has(sessionId)) throw new RpcError("PROMPT_IN_PROGRESS", "An ACP reply is already in progress.");
|
|
36698
|
-
this.
|
|
36983
|
+
const backend = this.sessionBackends.get(sessionId) ?? this.defaultBackend();
|
|
36984
|
+
const task = { backend, client: this.requireAcp(backend) };
|
|
36985
|
+
this.promptsInProgress.set(sessionId, task);
|
|
36699
36986
|
this.cancelledSessions.delete(sessionId);
|
|
36700
36987
|
const preparationBackend = this.sessionBackends.get(sessionId);
|
|
36701
36988
|
if (preparationBackend === "pi" || preparationBackend === "claude") {
|
|
@@ -36704,16 +36991,18 @@ var AcpRemoteGateway = class {
|
|
|
36704
36991
|
try {
|
|
36705
36992
|
const cwd2 = await this.catalogSessionCwd(preparationBackend, sessionId);
|
|
36706
36993
|
if (preparation.cancelled) return { accepted: false, stopReason: "cancelled" };
|
|
36707
|
-
|
|
36994
|
+
assertCurrent();
|
|
36995
|
+
await task.client.call("session/load", { sessionId, cwd: cwd2, mcpServers: [] });
|
|
36708
36996
|
if (preparation.cancelled) return { accepted: false, stopReason: "cancelled" };
|
|
36997
|
+
assertCurrent();
|
|
36709
36998
|
} catch (error) {
|
|
36710
36999
|
if (preparation.cancelled) return { accepted: false, stopReason: "cancelled" };
|
|
36711
|
-
this.promptsInProgress.delete(sessionId);
|
|
37000
|
+
if (this.promptsInProgress.get(sessionId) === task) this.promptsInProgress.delete(sessionId);
|
|
36712
37001
|
throw error;
|
|
36713
37002
|
} finally {
|
|
36714
37003
|
if (this.preparingPrompts.get(sessionId) === preparation) {
|
|
36715
37004
|
this.preparingPrompts.delete(sessionId);
|
|
36716
|
-
if (preparation.cancelled) this.promptsInProgress.delete(sessionId);
|
|
37005
|
+
if (preparation.cancelled && this.promptsInProgress.get(sessionId) === task) this.promptsInProgress.delete(sessionId);
|
|
36717
37006
|
}
|
|
36718
37007
|
}
|
|
36719
37008
|
}
|
|
@@ -36724,7 +37013,7 @@ var AcpRemoteGateway = class {
|
|
|
36724
37013
|
});
|
|
36725
37014
|
}
|
|
36726
37015
|
this.turnCatchUp.set(sessionId, []);
|
|
36727
|
-
void this.runPromptInBackground(sessionId, call.params);
|
|
37016
|
+
void this.runPromptInBackground(sessionId, call.params, task);
|
|
36728
37017
|
return { accepted: true, stopReason: "in_progress" };
|
|
36729
37018
|
}
|
|
36730
37019
|
if (call.method === "session/cancel" && sessionId !== void 0) {
|
|
@@ -36747,12 +37036,12 @@ var AcpRemoteGateway = class {
|
|
|
36747
37036
|
const sessionBackend = sessionId !== void 0 ? this.sessionBackends.get(sessionId) : void 0;
|
|
36748
37037
|
return sanitizeSessionResult(await this.requireAcp(typeof call.params.backend === "string" ? call.params.backend : sessionBackend).call(call.method, call.params));
|
|
36749
37038
|
}
|
|
36750
|
-
async runPromptInBackground(sessionId, params) {
|
|
37039
|
+
async runPromptInBackground(sessionId, params, task) {
|
|
37040
|
+
const current = () => !this.closed && this.promptsInProgress.get(sessionId) === task && !this.pendingReconfiguration.has(task.backend) && this.backendInstances.get(task.backend)?.client === task.client;
|
|
36751
37041
|
try {
|
|
36752
|
-
const
|
|
36753
|
-
const acp = this.requireAcp(sessionBackend);
|
|
36754
|
-
const result = await acp.call("session/prompt", params);
|
|
37042
|
+
const result = await task.client.call("session/prompt", params);
|
|
36755
37043
|
await this.inboundChain;
|
|
37044
|
+
if (!current()) return;
|
|
36756
37045
|
const stopReason = isRecord19(result) && typeof result.stopReason === "string" ? result.stopReason : "end_turn";
|
|
36757
37046
|
const catchUp = takeTurnCatchUp(this.turnCatchUp, sessionId);
|
|
36758
37047
|
this.logger.info("ACP prompt finished", {
|
|
@@ -36772,8 +37061,10 @@ var AcpRemoteGateway = class {
|
|
|
36772
37061
|
}
|
|
36773
37062
|
});
|
|
36774
37063
|
} catch (error) {
|
|
37064
|
+
if (!current()) return;
|
|
36775
37065
|
this.logger?.warn("ACP session/prompt failed", { code: errorCode3(error) });
|
|
36776
37066
|
await this.inboundChain.catch(() => void 0);
|
|
37067
|
+
if (!current()) return;
|
|
36777
37068
|
const catchUp = takeTurnCatchUp(this.turnCatchUp, sessionId);
|
|
36778
37069
|
await this.publishToSession(sessionId, {
|
|
36779
37070
|
method: "session/update",
|
|
@@ -36787,7 +37078,7 @@ var AcpRemoteGateway = class {
|
|
|
36787
37078
|
}
|
|
36788
37079
|
}).catch(() => void 0);
|
|
36789
37080
|
} finally {
|
|
36790
|
-
this.promptsInProgress.delete(sessionId);
|
|
37081
|
+
if (this.promptsInProgress.get(sessionId) === task) this.promptsInProgress.delete(sessionId);
|
|
36791
37082
|
}
|
|
36792
37083
|
}
|
|
36793
37084
|
async respond(connectionId, input3) {
|
|
@@ -36832,6 +37123,18 @@ var AcpRemoteGateway = class {
|
|
|
36832
37123
|
if (!tools) throw new RpcError("ACP_CONNECTION_CLOSED", "The ACP workspace connection is closed.");
|
|
36833
37124
|
return tools;
|
|
36834
37125
|
}
|
|
37126
|
+
/** Trusted, connection-scoped authority for the dedicated workspace uploader. */
|
|
37127
|
+
resolveUploadWorkspace(connectionId, publicId) {
|
|
37128
|
+
const match = /^(acp|cursor|pi|opencode|claude):(.+)$/.exec(publicId);
|
|
37129
|
+
const backend = match?.[1] === "acp" ? "antigravity" : match?.[1];
|
|
37130
|
+
const session = match ? this.sessionCwds.get(match[2]) : void 0;
|
|
37131
|
+
if (!this.peers.has(connectionId) || !session || session.backend !== backend || this.closed) {
|
|
37132
|
+
throw new RpcError("ACP_WORKSPACE_UNAVAILABLE", "The ACP session has no trusted workspace.");
|
|
37133
|
+
}
|
|
37134
|
+
this.requireAcp(backend);
|
|
37135
|
+
this.requireSessionOwner(connectionId, match[2]);
|
|
37136
|
+
return session.cwd;
|
|
37137
|
+
}
|
|
36835
37138
|
async openToolStream(connectionId, sessionId, backend, endpoint, args, signal) {
|
|
36836
37139
|
const session = this.sessionCwds.get(sessionId);
|
|
36837
37140
|
if (!session || session.backend !== backend) throw new RpcError("ACP_WORKSPACE_UNAVAILABLE", "The ACP session has no trusted workspace.");
|
|
@@ -36898,6 +37201,10 @@ var AcpRemoteGateway = class {
|
|
|
36898
37201
|
const terminalError = await this.workspaceState.dispose((sessionId) => changed.has(sessionId.startsWith("acp:") ? "antigravity" : sessionId.startsWith("pi:") ? "pi" : sessionId.startsWith("opencode:") ? "opencode" : "cursor")).then(() => void 0, () => new RpcError("TERMINAL_CLEANUP_FAILED", "Some ACP terminal processes could not be stopped. Save the backend settings again to retry."));
|
|
36899
37202
|
for (const id5 of sessions) {
|
|
36900
37203
|
this.sessionOwners.delete(id5);
|
|
37204
|
+
this.promptsInProgress.delete(id5);
|
|
37205
|
+
const preparation = this.preparingPrompts.get(id5);
|
|
37206
|
+
if (preparation) preparation.cancelled = true;
|
|
37207
|
+
this.preparingPrompts.delete(id5);
|
|
36901
37208
|
this.sessionBackends.delete(id5);
|
|
36902
37209
|
this.recentFrames.delete(id5);
|
|
36903
37210
|
this.turnCatchUp.delete(id5);
|
|
@@ -36932,6 +37239,9 @@ var AcpRemoteGateway = class {
|
|
|
36932
37239
|
this.sessionBackends.clear();
|
|
36933
37240
|
this.recentFrames.clear();
|
|
36934
37241
|
this.turnCatchUp.clear();
|
|
37242
|
+
this.promptsInProgress.clear();
|
|
37243
|
+
for (const preparation of this.preparingPrompts.values()) preparation.cancelled = true;
|
|
37244
|
+
this.preparingPrompts.clear();
|
|
36935
37245
|
this.approvals.clear();
|
|
36936
37246
|
const processes = await Promise.allSettled([...this.startingClients.keys()].map((client) => client.close()).concat(this.disposeAllInstances()));
|
|
36937
37247
|
const terminalError = await terminals;
|
|
@@ -36969,8 +37279,8 @@ var AcpRemoteGateway = class {
|
|
|
36969
37279
|
this.restartAttempt = 0;
|
|
36970
37280
|
return;
|
|
36971
37281
|
}
|
|
36972
|
-
const
|
|
36973
|
-
throw
|
|
37282
|
+
const failure4 = results.find((result) => result.status === "rejected");
|
|
37283
|
+
throw failure4?.status === "rejected" ? failure4.reason : new CursorAcpError("CURSOR_BINARY_UNAVAILABLE", "No ACP backend is available.");
|
|
36974
37284
|
}
|
|
36975
37285
|
backendCurrent(backend) {
|
|
36976
37286
|
return this.config.enabled && backend.enabled && !this.pendingReconfiguration.has(backend.id) && JSON.stringify(this.config.backends.find((item) => item.id === backend.id)) === JSON.stringify(backend);
|
|
@@ -37866,8 +38176,223 @@ function redact(value, key = "") {
|
|
|
37866
38176
|
return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redact(child, childKey)]));
|
|
37867
38177
|
}
|
|
37868
38178
|
|
|
37869
|
-
// src/
|
|
38179
|
+
// src/workspace-upload.ts
|
|
37870
38180
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
38181
|
+
import { constants as constants4 } from "node:fs";
|
|
38182
|
+
import { link, lstat as lstat2, open as open2, realpath as realpath6, stat as stat6, unlink } from "node:fs/promises";
|
|
38183
|
+
import { extname, isAbsolute as isAbsolute7, join as join12 } from "node:path";
|
|
38184
|
+
var WORKSPACE_UPLOAD_BYTES = 20 * 1024 * 1024;
|
|
38185
|
+
var WORKSPACE_UPLOAD_CHUNK = 64 * 1024;
|
|
38186
|
+
var scope = external_exports.object({ sessionId: external_exports.string().min(1).max(512), uploadId: external_exports.string().uuid() }).strict();
|
|
38187
|
+
var openParams = scope.extend({ name: external_exports.string().min(1), totalBytes: external_exports.number().int().min(0).max(WORKSPACE_UPLOAD_BYTES) }).strict();
|
|
38188
|
+
var chunkParams = scope.extend({ offset: external_exports.number().int().min(0), data: external_exports.string().min(1).max(Math.ceil(WORKSPACE_UPLOAD_CHUNK / 3) * 4) }).strict();
|
|
38189
|
+
var WorkspaceUploadHost = class {
|
|
38190
|
+
constructor(resolveRoot, ttlMs = 12e4) {
|
|
38191
|
+
this.resolveRoot = resolveRoot;
|
|
38192
|
+
this.ttlMs = ttlMs;
|
|
38193
|
+
this.timer = setInterval(() => {
|
|
38194
|
+
void this.serialize(() => this.expire()).catch(() => void 0);
|
|
38195
|
+
}, Math.min(ttlMs, 3e4));
|
|
38196
|
+
this.timer.unref();
|
|
38197
|
+
}
|
|
38198
|
+
uploads = /* @__PURE__ */ new Map();
|
|
38199
|
+
completed = /* @__PURE__ */ new Map();
|
|
38200
|
+
queue = Promise.resolve();
|
|
38201
|
+
lifetime = new AbortController();
|
|
38202
|
+
timer;
|
|
38203
|
+
closed = false;
|
|
38204
|
+
call(method, input3) {
|
|
38205
|
+
return this.serialize(async () => {
|
|
38206
|
+
if (this.closed) throw failure3("UPLOAD_CLOSED");
|
|
38207
|
+
await this.expire();
|
|
38208
|
+
try {
|
|
38209
|
+
switch (method) {
|
|
38210
|
+
case "workspace.upload.open":
|
|
38211
|
+
return await this.begin(input3);
|
|
38212
|
+
case "workspace.upload.chunk":
|
|
38213
|
+
return await this.append(input3);
|
|
38214
|
+
case "workspace.upload.commit":
|
|
38215
|
+
return await this.commit(input3);
|
|
38216
|
+
case "workspace.upload.abort": {
|
|
38217
|
+
const p = scope.parse(input3);
|
|
38218
|
+
const upload = this.uploads.get(p.uploadId);
|
|
38219
|
+
if (upload && upload.sessionId !== p.sessionId) throw failure3("UPLOAD_NOT_FOUND");
|
|
38220
|
+
if (upload) await this.discard(p.uploadId, upload);
|
|
38221
|
+
return { aborted: true };
|
|
38222
|
+
}
|
|
38223
|
+
default:
|
|
38224
|
+
throw failure3("METHOD_NOT_FOUND");
|
|
38225
|
+
}
|
|
38226
|
+
} catch (error) {
|
|
38227
|
+
const p = scope.safeParse(input3 && typeof input3 === "object" ? { sessionId: input3.sessionId, uploadId: input3.uploadId } : input3);
|
|
38228
|
+
const upload = p.success ? this.uploads.get(p.data.uploadId) : void 0;
|
|
38229
|
+
if (p.success && upload?.sessionId === p.data.sessionId) await this.discard(p.data.uploadId, upload);
|
|
38230
|
+
if (error instanceof external_exports.ZodError || error instanceof RpcError) throw error;
|
|
38231
|
+
throw failure3("UPLOAD_FAILED");
|
|
38232
|
+
}
|
|
38233
|
+
});
|
|
38234
|
+
}
|
|
38235
|
+
closeAll() {
|
|
38236
|
+
this.closed = true;
|
|
38237
|
+
this.lifetime.abort();
|
|
38238
|
+
clearInterval(this.timer);
|
|
38239
|
+
return this.serialize(async () => {
|
|
38240
|
+
await Promise.all([...this.uploads].map(([id5, upload]) => this.discard(id5, upload)));
|
|
38241
|
+
this.completed.clear();
|
|
38242
|
+
});
|
|
38243
|
+
}
|
|
38244
|
+
serialize(operation) {
|
|
38245
|
+
const next = this.queue.then(operation);
|
|
38246
|
+
this.queue = next.catch(() => void 0);
|
|
38247
|
+
return next;
|
|
38248
|
+
}
|
|
38249
|
+
async begin(input3) {
|
|
38250
|
+
const p = openParams.parse(input3);
|
|
38251
|
+
if (!validName(p.name)) throw failure3("UPLOAD_INVALID_NAME");
|
|
38252
|
+
if (this.uploads.has(p.uploadId) || this.completed.has(p.uploadId)) throw failure3("UPLOAD_ID_IN_USE");
|
|
38253
|
+
if (this.uploads.size >= 2) throw failure3("UPLOAD_LIMIT");
|
|
38254
|
+
const sourceRoot = await this.sessionRoot(p.sessionId);
|
|
38255
|
+
this.assertLive();
|
|
38256
|
+
if (!sourceRoot || !isAbsolute7(sourceRoot)) throw failure3("UPLOAD_WORKSPACE_UNAVAILABLE");
|
|
38257
|
+
const root = await realpath6(sourceRoot);
|
|
38258
|
+
const directory = await stat6(root);
|
|
38259
|
+
if (!directory.isDirectory()) throw failure3("UPLOAD_WORKSPACE_UNAVAILABLE");
|
|
38260
|
+
const temp = join12(root, `.dsh-upload-${randomUUID5()}.part`);
|
|
38261
|
+
const handle = await open2(temp, constants4.O_CREAT | constants4.O_EXCL | constants4.O_RDWR | (constants4.O_NOFOLLOW ?? 0), 384);
|
|
38262
|
+
const upload = { ...p, received: 0, touched: Date.now(), root, sourceRoot, dev: directory.dev, ino: directory.ino, temp, handle };
|
|
38263
|
+
this.uploads.set(p.uploadId, upload);
|
|
38264
|
+
await this.validateRoot(upload);
|
|
38265
|
+
return { uploadId: p.uploadId, chunkBytes: WORKSPACE_UPLOAD_CHUNK };
|
|
38266
|
+
}
|
|
38267
|
+
async append(input3) {
|
|
38268
|
+
const p = chunkParams.parse(input3);
|
|
38269
|
+
const upload = this.requireUpload(p);
|
|
38270
|
+
const data2 = Buffer.from(p.data, "base64");
|
|
38271
|
+
if (!data2.length || data2.length > WORKSPACE_UPLOAD_CHUNK || data2.toString("base64") !== p.data || p.offset !== upload.received || upload.received + data2.length > upload.totalBytes) throw failure3("UPLOAD_INVALID_CHUNK");
|
|
38272
|
+
await this.validateRoot(upload);
|
|
38273
|
+
let written = 0;
|
|
38274
|
+
while (written < data2.length) {
|
|
38275
|
+
const result = await upload.handle.write(data2, written, data2.length - written, upload.received + written);
|
|
38276
|
+
if (!result.bytesWritten) throw failure3("UPLOAD_FAILED");
|
|
38277
|
+
written += result.bytesWritten;
|
|
38278
|
+
}
|
|
38279
|
+
this.assertLive();
|
|
38280
|
+
upload.received += data2.length;
|
|
38281
|
+
upload.touched = Date.now();
|
|
38282
|
+
return { uploadId: p.uploadId, receivedBytes: upload.received };
|
|
38283
|
+
}
|
|
38284
|
+
async commit(input3) {
|
|
38285
|
+
const p = scope.parse(input3);
|
|
38286
|
+
const prior = this.completed.get(p.uploadId);
|
|
38287
|
+
if (prior) {
|
|
38288
|
+
if (prior.sessionId !== p.sessionId) throw failure3("UPLOAD_NOT_FOUND");
|
|
38289
|
+
return prior.result;
|
|
38290
|
+
}
|
|
38291
|
+
const upload = this.requireUpload(p);
|
|
38292
|
+
if (upload.received !== upload.totalBytes) throw failure3("UPLOAD_INCOMPLETE");
|
|
38293
|
+
await this.validateRoot(upload);
|
|
38294
|
+
await upload.handle.sync();
|
|
38295
|
+
const original = await upload.handle.stat();
|
|
38296
|
+
const temp = await lstat2(upload.temp);
|
|
38297
|
+
if (!temp.isFile() || temp.dev !== original.dev || temp.ino !== original.ino || temp.size !== upload.totalBytes) throw failure3("UPLOAD_WORKSPACE_CHANGED");
|
|
38298
|
+
const extension = extname(upload.name);
|
|
38299
|
+
const stem = upload.name.slice(0, upload.name.length - extension.length);
|
|
38300
|
+
for (let suffix = 0; suffix < 1e4; suffix++) {
|
|
38301
|
+
await this.validateRoot(upload);
|
|
38302
|
+
const name2 = suffix === 0 ? upload.name : `${stem} (${suffix})${extension}`;
|
|
38303
|
+
try {
|
|
38304
|
+
await link(upload.temp, join12(upload.root, name2));
|
|
38305
|
+
} catch (error) {
|
|
38306
|
+
if (error.code === "EEXIST") continue;
|
|
38307
|
+
throw error;
|
|
38308
|
+
}
|
|
38309
|
+
const result = { name: name2, path: `./${name2}`, bytes: upload.totalBytes };
|
|
38310
|
+
this.completed.set(p.uploadId, { sessionId: p.sessionId, result, touched: Date.now() });
|
|
38311
|
+
if (this.completed.size > 64) this.completed.delete(this.completed.keys().next().value);
|
|
38312
|
+
await this.discard(p.uploadId, upload);
|
|
38313
|
+
return result;
|
|
38314
|
+
}
|
|
38315
|
+
throw failure3("UPLOAD_NAME_CONFLICT");
|
|
38316
|
+
}
|
|
38317
|
+
requireUpload(p) {
|
|
38318
|
+
const upload = this.uploads.get(p.uploadId);
|
|
38319
|
+
if (!upload || upload.sessionId !== p.sessionId) throw failure3("UPLOAD_NOT_FOUND");
|
|
38320
|
+
return upload;
|
|
38321
|
+
}
|
|
38322
|
+
assertLive() {
|
|
38323
|
+
if (this.closed) throw failure3("UPLOAD_CLOSED");
|
|
38324
|
+
}
|
|
38325
|
+
async sessionRoot(sessionId) {
|
|
38326
|
+
const signal = AbortSignal.any([this.lifetime.signal, AbortSignal.timeout(15e3)]);
|
|
38327
|
+
let onAbort;
|
|
38328
|
+
try {
|
|
38329
|
+
signal.throwIfAborted();
|
|
38330
|
+
return await Promise.race([
|
|
38331
|
+
this.resolveRoot(sessionId, signal),
|
|
38332
|
+
new Promise((_, reject) => {
|
|
38333
|
+
onAbort = () => reject(failure3(this.closed ? "UPLOAD_CLOSED" : "UPLOAD_TIMEOUT"));
|
|
38334
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
38335
|
+
if (signal.aborted) onAbort();
|
|
38336
|
+
})
|
|
38337
|
+
]);
|
|
38338
|
+
} finally {
|
|
38339
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
38340
|
+
}
|
|
38341
|
+
}
|
|
38342
|
+
async validateRoot(upload) {
|
|
38343
|
+
const current = await this.sessionRoot(upload.sessionId);
|
|
38344
|
+
this.assertLive();
|
|
38345
|
+
if (current !== upload.sourceRoot || await realpath6(current) !== upload.root || await realpath6(upload.root) !== upload.root) throw failure3("UPLOAD_WORKSPACE_CHANGED");
|
|
38346
|
+
const directory = await stat6(upload.root);
|
|
38347
|
+
if (!directory.isDirectory() || directory.dev !== upload.dev || directory.ino !== upload.ino) throw failure3("UPLOAD_WORKSPACE_CHANGED");
|
|
38348
|
+
}
|
|
38349
|
+
async discard(id5, upload) {
|
|
38350
|
+
this.uploads.delete(id5);
|
|
38351
|
+
const original = await upload.handle.stat().catch(() => void 0);
|
|
38352
|
+
await upload.handle.close().catch(() => void 0);
|
|
38353
|
+
try {
|
|
38354
|
+
const root = await lstat2(upload.root);
|
|
38355
|
+
const temp = await lstat2(upload.temp);
|
|
38356
|
+
if (original && root.isDirectory() && root.dev === upload.dev && root.ino === upload.ino && temp.isFile() && temp.dev === original.dev && temp.ino === original.ino) await unlink(upload.temp);
|
|
38357
|
+
} catch {
|
|
38358
|
+
}
|
|
38359
|
+
}
|
|
38360
|
+
async expire() {
|
|
38361
|
+
const cutoff = Date.now() - this.ttlMs;
|
|
38362
|
+
await Promise.all([...this.uploads].filter(([, u]) => u.touched <= cutoff).map(([id5, u]) => this.discard(id5, u)));
|
|
38363
|
+
for (const [id5, entry] of this.completed) if (entry.touched <= cutoff) this.completed.delete(id5);
|
|
38364
|
+
}
|
|
38365
|
+
};
|
|
38366
|
+
function failure3(code) {
|
|
38367
|
+
return new RpcError(code, "The workspace upload could not be completed.");
|
|
38368
|
+
}
|
|
38369
|
+
function validName(name2) {
|
|
38370
|
+
return Buffer.byteLength(name2, "utf8") <= 220 && name2 !== "." && name2 !== ".." && !/[/\\\x00-\x1f\x7f<>:"|?*]/u.test(name2) && !/[. ]$/u.test(name2) && !/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(name2) && !name2.startsWith(".dsh-upload-");
|
|
38371
|
+
}
|
|
38372
|
+
|
|
38373
|
+
// src/workspace-upload-authority.ts
|
|
38374
|
+
function workspaceUploadRootResolver(gateway, supportsHarnessSessions, codex, acp) {
|
|
38375
|
+
return async (sessionId, signal) => {
|
|
38376
|
+
signal.throwIfAborted();
|
|
38377
|
+
if (sessionId.startsWith("codex:")) {
|
|
38378
|
+
const parsed = parseCodexSessionId(sessionId);
|
|
38379
|
+
if (!parsed) throw unavailable2();
|
|
38380
|
+
return codex(parsed.threadId);
|
|
38381
|
+
}
|
|
38382
|
+
if (/^(acp|cursor|pi|opencode|claude):/.test(sessionId)) return acp(sessionId);
|
|
38383
|
+
if (!gateway?.supportsCarrier || !supportsHarnessSessions()) throw unavailable2();
|
|
38384
|
+
const result = await gateway.dispatch("session/list", { args: { _request: {} } }, signal);
|
|
38385
|
+
signal.throwIfAborted();
|
|
38386
|
+
const value = result.ok ? result.value : void 0;
|
|
38387
|
+
return value?.items?.find((item) => item.sessionId === sessionId)?.cwd;
|
|
38388
|
+
};
|
|
38389
|
+
}
|
|
38390
|
+
function unavailable2() {
|
|
38391
|
+
return new RpcError("UPLOAD_WORKSPACE_UNAVAILABLE", "The session workspace is unavailable.");
|
|
38392
|
+
}
|
|
38393
|
+
|
|
38394
|
+
// src/service.ts
|
|
38395
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
37871
38396
|
|
|
37872
38397
|
// src/connection-controller.ts
|
|
37873
38398
|
var ConnectionController = class {
|
|
@@ -38098,7 +38623,11 @@ var apiMethods = /* @__PURE__ */ new Set([
|
|
|
38098
38623
|
"agent.acp.transfer.commit",
|
|
38099
38624
|
"agent.acp.transfer.read",
|
|
38100
38625
|
"agent.acp.transfer.close",
|
|
38101
|
-
"plugin.route.call"
|
|
38626
|
+
"plugin.route.call",
|
|
38627
|
+
"workspace.upload.open",
|
|
38628
|
+
"workspace.upload.chunk",
|
|
38629
|
+
"workspace.upload.commit",
|
|
38630
|
+
"workspace.upload.abort"
|
|
38102
38631
|
]);
|
|
38103
38632
|
var HOST_CAPABILITIES = [
|
|
38104
38633
|
"harness.api.v1",
|
|
@@ -38106,13 +38635,14 @@ var HOST_CAPABILITIES = [
|
|
|
38106
38635
|
"harness.remote.v1",
|
|
38107
38636
|
"harness.remote.transfer.v1",
|
|
38108
38637
|
"fileviewer.read.v1",
|
|
38638
|
+
"workspace.upload.v1",
|
|
38109
38639
|
"codex.appserver.v1",
|
|
38110
38640
|
"codex.appserver.transfer.v1",
|
|
38111
38641
|
"agent.acp.v1",
|
|
38112
38642
|
"agent.acp.transfer.v1"
|
|
38113
38643
|
];
|
|
38114
38644
|
var RpcRouter = class {
|
|
38115
|
-
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex, acp, loopback, workspaceTypes, pluginRoute) {
|
|
38645
|
+
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex, acp, loopback, workspaceTypes, pluginRoute, workspaceUpload) {
|
|
38116
38646
|
this.harnessApi = harnessApi;
|
|
38117
38647
|
this.maxPending = maxPending;
|
|
38118
38648
|
this.logger = logger;
|
|
@@ -38124,6 +38654,7 @@ var RpcRouter = class {
|
|
|
38124
38654
|
this.loopback = loopback;
|
|
38125
38655
|
this.workspaceTypes = workspaceTypes;
|
|
38126
38656
|
this.pluginRoute = pluginRoute;
|
|
38657
|
+
this.workspaceUpload = workspaceUpload;
|
|
38127
38658
|
}
|
|
38128
38659
|
active = 0;
|
|
38129
38660
|
async closePeerStreams() {
|
|
@@ -38132,7 +38663,8 @@ var RpcRouter = class {
|
|
|
38132
38663
|
this.harnessApi?.closeAll(),
|
|
38133
38664
|
this.harnessRemote?.closeAll(),
|
|
38134
38665
|
this.codex?.closeAll(),
|
|
38135
|
-
this.acp?.closeAll()
|
|
38666
|
+
this.acp?.closeAll(),
|
|
38667
|
+
this.workspaceUpload?.closeAll()
|
|
38136
38668
|
]);
|
|
38137
38669
|
}
|
|
38138
38670
|
async handle(message) {
|
|
@@ -38161,7 +38693,7 @@ var RpcRouter = class {
|
|
|
38161
38693
|
return createRpcResponse(request.id, result);
|
|
38162
38694
|
} catch (error) {
|
|
38163
38695
|
const response = errorResponse2(request.id, error);
|
|
38164
|
-
const failedEndpoint = typeof request.payload.params?.endpoint === "string" ? String(request.payload.params.endpoint) : void 0;
|
|
38696
|
+
const failedEndpoint = !request.payload.method.startsWith("workspace.upload.") && typeof request.payload.params?.endpoint === "string" ? String(request.payload.params.endpoint) : void 0;
|
|
38165
38697
|
this.logger?.warn("host rpc failed", {
|
|
38166
38698
|
method: request.payload.method,
|
|
38167
38699
|
...failedEndpoint ? { endpoint: failedEndpoint } : {},
|
|
@@ -38272,6 +38804,13 @@ var RpcRouter = class {
|
|
|
38272
38804
|
return this.requireAcp().readTransfer(params);
|
|
38273
38805
|
case "agent.acp.transfer.close":
|
|
38274
38806
|
return this.requireAcp().closeTransfer(params);
|
|
38807
|
+
case "workspace.upload.open":
|
|
38808
|
+
case "workspace.upload.chunk":
|
|
38809
|
+
case "workspace.upload.commit":
|
|
38810
|
+
case "workspace.upload.abort": {
|
|
38811
|
+
if (!this.workspaceUpload) throw new RpcError("FEATURE_NOT_SUPPORTED", "Workspace upload is unavailable on this Host.");
|
|
38812
|
+
return this.workspaceUpload.call(method, params);
|
|
38813
|
+
}
|
|
38275
38814
|
case "plugin.route.call":
|
|
38276
38815
|
return this.requirePluginRoute().call(params);
|
|
38277
38816
|
default:
|
|
@@ -39357,22 +39896,22 @@ function closeCode(code) {
|
|
|
39357
39896
|
}
|
|
39358
39897
|
|
|
39359
39898
|
// src/remote-directory-browser.ts
|
|
39360
|
-
import { readdir as readdir4, stat as
|
|
39899
|
+
import { readdir as readdir4, stat as stat7 } from "node:fs/promises";
|
|
39361
39900
|
import { homedir as homedir6, platform as platform2 } from "node:os";
|
|
39362
|
-
import { basename as basename5, dirname as dirname5, isAbsolute as
|
|
39901
|
+
import { basename as basename5, dirname as dirname5, isAbsolute as isAbsolute8, parse, resolve as resolve5 } from "node:path";
|
|
39363
39902
|
var MAX_ENTRIES = 500;
|
|
39364
39903
|
async function listRemoteDirectory(path, signal) {
|
|
39365
39904
|
signal?.throwIfAborted();
|
|
39366
39905
|
const home = resolve5(homedir6());
|
|
39367
39906
|
const target2 = path === void 0 || path.trim() === "" ? home : resolve5(path);
|
|
39368
|
-
if (!
|
|
39907
|
+
if (!isAbsolute8(target2)) throw new Error("The remote directory path must be absolute.");
|
|
39369
39908
|
const rows = await readdir4(target2, { withFileTypes: true });
|
|
39370
39909
|
const directories = [];
|
|
39371
39910
|
for (const row of rows) {
|
|
39372
39911
|
signal?.throwIfAborted();
|
|
39373
39912
|
const child = resolve5(target2, row.name);
|
|
39374
39913
|
let directory = row.isDirectory();
|
|
39375
|
-
if (!directory && row.isSymbolicLink()) directory = await
|
|
39914
|
+
if (!directory && row.isSymbolicLink()) directory = await stat7(child).then((value) => value.isDirectory()).catch(() => false);
|
|
39376
39915
|
if (!directory) continue;
|
|
39377
39916
|
directories.push({ name: row.name, path: child, hidden: platform2() !== "win32" && row.name.startsWith(".") });
|
|
39378
39917
|
}
|
|
@@ -40584,7 +41123,7 @@ var HarnessRemoteBridge = class {
|
|
|
40584
41123
|
}
|
|
40585
41124
|
async pump(streamId, source, signal) {
|
|
40586
41125
|
let reason = "completed";
|
|
40587
|
-
let
|
|
41126
|
+
let failure4;
|
|
40588
41127
|
try {
|
|
40589
41128
|
for await (const value of source) {
|
|
40590
41129
|
if (signal.aborted) break;
|
|
@@ -40601,13 +41140,13 @@ var HarnessRemoteBridge = class {
|
|
|
40601
41140
|
if (signal.aborted) reason = "cancelled";
|
|
40602
41141
|
} catch (error) {
|
|
40603
41142
|
reason = signal.aborted ? "cancelled" : "failed";
|
|
40604
|
-
if (!signal.aborted)
|
|
41143
|
+
if (!signal.aborted) failure4 = this.gateway.failure(error);
|
|
40605
41144
|
} finally {
|
|
40606
41145
|
this.streams.delete(streamId);
|
|
40607
41146
|
await this.publish("harness.remote.stream.closed", {
|
|
40608
41147
|
streamId,
|
|
40609
41148
|
reason,
|
|
40610
|
-
...
|
|
41149
|
+
...failure4 === void 0 ? {} : { failure: failure4 }
|
|
40611
41150
|
}).catch(() => void 0);
|
|
40612
41151
|
}
|
|
40613
41152
|
}
|
|
@@ -40816,7 +41355,13 @@ var HostPluginRuntime = class {
|
|
|
40816
41355
|
// Handles and their lifetime belong to this connection; only policy is shared.
|
|
40817
41356
|
this.createLoopbackHost(),
|
|
40818
41357
|
() => this.hostWorkspaceTypes(),
|
|
40819
|
-
pluginRoute
|
|
41358
|
+
pluginRoute,
|
|
41359
|
+
new WorkspaceUploadHost(workspaceUploadRootResolver(
|
|
41360
|
+
peerGateway,
|
|
41361
|
+
() => harnessSessionGeneration(this.harnessVersion) === "v3",
|
|
41362
|
+
(threadId) => this.codex.resolveThreadWorkspace(context.connectionId, threadId),
|
|
41363
|
+
(sessionId) => this.acp.resolveUploadWorkspace(context.connectionId, sessionId)
|
|
41364
|
+
))
|
|
40820
41365
|
);
|
|
40821
41366
|
}, this.logger);
|
|
40822
41367
|
if (config.serverUrl !== void 0) {
|
|
@@ -41117,7 +41662,7 @@ var HostPluginRuntime = class {
|
|
|
41117
41662
|
let reportedVersion;
|
|
41118
41663
|
let errorCode6;
|
|
41119
41664
|
try {
|
|
41120
|
-
const response = await this.apiProxy?.host.describe({ rpcId:
|
|
41665
|
+
const response = await this.apiProxy?.host.describe({ rpcId: randomUUID6(), payload: {} });
|
|
41121
41666
|
if (response === void 0) throw new Error("ApiProxy is unavailable");
|
|
41122
41667
|
if (!response.result.ok) {
|
|
41123
41668
|
errorCode6 = response.result.error.code;
|
|
@@ -41133,7 +41678,7 @@ var HostPluginRuntime = class {
|
|
|
41133
41678
|
return void 0;
|
|
41134
41679
|
}
|
|
41135
41680
|
hostCapabilities() {
|
|
41136
|
-
const capabilities = [];
|
|
41681
|
+
const capabilities = ["workspace.upload.v1"];
|
|
41137
41682
|
if (this.loopbackPorts.length > 0) capabilities.push("loopback.http-ws.v1");
|
|
41138
41683
|
if (this.localGateway?.supportsCarrier === true) {
|
|
41139
41684
|
capabilities.push(
|
|
@@ -41290,9 +41835,9 @@ function withSignal(pending, signal) {
|
|
|
41290
41835
|
}
|
|
41291
41836
|
|
|
41292
41837
|
// src/cli.ts
|
|
41293
|
-
import { stat as
|
|
41838
|
+
import { stat as stat8 } from "node:fs/promises";
|
|
41294
41839
|
import { hostname as hostname3 } from "node:os";
|
|
41295
|
-
import { join as
|
|
41840
|
+
import { join as join13 } from "node:path";
|
|
41296
41841
|
var QR_POLL_INTERVAL_MS = 2e3;
|
|
41297
41842
|
var TERMINAL_QR_MARGIN = 4;
|
|
41298
41843
|
async function runCli(args = process.argv.slice(2), dependencies = {}) {
|
|
@@ -41383,7 +41928,7 @@ async function status(args, runtime) {
|
|
|
41383
41928
|
`Server: ${serverUrl}`,
|
|
41384
41929
|
"Host control: enabled (dsh-TUI default)"
|
|
41385
41930
|
];
|
|
41386
|
-
if (!await exists3(
|
|
41931
|
+
if (!await exists3(join13(directory, "device.json"))) {
|
|
41387
41932
|
lines.push("Device: not initialized", "Authorization: logged out", "Credential: unavailable");
|
|
41388
41933
|
write(runtime.stdout, `${lines.join("\n")}
|
|
41389
41934
|
|
|
@@ -41428,7 +41973,7 @@ async function logout(args, runtime) {
|
|
|
41428
41973
|
const serverUrl = selectedServer();
|
|
41429
41974
|
const root = new IdentityStore({ env: runtime.env }).directory;
|
|
41430
41975
|
const directory = serverStorageDirectory(root, serverUrl, "host");
|
|
41431
|
-
if (!await exists3(
|
|
41976
|
+
if (!await exists3(join13(directory, "device.json"))) {
|
|
41432
41977
|
await new ServerCredentialStore(directory).clear();
|
|
41433
41978
|
write(runtime.stdout, "This Host is already logged out.\n");
|
|
41434
41979
|
return 0;
|
|
@@ -41554,7 +42099,7 @@ function terminalLink(url, target2) {
|
|
|
41554
42099
|
}
|
|
41555
42100
|
async function exists3(path) {
|
|
41556
42101
|
try {
|
|
41557
|
-
await
|
|
42102
|
+
await stat8(path);
|
|
41558
42103
|
return true;
|
|
41559
42104
|
} catch (error) {
|
|
41560
42105
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
@@ -42273,18 +42818,18 @@ async function createSettingsBinding(ctx, settings, readConfig, entryId) {
|
|
|
42273
42818
|
}
|
|
42274
42819
|
};
|
|
42275
42820
|
}
|
|
42276
|
-
const
|
|
42821
|
+
const scope2 = settings.register(pluginSettingsNamespace, Config, {
|
|
42277
42822
|
base: readConfig(),
|
|
42278
42823
|
applies: "restart",
|
|
42279
42824
|
validate: (value) => {
|
|
42280
42825
|
resolveConfig(value);
|
|
42281
42826
|
}
|
|
42282
42827
|
});
|
|
42283
|
-
reportSettingsMigration(ctx, await migrateLegacySettings(settings,
|
|
42828
|
+
reportSettingsMigration(ctx, await migrateLegacySettings(settings, scope2));
|
|
42284
42829
|
return {
|
|
42285
|
-
get: () =>
|
|
42830
|
+
get: () => scope2.get(),
|
|
42286
42831
|
replace: async (section) => {
|
|
42287
|
-
await
|
|
42832
|
+
await scope2.replace(section);
|
|
42288
42833
|
}
|
|
42289
42834
|
};
|
|
42290
42835
|
}
|