@skylandnpm/octopus-proxy 0.1.72 → 0.1.74
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/{chunk-CGHUM2TC.js → chunk-JUUJGAHF.js} +186 -31
- package/dist/cli.js +1 -1
- package/dist/main.js +1 -1
- package/package.json +1 -1
|
@@ -11034,6 +11034,24 @@ var UNSUPPORTED = /* @__PURE__ */ new Set([
|
|
|
11034
11034
|
"runtime.restart",
|
|
11035
11035
|
"runtime.logs"
|
|
11036
11036
|
]);
|
|
11037
|
+
var SESSION_ID = /^ses_[A-Za-z0-9]+$/;
|
|
11038
|
+
var PERMISSION_ID = /^per_[A-Za-z0-9]+$/;
|
|
11039
|
+
var OpenCodeHttpError = class extends Error {
|
|
11040
|
+
constructor(status, body) {
|
|
11041
|
+
super(`OpenCode ${status}: ${body}`);
|
|
11042
|
+
this.status = status;
|
|
11043
|
+
this.body = body;
|
|
11044
|
+
}
|
|
11045
|
+
};
|
|
11046
|
+
function isPermissionMetadataError(error) {
|
|
11047
|
+
if (!(error instanceof OpenCodeHttpError) || error.status !== 400) return false;
|
|
11048
|
+
try {
|
|
11049
|
+
const body = JSON.parse(error.body);
|
|
11050
|
+
return body?._tag === "InvalidRequestError" && body.kind === "Body" && typeof body.message === "string" && /^Expected JSON value\s+at \["data"\]\[\d+\]\["metadata"\]\["[^"]+"\]$/.test(body.message);
|
|
11051
|
+
} catch {
|
|
11052
|
+
return false;
|
|
11053
|
+
}
|
|
11054
|
+
}
|
|
11037
11055
|
var OpenCodeAdapter = class {
|
|
11038
11056
|
constructor(runtime, workspace, uploads, emit) {
|
|
11039
11057
|
this.runtime = runtime;
|
|
@@ -11050,7 +11068,9 @@ var OpenCodeAdapter = class {
|
|
|
11050
11068
|
streams = /* @__PURE__ */ new Map();
|
|
11051
11069
|
terminals = /* @__PURE__ */ new Map();
|
|
11052
11070
|
terminalOutput = /* @__PURE__ */ new Map();
|
|
11071
|
+
permissionEvents = /* @__PURE__ */ new Map();
|
|
11053
11072
|
resolving = /* @__PURE__ */ new Set();
|
|
11073
|
+
approveAll = /* @__PURE__ */ new Set();
|
|
11054
11074
|
config;
|
|
11055
11075
|
media;
|
|
11056
11076
|
async request(project, method, path, body, signal) {
|
|
@@ -11072,7 +11092,7 @@ var OpenCodeAdapter = class {
|
|
|
11072
11092
|
}
|
|
11073
11093
|
throw error;
|
|
11074
11094
|
});
|
|
11075
|
-
if (!response.ok) throw new
|
|
11095
|
+
if (!response.ok) throw new OpenCodeHttpError(response.status, (await response.text()).slice(0, 1500));
|
|
11076
11096
|
if (response.status === 204) return null;
|
|
11077
11097
|
const text = await response.text();
|
|
11078
11098
|
return text ? JSON.parse(text) : null;
|
|
@@ -11081,7 +11101,7 @@ var OpenCodeAdapter = class {
|
|
|
11081
11101
|
return unwrap2(await this.request(project, method, path, body, signal));
|
|
11082
11102
|
}
|
|
11083
11103
|
async session(project, id) {
|
|
11084
|
-
if (
|
|
11104
|
+
if (!SESSION_ID.test(id)) throw new Error("Invalid session id");
|
|
11085
11105
|
const session = await this.data(project, "GET", `/api/session/${encodeURIComponent(id)}`);
|
|
11086
11106
|
const directory = session?.location?.directory ?? session?.directory;
|
|
11087
11107
|
if (typeof directory !== "string" || await realpath4(directory) !== await this.workspace.root(project)) throw new Error("Session belongs to another project");
|
|
@@ -11099,22 +11119,27 @@ var OpenCodeAdapter = class {
|
|
|
11099
11119
|
const op = command.operation, p = command.payload, id = command.sessionId;
|
|
11100
11120
|
if (op.startsWith("auth.")) return this.authentication(project, op, p);
|
|
11101
11121
|
const needsSession = op.startsWith("sessions.") && !["sessions.list", "sessions.create"].includes(op);
|
|
11122
|
+
let session;
|
|
11102
11123
|
if (needsSession) {
|
|
11103
11124
|
if (!id) throw new Error("Session required");
|
|
11104
|
-
await this.session(project, id);
|
|
11125
|
+
session = await this.session(project, id);
|
|
11105
11126
|
}
|
|
11106
11127
|
const base = `/api/session/${encodeURIComponent(id ?? "")}`;
|
|
11107
11128
|
if (op === "configuration.permissionModes") return { defaultMode: "ask", modes: [
|
|
11108
11129
|
{ id: "ask", label: "\u8BF7\u6C42\u6279\u51C6", description: "\u6BCF\u6B21\u4F7F\u7528\u5DE5\u5177\u524D\u8BF7\u6C42\u6279\u51C6" },
|
|
11109
11130
|
{ id: "full", label: "\u5B8C\u5168\u8BBF\u95EE", description: "\u81EA\u52A8\u5141\u8BB8\u5DE5\u5177\u64CD\u4F5C\uFF1B\u4ECD\u9075\u5B88\u4E3B\u673A\u548C\u5DE5\u5177\u81EA\u8EAB\u7684\u8BBF\u95EE\u9650\u5236" }
|
|
11110
11131
|
] };
|
|
11111
|
-
if (
|
|
11132
|
+
if (id && ["sessions.prompt", "sessions.command", "sessions.shell", "sessions.abort", "sessions.delete"].includes(op)) this.approveAll.delete(`${project}\0${id}`);
|
|
11133
|
+
if (op === "sessions.prompt") return this.prompt(project, id, p, base, session?.model);
|
|
11112
11134
|
if (op === "sessions.command" || op === "sessions.shell") {
|
|
11113
11135
|
await this.switchSelection(project, base, p);
|
|
11114
11136
|
if (op === "sessions.shell") return this.data(project, "POST", `${base}/shell`, { ...p.id ? { id: p.id } : {}, command: p.command });
|
|
11115
11137
|
return this.data(project, "POST", `${base}/command`, { name: p.name ?? p.command, text: p.text ?? p.arguments ?? "", delivery: "queue", ...Array.isArray(p.files) ? { files: p.files } : {}, ...Array.isArray(p.agents) ? { agents: p.agents } : {}, ...Array.isArray(p.skills) ? { skills: p.skills } : {} });
|
|
11116
11138
|
}
|
|
11117
|
-
if (op === "permissions.list")
|
|
11139
|
+
if (op === "permissions.list") {
|
|
11140
|
+
if (id) await this.session(project, id);
|
|
11141
|
+
return this.permissions(project, id);
|
|
11142
|
+
}
|
|
11118
11143
|
if (op === "questions.list") return this.forms(project, id);
|
|
11119
11144
|
if (op === "permissions.reply" || op === "questions.reply" || op === "questions.reject") return this.resolveInteraction(project, id, op, p);
|
|
11120
11145
|
if (op === "configuration.models") return this.models(project);
|
|
@@ -11163,18 +11188,32 @@ var OpenCodeAdapter = class {
|
|
|
11163
11188
|
if (op.startsWith("sessions.")) return this.media.normalize(project, normalizeSession(result));
|
|
11164
11189
|
return result;
|
|
11165
11190
|
}
|
|
11166
|
-
async prompt(project, session, p, base) {
|
|
11191
|
+
async prompt(project, session, p, base, sessionModel) {
|
|
11167
11192
|
const text = typeof p.text === "string" ? screenshotPrompt(p.text) : "";
|
|
11168
11193
|
const files = [];
|
|
11194
|
+
let hasImage = false;
|
|
11169
11195
|
for (const uploadId of Array.isArray(p.attachments) ? p.attachments : []) {
|
|
11170
11196
|
const upload = await this.uploads.attachment(project, String(uploadId), session);
|
|
11171
11197
|
files.push({ uri: pathToFileURL(await this.workspace.path(project, upload.destination)).href, name: upload.name });
|
|
11198
|
+
hasImage ||= upload.mime.startsWith("image/");
|
|
11172
11199
|
}
|
|
11173
11200
|
if (!text.trim() && !files.length) throw new Error("Message or attachments required");
|
|
11174
|
-
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11201
|
+
const model = this.modelSelection(p);
|
|
11202
|
+
if (hasImage) await this.checkImageInput(project, model ?? sessionModel);
|
|
11203
|
+
await this.switchSelection(project, base, p, model);
|
|
11204
|
+
const admitted = await this.data(project, "POST", `${base}/prompt`, { text, files, delivery: "queue", ...p.messageID ? { id: p.messageID } : {} });
|
|
11205
|
+
return typeof admitted?.id === "string" ? { id: admitted.id } : null;
|
|
11206
|
+
}
|
|
11207
|
+
async checkImageInput(project, model) {
|
|
11208
|
+
if (typeof model?.providerID !== "string" || typeof model.id !== "string") return;
|
|
11209
|
+
const catalog = await this.data(project, "GET", "/api/model", void 0, AbortSignal.timeout(2e3)).catch(() => null);
|
|
11210
|
+
const entry = Array.isArray(catalog) ? catalog.find((item) => item.providerID === model.providerID && (item.id ?? item.modelID) === model.id) : void 0;
|
|
11211
|
+
const input = entry?.capabilities?.input;
|
|
11212
|
+
if (Array.isArray(input) && !input.includes("image")) {
|
|
11213
|
+
throw new Error(`OpenCode \u6A21\u578B ${model.providerID}/${model.id} \u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C\u8BF7\u5207\u6362\u6A21\u578B\u6216\u68C0\u67E5\u8BE5\u6A21\u578B\u7684\u8F93\u5165\u914D\u7F6E\u3002`);
|
|
11214
|
+
}
|
|
11215
|
+
}
|
|
11216
|
+
async switchSelection(project, base, payload, model = this.modelSelection(payload)) {
|
|
11178
11217
|
if (payload.permissionMode !== void 0) {
|
|
11179
11218
|
if (!["ask", "full"].includes(String(payload.permissionMode))) throw new Error("Unsupported permission mode");
|
|
11180
11219
|
const states = await this.data(project, "GET", "/api/session/active");
|
|
@@ -11183,6 +11222,9 @@ var OpenCodeAdapter = class {
|
|
|
11183
11222
|
await this.data(project, "PATCH", base, { permissions: [{ action: "*", resource: "*", effect: payload.permissionMode === "full" ? "allow" : "ask" }] });
|
|
11184
11223
|
}
|
|
11185
11224
|
if (payload.agent) await this.data(project, "POST", `${base}/agent`, { agent: payload.agent });
|
|
11225
|
+
if (model) await this.data(project, "POST", `${base}/model`, { model });
|
|
11226
|
+
}
|
|
11227
|
+
modelSelection(payload) {
|
|
11186
11228
|
if (!payload.model) return;
|
|
11187
11229
|
let model;
|
|
11188
11230
|
if (typeof payload.model === "string") {
|
|
@@ -11195,7 +11237,7 @@ var OpenCodeAdapter = class {
|
|
|
11195
11237
|
} else throw new Error("Invalid model identifier");
|
|
11196
11238
|
const level = selectedReasoningLevel(payload, "model.variant");
|
|
11197
11239
|
if (level) model.variant = level;
|
|
11198
|
-
|
|
11240
|
+
return model;
|
|
11199
11241
|
}
|
|
11200
11242
|
async models(project) {
|
|
11201
11243
|
const models = await this.data(project, "GET", "/api/model");
|
|
@@ -11214,8 +11256,34 @@ var OpenCodeAdapter = class {
|
|
|
11214
11256
|
return redact({ providers: [...providers.values()] });
|
|
11215
11257
|
}
|
|
11216
11258
|
async permissions(project, session) {
|
|
11217
|
-
|
|
11218
|
-
|
|
11259
|
+
let entries;
|
|
11260
|
+
try {
|
|
11261
|
+
entries = await this.data(project, "GET", session ? `/api/session/${encodeURIComponent(session)}/permission` : "/api/permission/request");
|
|
11262
|
+
} catch (error) {
|
|
11263
|
+
if (!isPermissionMetadataError(error)) throw error;
|
|
11264
|
+
entries = [...this.permissionEvents.get(project)?.values() ?? []].filter((entry) => !session || entry.sessionID === session);
|
|
11265
|
+
if (!entries.length) throw error;
|
|
11266
|
+
}
|
|
11267
|
+
return (Array.isArray(entries) ? entries : []).map((entry) => ({ ...entry, permission: entry.action, patterns: entry.resources ?? [], replies: ["once", "always", "all", "reject"] }));
|
|
11268
|
+
}
|
|
11269
|
+
trackPermission(project, type, data) {
|
|
11270
|
+
if (typeof data?.sessionID !== "string" || !SESSION_ID.test(data.sessionID)) return;
|
|
11271
|
+
if (type === "permission.asked") {
|
|
11272
|
+
if (typeof data.id !== "string" || !PERMISSION_ID.test(data.id) || typeof data.action !== "string" || !Array.isArray(data.resources) || !data.resources.every((resource) => typeof resource === "string")) return;
|
|
11273
|
+
let pending = this.permissionEvents.get(project);
|
|
11274
|
+
if (!pending) this.permissionEvents.set(project, pending = /* @__PURE__ */ new Map());
|
|
11275
|
+
pending.set(data.id, data);
|
|
11276
|
+
if (pending.size > 256) pending.delete(pending.keys().next().value);
|
|
11277
|
+
if (this.approveAll.has(`${project}\0${data.sessionID}`)) {
|
|
11278
|
+
void this.resolveInteraction(project, data.sessionID, "permissions.reply", { requestId: data.id, reply: "once" }).catch(() => {
|
|
11279
|
+
});
|
|
11280
|
+
}
|
|
11281
|
+
} else if (type === "session.idle" || type === "session.status" && data.status?.type === "idle") {
|
|
11282
|
+
this.approveAll.delete(`${project}\0${data.sessionID}`);
|
|
11283
|
+
} else if (type === "permission.replied") {
|
|
11284
|
+
const pending = this.permissionEvents.get(project);
|
|
11285
|
+
if (pending?.get(data.requestID)?.sessionID === data.sessionID) pending.delete(data.requestID);
|
|
11286
|
+
}
|
|
11219
11287
|
}
|
|
11220
11288
|
async forms(project, session) {
|
|
11221
11289
|
const entries = await this.data(project, "GET", session ? `/api/session/${encodeURIComponent(session)}/form` : "/api/form");
|
|
@@ -11228,15 +11296,25 @@ var OpenCodeAdapter = class {
|
|
|
11228
11296
|
if (this.resolving.has(key)) throw new Error("Request is already being resolved by another client");
|
|
11229
11297
|
this.resolving.add(key);
|
|
11230
11298
|
try {
|
|
11231
|
-
|
|
11299
|
+
if (kind === "permission" && session) await this.session(project, session);
|
|
11300
|
+
const pending = kind === "permission" ? await this.permissions(project, session) : await this.forms(project);
|
|
11232
11301
|
const item = pending.find((entry) => entry.id === payload.requestId);
|
|
11233
11302
|
if (!item) throw new Error("Request is no longer pending");
|
|
11234
11303
|
if (session && session !== item.sessionID) throw new Error("Request belongs to another session");
|
|
11235
|
-
await this.session(project, item.sessionID);
|
|
11304
|
+
if (kind !== "permission" || !session) await this.session(project, item.sessionID);
|
|
11236
11305
|
const base = `/api/session/${encodeURIComponent(item.sessionID)}/${kind}/${requestId}`;
|
|
11237
11306
|
if (kind === "permission") {
|
|
11238
|
-
|
|
11239
|
-
|
|
11307
|
+
if (payload.reply === "all") {
|
|
11308
|
+
this.approveAll.add(`${project}\0${item.sessionID}`);
|
|
11309
|
+
for (const other of pending) if (other.sessionID === item.sessionID && other.id !== item.id) {
|
|
11310
|
+
await this.data(project, "POST", `/api/session/${encodeURIComponent(item.sessionID)}/permission/${this.identifier(other.id)}/reply`, { decision: "once" });
|
|
11311
|
+
this.permissionEvents.get(project)?.delete(other.id);
|
|
11312
|
+
}
|
|
11313
|
+
}
|
|
11314
|
+
const decision = payload.reply === "all" ? "once" : payload.reply === "deny" ? "reject" : payload.reply;
|
|
11315
|
+
const result = await this.data(project, "POST", `${base}/reply`, { decision, ...payload.message ? { message: payload.message } : {} });
|
|
11316
|
+
this.permissionEvents.get(project)?.delete(String(payload.requestId));
|
|
11317
|
+
return result;
|
|
11240
11318
|
}
|
|
11241
11319
|
if (operation === "questions.reject") return this.data(project, "DELETE", base);
|
|
11242
11320
|
const fields = item.fields;
|
|
@@ -11336,7 +11414,10 @@ var OpenCodeAdapter = class {
|
|
|
11336
11414
|
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
11337
11415
|
if (data) {
|
|
11338
11416
|
const event = JSON.parse(data);
|
|
11339
|
-
if (!event.location?.directory || event.location.directory === root)
|
|
11417
|
+
if (!event.location?.directory || event.location.directory === root) {
|
|
11418
|
+
if (event.location?.directory === root) this.trackPermission(project, event.type, event.data);
|
|
11419
|
+
this.emit(project, event.type === "server.connected" ? "octopus.resync" : event.type, await this.media.normalize(project, event.data ?? {}));
|
|
11420
|
+
}
|
|
11340
11421
|
}
|
|
11341
11422
|
}
|
|
11342
11423
|
if (buffer.length > Math.max(4 * 1024 * 1024, Math.ceil(this.uploads.limits.fileBytes * 4 / 3) + 1024 * 1024)) throw new Error("Event too large");
|
|
@@ -11344,6 +11425,7 @@ var OpenCodeAdapter = class {
|
|
|
11344
11425
|
} catch {
|
|
11345
11426
|
if (!controller.signal.aborted) this.emit(project, "octopus.disconnected", {});
|
|
11346
11427
|
}
|
|
11428
|
+
if (!controller.signal.aborted) this.permissionEvents.delete(project);
|
|
11347
11429
|
if (!controller.signal.aborted) await new Promise((resolve5) => setTimeout(resolve5, 2e3));
|
|
11348
11430
|
}
|
|
11349
11431
|
};
|
|
@@ -11352,6 +11434,8 @@ var OpenCodeAdapter = class {
|
|
|
11352
11434
|
removeProject(project) {
|
|
11353
11435
|
this.streams.get(project)?.abort();
|
|
11354
11436
|
this.streams.delete(project);
|
|
11437
|
+
this.permissionEvents.delete(project);
|
|
11438
|
+
for (const key of this.approveAll) if (key.startsWith(`${project}\0`)) this.approveAll.delete(key);
|
|
11355
11439
|
for (const [key, terminal] of this.terminals) {
|
|
11356
11440
|
if (!key.startsWith(`${project}:`)) continue;
|
|
11357
11441
|
terminal.close();
|
|
@@ -11412,6 +11496,8 @@ var OpenCodeAdapter = class {
|
|
|
11412
11496
|
}
|
|
11413
11497
|
close() {
|
|
11414
11498
|
this.auth.close();
|
|
11499
|
+
this.permissionEvents.clear();
|
|
11500
|
+
this.approveAll.clear();
|
|
11415
11501
|
for (const controller of this.streams.values()) controller.abort();
|
|
11416
11502
|
for (const terminal of this.terminals.values()) terminal.close();
|
|
11417
11503
|
}
|
|
@@ -11434,7 +11520,13 @@ function normalizeMessage(message) {
|
|
|
11434
11520
|
if (message?.model) info.model = { ...message.model, modelID: message.model.id };
|
|
11435
11521
|
const parts = [];
|
|
11436
11522
|
if (typeof message?.text === "string" && message.text) parts.push({ type: "text", text: message.text });
|
|
11437
|
-
for (const file of message?.files ?? [])
|
|
11523
|
+
for (const file of message?.files ?? []) {
|
|
11524
|
+
if (typeof file.data === "string" && /^image\/(?:png|jpeg|gif|webp)$/.test(file.mime)) {
|
|
11525
|
+
parts.push({ type: "image", filename: file.name, mime: file.mime, base64: file.data });
|
|
11526
|
+
} else {
|
|
11527
|
+
parts.push({ type: "file", filename: file.name, mime: file.mime, url: file.uri });
|
|
11528
|
+
}
|
|
11529
|
+
}
|
|
11438
11530
|
if (message?.type === "shell") parts.push({ type: "tool", tool: "shell", state: { status: message.time?.completed ? "completed" : "running", input: { command: message.command }, output: message.output } });
|
|
11439
11531
|
if (message?.type === "compaction") {
|
|
11440
11532
|
info.summary = true;
|
|
@@ -12241,6 +12333,7 @@ var CodexAdapter = class {
|
|
|
12241
12333
|
media;
|
|
12242
12334
|
liveTurns = /* @__PURE__ */ new Map();
|
|
12243
12335
|
approvals = /* @__PURE__ */ new Map();
|
|
12336
|
+
approveAll = /* @__PURE__ */ new Set();
|
|
12244
12337
|
inputs = /* @__PURE__ */ new Map();
|
|
12245
12338
|
activeTurns = /* @__PURE__ */ new Map();
|
|
12246
12339
|
loadedThreads = /* @__PURE__ */ new Map();
|
|
@@ -12281,13 +12374,17 @@ var CodexAdapter = class {
|
|
|
12281
12374
|
this.trackNotification(project, method, params);
|
|
12282
12375
|
if (method === "turn/completed") {
|
|
12283
12376
|
const detail = params ?? {};
|
|
12284
|
-
if (detail.threadId && this.activeTurns.get(detail.threadId) === detail.turn?.id)
|
|
12377
|
+
if (detail.threadId && this.activeTurns.get(detail.threadId) === detail.turn?.id) {
|
|
12378
|
+
this.activeTurns.delete(detail.threadId);
|
|
12379
|
+
this.approveAll.delete(`${project}\0${detail.threadId}`);
|
|
12380
|
+
}
|
|
12285
12381
|
}
|
|
12286
12382
|
this.emit(project, method, params);
|
|
12287
12383
|
},
|
|
12288
12384
|
onServerRequest: (id, method, params, respond) => this.onServerRequest(project, id, method, params, respond),
|
|
12289
12385
|
onClose: () => {
|
|
12290
12386
|
this.loadedThreads.delete(project);
|
|
12387
|
+
for (const key of this.approveAll) if (key.startsWith(`${project}\0`)) this.approveAll.delete(key);
|
|
12291
12388
|
for (const key of this.liveTurns.keys()) if (key.startsWith(`${project}\0`)) this.liveTurns.delete(key);
|
|
12292
12389
|
for (const [key, entry] of this.approvals) if (entry.project === project) this.approvals.delete(key);
|
|
12293
12390
|
for (const [key, entry] of this.inputs) if (entry.project === project) this.inputs.delete(key);
|
|
@@ -12305,7 +12402,12 @@ var CodexAdapter = class {
|
|
|
12305
12402
|
const requestId = randomUUID13();
|
|
12306
12403
|
const threadId = String(detail.threadId ?? "");
|
|
12307
12404
|
if (["item/permissions/requestApproval", "item/commandExecution/requestApproval", "item/fileChange/requestApproval"].includes(method)) {
|
|
12308
|
-
|
|
12405
|
+
const entry = { project, method, requestId, threadId, detail, respond };
|
|
12406
|
+
if (this.approveAll.has(`${project}\0${threadId}`)) {
|
|
12407
|
+
respond(this.approvalResponse(entry, "once"));
|
|
12408
|
+
return;
|
|
12409
|
+
}
|
|
12410
|
+
this.approvals.set(requestId, entry);
|
|
12309
12411
|
this.emit(project, "permission.request", { requestId, threadId });
|
|
12310
12412
|
return;
|
|
12311
12413
|
}
|
|
@@ -12536,6 +12638,7 @@ var CodexAdapter = class {
|
|
|
12536
12638
|
for (const approval of this.approvals.values()) approval.respond(this.approvalResponse(approval, "reject"));
|
|
12537
12639
|
for (const input of this.inputs.values()) input.respond({ answers: {} });
|
|
12538
12640
|
this.approvals.clear();
|
|
12641
|
+
this.approveAll.clear();
|
|
12539
12642
|
this.inputs.clear();
|
|
12540
12643
|
this.liveTurns.clear();
|
|
12541
12644
|
}
|
|
@@ -12662,6 +12765,7 @@ var CodexAdapter = class {
|
|
|
12662
12765
|
}
|
|
12663
12766
|
if (lines.length) input.push({ type: "text", text: lines.join("\n") });
|
|
12664
12767
|
if (!input.length) throw new Error("Message or attachments required");
|
|
12768
|
+
this.approveAll.delete(`${project}\0${id}`);
|
|
12665
12769
|
const selectedModel = p.model;
|
|
12666
12770
|
const model = selectedModel?.providerID === "codex" && typeof selectedModel.modelID === "string" ? selectedModel.modelID : void 0;
|
|
12667
12771
|
const effort = selectedReasoningLevel(p, "effort");
|
|
@@ -12671,6 +12775,7 @@ var CodexAdapter = class {
|
|
|
12671
12775
|
}
|
|
12672
12776
|
if (op === "sessions.abort") {
|
|
12673
12777
|
if (!id) throw new Error("Session required");
|
|
12778
|
+
this.approveAll.delete(`${project}\0${id}`);
|
|
12674
12779
|
await this.session(project, id);
|
|
12675
12780
|
let turnId = this.activeTurns.get(id);
|
|
12676
12781
|
if (!turnId) {
|
|
@@ -12695,15 +12800,23 @@ var CodexAdapter = class {
|
|
|
12695
12800
|
permission: entry.detail.type ?? (entry.method.includes("fileChange") ? "\u6587\u4EF6\u4FEE\u6539" : entry.detail.permissions ? "\u6269\u5C55\u6743\u9650" : "\u547D\u4EE4\u6267\u884C"),
|
|
12696
12801
|
patterns: [entry.detail.command ?? entry.detail.commandDraft ?? entry.detail.path ?? entry.detail.grantRoot ?? ""],
|
|
12697
12802
|
metadata: entry.detail,
|
|
12698
|
-
replies: ["once", "always", "reject"],
|
|
12699
|
-
alwaysLabel: "\
|
|
12803
|
+
replies: ["once", "always", "all", "reject"],
|
|
12804
|
+
alwaysLabel: "\u5141\u8BB8\u7C7B\u4F3C\u8BF7\u6C42"
|
|
12700
12805
|
}));
|
|
12701
12806
|
}
|
|
12702
12807
|
if (op === "permissions.reply") {
|
|
12703
12808
|
const entry = this.approvals.get(String(p.requestId));
|
|
12704
12809
|
if (!entry || entry.project !== project) throw new Error("Request is no longer pending in this project");
|
|
12705
12810
|
if (id && id !== entry.threadId) throw new Error("Request belongs to another session");
|
|
12706
|
-
if (!["accept", "allow", "yes", "once", "approve", "true", "always", "reject"].includes(String(p.reply))) throw new Error("Invalid permission reply");
|
|
12811
|
+
if (!["accept", "allow", "yes", "once", "approve", "true", "always", "all", "reject"].includes(String(p.reply))) throw new Error("Invalid permission reply");
|
|
12812
|
+
if (p.reply === "all") {
|
|
12813
|
+
this.approveAll.add(`${project}\0${entry.threadId}`);
|
|
12814
|
+
for (const [key, pending] of this.approvals) if (pending.project === project && pending.threadId === entry.threadId) {
|
|
12815
|
+
this.approvals.delete(key);
|
|
12816
|
+
pending.respond(this.approvalResponse(pending, "once"));
|
|
12817
|
+
}
|
|
12818
|
+
return { ok: true };
|
|
12819
|
+
}
|
|
12707
12820
|
entry.respond(this.approvalResponse(entry, String(p.reply)));
|
|
12708
12821
|
this.approvals.delete(String(p.requestId));
|
|
12709
12822
|
return { ok: true };
|
|
@@ -12766,6 +12879,7 @@ var ZcodeAdapter = class {
|
|
|
12766
12879
|
auth = new AuthFlows();
|
|
12767
12880
|
media;
|
|
12768
12881
|
permissions = /* @__PURE__ */ new Map();
|
|
12882
|
+
approveAll = /* @__PURE__ */ new Set();
|
|
12769
12883
|
inputs = /* @__PURE__ */ new Map();
|
|
12770
12884
|
subscribed = /* @__PURE__ */ new Set();
|
|
12771
12885
|
resuming = /* @__PURE__ */ new Map();
|
|
@@ -12780,6 +12894,7 @@ var ZcodeAdapter = class {
|
|
|
12780
12894
|
},
|
|
12781
12895
|
onServerRequest: (id, method, params, respond, respondError) => this.onServerRequest(project, id, method, params, respond, respondError),
|
|
12782
12896
|
onClose: () => {
|
|
12897
|
+
for (const key of this.approveAll) if (key.startsWith(`${project}\0`)) this.approveAll.delete(key);
|
|
12783
12898
|
for (const key of this.subscribed) if (key.startsWith(`${project}\0`)) this.subscribed.delete(key);
|
|
12784
12899
|
for (const [key, entry] of this.permissions) if (entry.project === project) this.permissions.delete(key);
|
|
12785
12900
|
for (const [key, entry] of this.inputs) if (entry.project === project) this.inputs.delete(key);
|
|
@@ -12798,6 +12913,10 @@ var ZcodeAdapter = class {
|
|
|
12798
12913
|
return;
|
|
12799
12914
|
}
|
|
12800
12915
|
if (method === "interaction/requestPermission" || method === "permission.request") {
|
|
12916
|
+
if (this.approveAll.has(`${project}\0${sessionId}`)) {
|
|
12917
|
+
respond({ decision: "allow" });
|
|
12918
|
+
return;
|
|
12919
|
+
}
|
|
12801
12920
|
this.permissions.set(requestId, { project, requestId, sessionId, detail, respond });
|
|
12802
12921
|
this.emit(project, "permission.request", { sessionID: sessionId, requestId });
|
|
12803
12922
|
return;
|
|
@@ -12923,6 +13042,7 @@ var ZcodeAdapter = class {
|
|
|
12923
13042
|
for (const entry of this.permissions.values()) entry.respond({ decision: "deny" });
|
|
12924
13043
|
for (const entry of this.inputs.values()) entry.respond({ action: "decline" });
|
|
12925
13044
|
this.permissions.clear();
|
|
13045
|
+
this.approveAll.clear();
|
|
12926
13046
|
this.inputs.clear();
|
|
12927
13047
|
}
|
|
12928
13048
|
async dispatch(command) {
|
|
@@ -13043,10 +13163,16 @@ var ZcodeAdapter = class {
|
|
|
13043
13163
|
const level = selectedReasoningLevel(p, "thoughtLevel");
|
|
13044
13164
|
await rpc.request("session/setThoughtLevel", { sessionId: id, thoughtLevel: level });
|
|
13045
13165
|
}
|
|
13046
|
-
|
|
13166
|
+
this.approveAll.delete(`${project}\0${id}`);
|
|
13167
|
+
try {
|
|
13168
|
+
return await rpc.request("session/send", { sessionId: id, content: lines.join("\n") });
|
|
13169
|
+
} finally {
|
|
13170
|
+
this.approveAll.delete(`${project}\0${id}`);
|
|
13171
|
+
}
|
|
13047
13172
|
}
|
|
13048
13173
|
if (op === "sessions.abort") {
|
|
13049
13174
|
if (!id) throw new Error("Session required");
|
|
13175
|
+
this.approveAll.delete(`${project}\0${id}`);
|
|
13050
13176
|
return await rpc.request("session/stop", { sessionId: id });
|
|
13051
13177
|
}
|
|
13052
13178
|
if (op === "sessions.delete") {
|
|
@@ -13061,15 +13187,23 @@ var ZcodeAdapter = class {
|
|
|
13061
13187
|
permission: entry.detail.type ?? entry.detail.toolName ?? "\u64CD\u4F5C\u786E\u8BA4",
|
|
13062
13188
|
patterns: [entry.detail.command ?? entry.detail.pattern ?? ""],
|
|
13063
13189
|
metadata: entry.detail,
|
|
13064
|
-
replies: this.alwaysResponse(entry) ? ["once", "always", "reject"] : ["once", "reject"],
|
|
13065
|
-
alwaysLabel: "\
|
|
13190
|
+
replies: this.alwaysResponse(entry) ? ["once", "always", "all", "reject"] : ["once", "all", "reject"],
|
|
13191
|
+
alwaysLabel: "\u5141\u8BB8\u7C7B\u4F3C\u8BF7\u6C42"
|
|
13066
13192
|
}));
|
|
13067
13193
|
}
|
|
13068
13194
|
if (op === "permissions.reply") {
|
|
13069
13195
|
const entry = this.permissions.get(String(p.requestId));
|
|
13070
13196
|
if (!entry || entry.project !== project) throw new Error("Request is no longer pending in this project");
|
|
13071
13197
|
if (id && id !== entry.sessionId) throw new Error("Request belongs to another session");
|
|
13072
|
-
if (!["accept", "once", "always", "reject"].includes(String(p.reply))) throw new Error("Invalid permission reply");
|
|
13198
|
+
if (!["accept", "once", "always", "all", "reject"].includes(String(p.reply))) throw new Error("Invalid permission reply");
|
|
13199
|
+
if (p.reply === "all") {
|
|
13200
|
+
this.approveAll.add(`${project}\0${entry.sessionId}`);
|
|
13201
|
+
for (const [key, pending] of this.permissions) if (pending.project === project && pending.sessionId === entry.sessionId) {
|
|
13202
|
+
this.permissions.delete(key);
|
|
13203
|
+
pending.respond({ decision: "allow" });
|
|
13204
|
+
}
|
|
13205
|
+
return { ok: true };
|
|
13206
|
+
}
|
|
13073
13207
|
const always = p.reply === "always" ? this.alwaysResponse(entry) : void 0;
|
|
13074
13208
|
if (p.reply === "always" && !always) throw new Error("Persistent approval is not offered by this request");
|
|
13075
13209
|
entry.respond(always ?? { decision: p.reply === "reject" ? "deny" : "allow" });
|
|
@@ -13169,6 +13303,7 @@ var PiAdapter = class {
|
|
|
13169
13303
|
partials = /* @__PURE__ */ new Map();
|
|
13170
13304
|
active = /* @__PURE__ */ new Set();
|
|
13171
13305
|
questions = /* @__PURE__ */ new Map();
|
|
13306
|
+
approveAll = /* @__PURE__ */ new Set();
|
|
13172
13307
|
mutations = /* @__PURE__ */ new Map();
|
|
13173
13308
|
deleting = /* @__PURE__ */ new Set();
|
|
13174
13309
|
reconfiguring = /* @__PURE__ */ new Set();
|
|
@@ -13212,7 +13347,10 @@ var PiAdapter = class {
|
|
|
13212
13347
|
calls.set(detail.toolCallId, progress);
|
|
13213
13348
|
this.toolProgress.set(key, calls);
|
|
13214
13349
|
}
|
|
13215
|
-
if (type === "agent_settled")
|
|
13350
|
+
if (type === "agent_settled") {
|
|
13351
|
+
this.active.delete(key);
|
|
13352
|
+
this.approveAll.delete(key);
|
|
13353
|
+
}
|
|
13216
13354
|
if (type === "message_update") this.partials.set(key, detail.message);
|
|
13217
13355
|
if (type === "message_end" || type === "agent_end" || type === "agent_settled") this.partials.delete(key);
|
|
13218
13356
|
if (type === "extension_ui_request") {
|
|
@@ -13223,6 +13361,7 @@ var PiAdapter = class {
|
|
|
13223
13361
|
this.emit(project, event, { ...detail, sessionID: id });
|
|
13224
13362
|
},
|
|
13225
13363
|
onClose: () => {
|
|
13364
|
+
this.approveAll.delete(this.key(project, id));
|
|
13226
13365
|
this.partials.delete(this.key(project, id));
|
|
13227
13366
|
this.active.delete(this.key(project, id));
|
|
13228
13367
|
this.clearQuestions(project, id);
|
|
@@ -13236,6 +13375,10 @@ var PiAdapter = class {
|
|
|
13236
13375
|
void this.rpc(project, sessionId).then((rpc) => {
|
|
13237
13376
|
if (rpc.exited) return;
|
|
13238
13377
|
const question = { id, project, sessionId, detail, rpc };
|
|
13378
|
+
if (this.isPermission(question) && this.approveAll.has(this.key(project, sessionId))) {
|
|
13379
|
+
rpc.notify("extension_ui_response", { id: detail.id, confirmed: true });
|
|
13380
|
+
return;
|
|
13381
|
+
}
|
|
13239
13382
|
if (typeof detail.timeout === "number" && detail.timeout > 0) {
|
|
13240
13383
|
question.timer = setTimeout(() => {
|
|
13241
13384
|
this.questions.delete(id);
|
|
@@ -13264,6 +13407,7 @@ var PiAdapter = class {
|
|
|
13264
13407
|
if (!question.rpc.exited) question.rpc.notify("extension_ui_response", { id: question.detail.id, cancelled: true });
|
|
13265
13408
|
}
|
|
13266
13409
|
this.questions.clear();
|
|
13410
|
+
this.approveAll.clear();
|
|
13267
13411
|
this.partials.clear();
|
|
13268
13412
|
this.active.clear();
|
|
13269
13413
|
this.toolProgress.clear();
|
|
@@ -13365,13 +13509,22 @@ var PiAdapter = class {
|
|
|
13365
13509
|
sessionID: q.sessionId,
|
|
13366
13510
|
permission: "Pi \u5DE5\u5177\u64CD\u4F5C",
|
|
13367
13511
|
patterns: [q.detail.message ?? ""],
|
|
13368
|
-
replies: ["once", "reject"]
|
|
13512
|
+
replies: ["once", "all", "reject"]
|
|
13369
13513
|
}));
|
|
13370
13514
|
}
|
|
13371
13515
|
if (op === "permissions.reply") {
|
|
13372
13516
|
const question = this.questions.get(String(p.requestId));
|
|
13373
13517
|
if (!question || !this.isPermission(question) || question.project !== project || id && id !== question.sessionId) throw new Error("Request is no longer pending in this session");
|
|
13374
|
-
if (p.reply !== "once" && p.reply !== "reject") throw new Error("Invalid permission reply");
|
|
13518
|
+
if (p.reply !== "once" && p.reply !== "all" && p.reply !== "reject") throw new Error("Invalid permission reply");
|
|
13519
|
+
if (p.reply === "all") {
|
|
13520
|
+
this.approveAll.add(this.key(project, question.sessionId));
|
|
13521
|
+
for (const [key, pending] of this.questions) if (pending.project === project && pending.sessionId === question.sessionId && this.isPermission(pending)) {
|
|
13522
|
+
clearTimeout(pending.timer);
|
|
13523
|
+
this.questions.delete(key);
|
|
13524
|
+
pending.rpc.notify("extension_ui_response", { id: pending.detail.id, confirmed: true });
|
|
13525
|
+
}
|
|
13526
|
+
return { ok: true };
|
|
13527
|
+
}
|
|
13375
13528
|
question.rpc.notify("extension_ui_response", { id: question.detail.id, confirmed: p.reply === "once" });
|
|
13376
13529
|
clearTimeout(question.timer);
|
|
13377
13530
|
this.questions.delete(question.id);
|
|
@@ -13434,6 +13587,7 @@ var PiAdapter = class {
|
|
|
13434
13587
|
return this.media.messages(project, id, this.messages(messages, this.key(project, id)));
|
|
13435
13588
|
}
|
|
13436
13589
|
if (op === "sessions.abort") {
|
|
13590
|
+
this.approveAll.delete(this.key(project, id));
|
|
13437
13591
|
this.clearQuestions(project, id);
|
|
13438
13592
|
await rpc.request("clear_queue");
|
|
13439
13593
|
await rpc.request("abort");
|
|
@@ -13469,6 +13623,7 @@ var PiAdapter = class {
|
|
|
13469
13623
|
const level = selectedReasoningLevel(p, "level");
|
|
13470
13624
|
if (model && typeof model.providerID === "string" && typeof model.modelID === "string") await rpc.request("set_model", { provider: model.providerID, modelId: model.modelID });
|
|
13471
13625
|
if (level) await rpc.request("set_thinking_level", { level });
|
|
13626
|
+
this.approveAll.delete(this.key(project, id));
|
|
13472
13627
|
const result = await rpc.request("prompt", { message: lines.join("\n"), ...images.length ? { images } : {} });
|
|
13473
13628
|
session.time.updated = Date.now();
|
|
13474
13629
|
if (session.title === "\u65B0\u4EFB\u52A1" && typeof p.text === "string" && p.text.trim()) session.title = p.text.trim().slice(0, 100);
|
package/dist/cli.js
CHANGED
package/dist/main.js
CHANGED