@skylandnpm/octopus-proxy 0.1.63 → 0.1.64
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-R73E3EFB.js → chunk-J3ZMMSW2.js} +163 -19
- package/dist/cli.js +1 -1
- package/dist/main.js +1 -1
- package/package.json +1 -1
|
@@ -8334,6 +8334,7 @@ var Operations = [
|
|
|
8334
8334
|
"configuration.get",
|
|
8335
8335
|
"configuration.patch",
|
|
8336
8336
|
"configuration.models",
|
|
8337
|
+
"configuration.permissionModes",
|
|
8337
8338
|
"configuration.agents",
|
|
8338
8339
|
"configuration.commands",
|
|
8339
8340
|
"configuration.providers",
|
|
@@ -8373,7 +8374,7 @@ var CommandSchema = z.object({
|
|
|
8373
8374
|
sessionId: z.string().optional(),
|
|
8374
8375
|
payload: z.record(z.unknown()).default({})
|
|
8375
8376
|
}).strict();
|
|
8376
|
-
var isReadOperation = (operation) => operation === "desktop.capture" || /\.(status|snapshot|list|get|messages|diff|todos|children|logs|projects|read|search|history|models|agents|commands|providers|mcp)$/.test(operation);
|
|
8377
|
+
var isReadOperation = (operation) => operation === "desktop.capture" || /\.(status|snapshot|list|get|messages|diff|todos|children|logs|projects|read|search|history|models|permissionModes|agents|commands|providers|mcp)$/.test(operation);
|
|
8377
8378
|
|
|
8378
8379
|
// src/journal.ts
|
|
8379
8380
|
var Journal = class {
|
|
@@ -11031,6 +11032,10 @@ var OpenCodeAdapter = class {
|
|
|
11031
11032
|
await this.session(project, id);
|
|
11032
11033
|
}
|
|
11033
11034
|
const base = `/api/session/${encodeURIComponent(id ?? "")}`;
|
|
11035
|
+
if (op === "configuration.permissionModes") return { defaultMode: "ask", modes: [
|
|
11036
|
+
{ id: "ask", label: "\u8BF7\u6C42\u6279\u51C6", description: "\u6BCF\u6B21\u4F7F\u7528\u5DE5\u5177\u524D\u8BF7\u6C42\u6279\u51C6" },
|
|
11037
|
+
{ 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" }
|
|
11038
|
+
] };
|
|
11034
11039
|
if (op === "sessions.prompt") return this.prompt(project, id, p, base);
|
|
11035
11040
|
if (op === "sessions.command" || op === "sessions.shell") {
|
|
11036
11041
|
await this.switchSelection(project, base, p);
|
|
@@ -11098,6 +11103,13 @@ var OpenCodeAdapter = class {
|
|
|
11098
11103
|
return this.data(project, "POST", `${base}/prompt`, { text, files, delivery: "queue", ...p.messageID ? { id: p.messageID } : {} });
|
|
11099
11104
|
}
|
|
11100
11105
|
async switchSelection(project, base, payload) {
|
|
11106
|
+
if (payload.permissionMode !== void 0) {
|
|
11107
|
+
if (!["ask", "full"].includes(String(payload.permissionMode))) throw new Error("Unsupported permission mode");
|
|
11108
|
+
const states = await this.data(project, "GET", "/api/session/active");
|
|
11109
|
+
const state = states?.[decodeURIComponent(base.split("/").at(-1))];
|
|
11110
|
+
if (state && !["idle", "completed", "failed", "interrupted"].includes(state.type)) throw new Error("Wait for the active task before changing permission mode");
|
|
11111
|
+
await this.data(project, "PATCH", base, { permissions: [{ action: "*", resource: "*", effect: payload.permissionMode === "full" ? "allow" : "ask" }] });
|
|
11112
|
+
}
|
|
11101
11113
|
if (payload.agent) await this.data(project, "POST", `${base}/agent`, { agent: payload.agent });
|
|
11102
11114
|
if (!payload.model) return;
|
|
11103
11115
|
let model;
|
|
@@ -12129,6 +12141,7 @@ var OPERATIONS = [
|
|
|
12129
12141
|
"sessions.abort",
|
|
12130
12142
|
"sessions.delete",
|
|
12131
12143
|
"configuration.models",
|
|
12144
|
+
"configuration.permissionModes",
|
|
12132
12145
|
"permissions.list",
|
|
12133
12146
|
"permissions.reply",
|
|
12134
12147
|
"questions.list",
|
|
@@ -12403,6 +12416,31 @@ var CodexAdapter = class {
|
|
|
12403
12416
|
}
|
|
12404
12417
|
return { decision: accept ? reply === "always" ? "acceptForSession" : "accept" : "decline" };
|
|
12405
12418
|
}
|
|
12419
|
+
async permissionModes(project, rpc) {
|
|
12420
|
+
const result = await rpc.request("config/read", { cwd: await this.workspace.root(project), includeLayers: false }).catch(() => void 0);
|
|
12421
|
+
const autoReview = !!result?.config && Object.hasOwn(result.config, "approvals_reviewer");
|
|
12422
|
+
return {
|
|
12423
|
+
defaultMode: "ask",
|
|
12424
|
+
modes: [
|
|
12425
|
+
{ id: "ask", label: "\u8BF7\u6C42\u6279\u51C6", description: "\u7F16\u8F91\u9879\u76EE\u5916\u7684\u6587\u4EF6\u548C\u4F7F\u7528\u4E92\u8054\u7F51\u65F6\u8BF7\u6C42\u6279\u51C6" },
|
|
12426
|
+
...autoReview ? [{ id: "auto", label: "\u5E2E\u6211\u6279\u51C6", description: "\u7531 Codex \u81EA\u52A8\u5BA1\u6838\u64CD\u4F5C\u98CE\u9669\uFF0C\u9700\u8981\u4F60\u786E\u8BA4\u65F6\u518D\u8BF7\u6C42\u6279\u51C6" }] : [],
|
|
12427
|
+
{ id: "full", label: "\u5B8C\u5168\u8BBF\u95EE", description: "\u5141\u8BB8\u8BBF\u95EE\u4E92\u8054\u7F51\u548C Proxy \u4E3B\u673A\u4E0A\u7684\u4EFB\u4F55\u6587\u4EF6\uFF0C\u65E0\u9700\u64CD\u4F5C\u6279\u51C6" }
|
|
12428
|
+
],
|
|
12429
|
+
autoReview
|
|
12430
|
+
};
|
|
12431
|
+
}
|
|
12432
|
+
async permissionOverrides(project, rpc, mode) {
|
|
12433
|
+
if (mode === void 0) return {};
|
|
12434
|
+
const options = await this.permissionModes(project, rpc);
|
|
12435
|
+
if (!options.modes.some((entry) => entry.id === mode)) throw new Error("Unsupported permission mode");
|
|
12436
|
+
const reviewer = options.autoReview ? { approvalsReviewer: mode === "auto" ? "guardian_subagent" : "user" } : {};
|
|
12437
|
+
if (mode === "full") return { approvalPolicy: "never", sandboxPolicy: { type: "dangerFullAccess" }, ...reviewer };
|
|
12438
|
+
return {
|
|
12439
|
+
approvalPolicy: "on-request",
|
|
12440
|
+
sandboxPolicy: { type: "workspaceWrite", writableRoots: [await this.workspace.root(project)], networkAccess: false },
|
|
12441
|
+
...reviewer
|
|
12442
|
+
};
|
|
12443
|
+
}
|
|
12406
12444
|
async session(project, id) {
|
|
12407
12445
|
if (!describeTool("codex").sessionPattern.test(id)) throw new Error("Invalid session id");
|
|
12408
12446
|
const rpc = await this.rpc(project);
|
|
@@ -12431,6 +12469,10 @@ var CodexAdapter = class {
|
|
|
12431
12469
|
if (op === "auth.poll") return this.auth.poll(project, p);
|
|
12432
12470
|
if (op === "auth.cancel") return this.auth.cancel(project, p.flowId);
|
|
12433
12471
|
const rpc = await this.rpc(project);
|
|
12472
|
+
if (op === "configuration.permissionModes") {
|
|
12473
|
+
const { autoReview: _, ...options } = await this.permissionModes(project, rpc);
|
|
12474
|
+
return options;
|
|
12475
|
+
}
|
|
12434
12476
|
if (op === "auth.status") {
|
|
12435
12477
|
const result = await rpc.request("account/read", { refreshToken: false });
|
|
12436
12478
|
return {
|
|
@@ -12530,6 +12572,7 @@ var CodexAdapter = class {
|
|
|
12530
12572
|
if (op === "sessions.prompt") {
|
|
12531
12573
|
if (!id) throw new Error("Session required");
|
|
12532
12574
|
await this.session(project, id);
|
|
12575
|
+
const permissions = await this.permissionOverrides(project, rpc, p.permissionMode);
|
|
12533
12576
|
await this.resume(project, id, rpc);
|
|
12534
12577
|
const input = [];
|
|
12535
12578
|
const lines = [];
|
|
@@ -12544,7 +12587,7 @@ var CodexAdapter = class {
|
|
|
12544
12587
|
const selectedModel = p.model;
|
|
12545
12588
|
const model = selectedModel?.providerID === "codex" && typeof selectedModel.modelID === "string" ? selectedModel.modelID : void 0;
|
|
12546
12589
|
const effort = selectedReasoningLevel(p, "effort");
|
|
12547
|
-
const result = await rpc.request("turn/start", { threadId: id, input, ...model ? { model } : {}, ...effort ? { effort } : {} });
|
|
12590
|
+
const result = await rpc.request("turn/start", { threadId: id, input, ...model ? { model } : {}, ...effort ? { effort } : {}, ...permissions });
|
|
12548
12591
|
this.activeTurns.set(id, result.turn.id);
|
|
12549
12592
|
return result;
|
|
12550
12593
|
}
|
|
@@ -12619,6 +12662,7 @@ var OPERATIONS2 = [
|
|
|
12619
12662
|
...DAEMON_OPERATIONS,
|
|
12620
12663
|
...AUTH_OPERATIONS,
|
|
12621
12664
|
"configuration.models",
|
|
12665
|
+
"configuration.permissionModes",
|
|
12622
12666
|
"sessions.list",
|
|
12623
12667
|
"sessions.create",
|
|
12624
12668
|
"sessions.get",
|
|
@@ -12811,6 +12855,12 @@ var ZcodeAdapter = class {
|
|
|
12811
12855
|
if (op === "auth.poll") return this.auth.poll(project, p);
|
|
12812
12856
|
if (op === "auth.cancel") return this.auth.cancel(project, p.flowId);
|
|
12813
12857
|
const rpc = await this.rpc(project);
|
|
12858
|
+
if (op === "configuration.permissionModes") return { defaultMode: "ask", modes: [
|
|
12859
|
+
{ id: "ask", label: "\u8BF7\u6C42\u6279\u51C6", description: "\u4F7F\u7528 ZCode build \u6A21\u5F0F\uFF0C\u4FEE\u6539\u6587\u4EF6\u524D\u8BF7\u6C42\u6279\u51C6" },
|
|
12860
|
+
{ id: "edit", label: "\u81EA\u52A8\u7F16\u8F91", description: "\u81EA\u52A8\u7F16\u8F91\u76F8\u5173\u6587\u4EF6\uFF0C\u5176\u4ED6\u64CD\u4F5C\u6309 ZCode \u539F\u751F\u89C4\u5219\u5BA1\u6279" },
|
|
12861
|
+
{ id: "plan", label: "\u53EA\u8BFB\u89C4\u5212", description: "\u4F7F\u7528 ZCode plan \u6A21\u5F0F\uFF0C\u68C0\u67E5\u9879\u76EE\u5E76\u5236\u5B9A\u8BA1\u5212" },
|
|
12862
|
+
{ id: "full", label: "\u5B8C\u5168\u8BBF\u95EE", description: "\u4F7F\u7528 ZCode yolo \u6A21\u5F0F\uFF0C\u51CF\u5C11\u5DE5\u5177\u64CD\u4F5C\u786E\u8BA4" }
|
|
12863
|
+
] };
|
|
12814
12864
|
if (op === "configuration.models" || op === "auth.status") {
|
|
12815
12865
|
const snapshot = await rpc.request("session/create", { workspace: await this.workspaceRef(project), persistence: "deferred", titleGenerationEnabled: false, toolAllowlist: [] });
|
|
12816
12866
|
try {
|
|
@@ -12886,7 +12936,8 @@ var ZcodeAdapter = class {
|
|
|
12886
12936
|
}
|
|
12887
12937
|
if (op === "sessions.prompt") {
|
|
12888
12938
|
if (!id) throw new Error("Session required");
|
|
12889
|
-
await this.session(project, id);
|
|
12939
|
+
const session = await this.session(project, id);
|
|
12940
|
+
if (p.permissionMode !== void 0 && typeof session.status === "string" && !["idle", "completed", "stopped", "closed", "failed", "error"].includes(session.status.toLowerCase())) throw new Error("Wait for the active task before changing permission mode");
|
|
12890
12941
|
await this.watch(project, rpc, id);
|
|
12891
12942
|
const lines = [];
|
|
12892
12943
|
if (typeof p.text === "string" && p.text.trim()) lines.push(screenshotPrompt(p.text));
|
|
@@ -12895,6 +12946,12 @@ var ZcodeAdapter = class {
|
|
|
12895
12946
|
lines.push(`Attached file: ${upload.destination} (${upload.name}, ${upload.mime}). Read this file if relevant to the task.`);
|
|
12896
12947
|
}
|
|
12897
12948
|
if (!lines.length) throw new Error("Message or attachments required");
|
|
12949
|
+
if (p.permissionMode !== void 0) {
|
|
12950
|
+
const modes = { ask: "build", edit: "edit", plan: "plan", full: "yolo" };
|
|
12951
|
+
const mode = typeof p.permissionMode === "string" && Object.hasOwn(modes, p.permissionMode) ? modes[p.permissionMode] : void 0;
|
|
12952
|
+
if (!mode) throw new Error("Unsupported permission mode");
|
|
12953
|
+
await rpc.request("session/setMode", { sessionId: id, mode });
|
|
12954
|
+
}
|
|
12898
12955
|
const selected = p.model;
|
|
12899
12956
|
if (selected?.providerID && selected.modelID) {
|
|
12900
12957
|
let level = selectedReasoningLevel(p, "model.options.reasoningLevel");
|
|
@@ -12986,6 +13043,22 @@ var ZcodeAdapter = class {
|
|
|
12986
13043
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
12987
13044
|
import { readFile as readFile8, readdir as readdir3, rm as rm2 } from "node:fs/promises";
|
|
12988
13045
|
import { dirname as dirname5 } from "node:path";
|
|
13046
|
+
|
|
13047
|
+
// src/tools/pi-permissions.ts
|
|
13048
|
+
var PI_PERMISSION_TITLE = "Octopus \u5DE5\u5177\u6388\u6743";
|
|
13049
|
+
var PI_PERMISSION_EXTENSION = `
|
|
13050
|
+
export default function (pi) {
|
|
13051
|
+
const ready = process.env.OCTOPUS_PI_PERMISSION_READY;
|
|
13052
|
+
pi.on('session_start', (_event, ctx) => ctx.ui.notify(ready, 'info'));
|
|
13053
|
+
pi.on('tool_call', async (event, ctx) => {
|
|
13054
|
+
if (!ctx.hasUI) return { block: true, reason: '\u5DE5\u5177\u64CD\u4F5C\u9700\u8981\u7528\u6237\u6279\u51C6' };
|
|
13055
|
+
const allowed = await ctx.ui.confirm(${JSON.stringify(PI_PERMISSION_TITLE)}, JSON.stringify({ toolName: event.toolName, input: event.input }));
|
|
13056
|
+
if (allowed !== true) return { block: true, reason: '\u7528\u6237\u62D2\u7EDD\u4E86\u5DE5\u5177\u64CD\u4F5C' };
|
|
13057
|
+
});
|
|
13058
|
+
}
|
|
13059
|
+
`;
|
|
13060
|
+
|
|
13061
|
+
// src/tools/pi.ts
|
|
12989
13062
|
var OPERATIONS3 = [
|
|
12990
13063
|
...DAEMON_OPERATIONS,
|
|
12991
13064
|
"sessions.list",
|
|
@@ -12998,7 +13071,9 @@ var OPERATIONS3 = [
|
|
|
12998
13071
|
"sessions.delete",
|
|
12999
13072
|
"auth.status",
|
|
13000
13073
|
"configuration.models",
|
|
13074
|
+
"configuration.permissionModes",
|
|
13001
13075
|
"permissions.list",
|
|
13076
|
+
"permissions.reply",
|
|
13002
13077
|
"questions.list",
|
|
13003
13078
|
"questions.reply",
|
|
13004
13079
|
"questions.reject"
|
|
@@ -13018,6 +13093,7 @@ var PiAdapter = class {
|
|
|
13018
13093
|
questions = /* @__PURE__ */ new Map();
|
|
13019
13094
|
mutations = /* @__PURE__ */ new Map();
|
|
13020
13095
|
deleting = /* @__PURE__ */ new Set();
|
|
13096
|
+
reconfiguring = /* @__PURE__ */ new Set();
|
|
13021
13097
|
supportedOperations() {
|
|
13022
13098
|
return OPERATIONS3;
|
|
13023
13099
|
}
|
|
@@ -13072,7 +13148,7 @@ var PiAdapter = class {
|
|
|
13072
13148
|
this.partials.delete(this.key(project, id));
|
|
13073
13149
|
this.active.delete(this.key(project, id));
|
|
13074
13150
|
this.clearQuestions(project, id);
|
|
13075
|
-
this.emit(project, "session.error", { sessionID: id, message: "Pi connection closed; reconnect to resume the saved session" });
|
|
13151
|
+
if (!this.reconfiguring.has(this.key(project, id))) this.emit(project, "session.error", { sessionID: id, message: "Pi connection closed; reconnect to resume the saved session" });
|
|
13076
13152
|
}
|
|
13077
13153
|
});
|
|
13078
13154
|
}
|
|
@@ -13090,16 +13166,20 @@ var PiAdapter = class {
|
|
|
13090
13166
|
question.timer.unref();
|
|
13091
13167
|
}
|
|
13092
13168
|
this.questions.set(id, question);
|
|
13093
|
-
this.emit(project, "question.request", { sessionID: sessionId, requestId: id });
|
|
13169
|
+
this.emit(project, this.isPermission(question) ? "permission.request" : "question.request", { sessionID: sessionId, requestId: id });
|
|
13094
13170
|
}).catch(() => {
|
|
13095
13171
|
});
|
|
13096
13172
|
}
|
|
13097
13173
|
clearQuestions(project, id) {
|
|
13098
13174
|
for (const [key, question] of this.questions) if (question.project === project && question.sessionId === id) {
|
|
13099
13175
|
clearTimeout(question.timer);
|
|
13176
|
+
if (!question.rpc.exited) question.rpc.notify("extension_ui_response", { id: question.detail.id, cancelled: true });
|
|
13100
13177
|
this.questions.delete(key);
|
|
13101
13178
|
}
|
|
13102
13179
|
}
|
|
13180
|
+
isPermission(question) {
|
|
13181
|
+
return question.detail.method === "confirm" && question.detail.title === PI_PERMISSION_TITLE;
|
|
13182
|
+
}
|
|
13103
13183
|
close() {
|
|
13104
13184
|
for (const question of this.questions.values()) {
|
|
13105
13185
|
clearTimeout(question.timer);
|
|
@@ -13157,6 +13237,10 @@ var PiAdapter = class {
|
|
|
13157
13237
|
async execute(command, project) {
|
|
13158
13238
|
const op = command.operation, p = command.payload, id = command.sessionId;
|
|
13159
13239
|
if (!OPERATIONS3.includes(op)) unsupportedOperation("pi", op);
|
|
13240
|
+
if (op === "configuration.permissionModes") return { defaultMode: "ask", modes: [
|
|
13241
|
+
{ id: "ask", label: "\u8BF7\u6C42\u6279\u51C6", description: "\u901A\u8FC7 Pi \u5DE5\u5177\u6388\u6743\u6269\u5C55\uFF0C\u5728\u6BCF\u6B21\u5DE5\u5177\u6267\u884C\u524D\u8BF7\u6C42\u6279\u51C6" },
|
|
13242
|
+
{ id: "full", label: "\u5B8C\u5168\u8BBF\u95EE", description: "\u6309 Pi \u539F\u751F\u6743\u9650\u6267\u884C\u5DE5\u5177\uFF0C\u4E0D\u542F\u7528\u9010\u6B21\u5BA1\u6279\uFF1B\u4E0D\u63D0\u4F9B\u7CFB\u7EDF\u6C99\u7BB1" }
|
|
13243
|
+
] };
|
|
13160
13244
|
if (op === "auth.status") {
|
|
13161
13245
|
const rpc2 = await this.runtime.connect(project);
|
|
13162
13246
|
const result = await rpc2.request("get_available_models");
|
|
@@ -13198,11 +13282,26 @@ var PiAdapter = class {
|
|
|
13198
13282
|
}
|
|
13199
13283
|
if (op === "permissions.list") {
|
|
13200
13284
|
if (id) await this.session(project, id);
|
|
13201
|
-
return []
|
|
13285
|
+
return [...this.questions.values()].filter((q) => q.project === project && (!id || q.sessionId === id) && this.isPermission(q)).map((q) => ({
|
|
13286
|
+
id: q.id,
|
|
13287
|
+
sessionID: q.sessionId,
|
|
13288
|
+
permission: "Pi \u5DE5\u5177\u64CD\u4F5C",
|
|
13289
|
+
patterns: [q.detail.message ?? ""],
|
|
13290
|
+
replies: ["once", "reject"]
|
|
13291
|
+
}));
|
|
13292
|
+
}
|
|
13293
|
+
if (op === "permissions.reply") {
|
|
13294
|
+
const question = this.questions.get(String(p.requestId));
|
|
13295
|
+
if (!question || !this.isPermission(question) || question.project !== project || id && id !== question.sessionId) throw new Error("Request is no longer pending in this session");
|
|
13296
|
+
if (p.reply !== "once" && p.reply !== "reject") throw new Error("Invalid permission reply");
|
|
13297
|
+
question.rpc.notify("extension_ui_response", { id: question.detail.id, confirmed: p.reply === "once" });
|
|
13298
|
+
clearTimeout(question.timer);
|
|
13299
|
+
this.questions.delete(question.id);
|
|
13300
|
+
return { ok: true };
|
|
13202
13301
|
}
|
|
13203
13302
|
if (op === "questions.list") {
|
|
13204
13303
|
if (id) await this.session(project, id);
|
|
13205
|
-
return [...this.questions.values()].filter((q) => q.project === project && (!id || q.sessionId === id)).map((q) => ({
|
|
13304
|
+
return [...this.questions.values()].filter((q) => q.project === project && (!id || q.sessionId === id) && !this.isPermission(q)).map((q) => ({
|
|
13206
13305
|
id: q.id,
|
|
13207
13306
|
sessionID: q.sessionId,
|
|
13208
13307
|
questions: [{
|
|
@@ -13216,7 +13315,7 @@ var PiAdapter = class {
|
|
|
13216
13315
|
}
|
|
13217
13316
|
if (op === "questions.reply" || op === "questions.reject") {
|
|
13218
13317
|
const question = this.questions.get(String(p.requestId));
|
|
13219
|
-
if (!question || question.project !== project || id && id !== question.sessionId) throw new Error("Request is no longer pending in this session");
|
|
13318
|
+
if (!question || this.isPermission(question) || question.project !== project || id && id !== question.sessionId) throw new Error("Request is no longer pending in this session");
|
|
13220
13319
|
const answers = Array.isArray(p.answers) ? p.answers : [];
|
|
13221
13320
|
const first = Array.isArray(answers[0]) ? answers[0][0] : answers[0];
|
|
13222
13321
|
const value = typeof first === "string" ? first : "";
|
|
@@ -13234,13 +13333,14 @@ var PiAdapter = class {
|
|
|
13234
13333
|
await this.runtime.closeSession(project, id);
|
|
13235
13334
|
await rm2(this.runtime.sessionFile(project, id), { force: true });
|
|
13236
13335
|
await rm2(this.metadataPath(project, id));
|
|
13336
|
+
await rm2(this.runtime.sessionFile(project, id).replace(/\.jsonl$/, ".permission.ts"), { force: true });
|
|
13237
13337
|
this.clearQuestions(project, id);
|
|
13238
13338
|
} finally {
|
|
13239
13339
|
this.deleting.delete(this.key(project, id));
|
|
13240
13340
|
}
|
|
13241
13341
|
return { ok: true };
|
|
13242
13342
|
}
|
|
13243
|
-
|
|
13343
|
+
let rpc = await this.rpc(project, id);
|
|
13244
13344
|
if (op === "sessions.status") {
|
|
13245
13345
|
const state = await rpc.request("get_state");
|
|
13246
13346
|
return { [id]: { type: this.active.has(this.key(project, id)) || state.isStreaming || state.isCompacting || (state.pendingMessageCount ?? 0) > 0 ? "busy" : "idle" } };
|
|
@@ -13256,6 +13356,7 @@ var PiAdapter = class {
|
|
|
13256
13356
|
return this.media.messages(project, id, this.messages(messages, this.key(project, id)));
|
|
13257
13357
|
}
|
|
13258
13358
|
if (op === "sessions.abort") {
|
|
13359
|
+
this.clearQuestions(project, id);
|
|
13259
13360
|
await rpc.request("clear_queue");
|
|
13260
13361
|
await rpc.request("abort");
|
|
13261
13362
|
return { ok: true };
|
|
@@ -13270,6 +13371,22 @@ var PiAdapter = class {
|
|
|
13270
13371
|
} else lines.push(`Attached file: ${upload.destination} (${upload.name}, ${upload.mime}). Read this file if relevant to the task.`);
|
|
13271
13372
|
}
|
|
13272
13373
|
if (!lines.length && !images.length) throw new Error("Message or attachments required");
|
|
13374
|
+
if (p.permissionMode !== void 0) {
|
|
13375
|
+
if (p.permissionMode !== "ask" && p.permissionMode !== "full") throw new Error("Unsupported permission mode");
|
|
13376
|
+
if (p.permissionMode !== (session.permissionMode ?? "full")) {
|
|
13377
|
+
const state = await rpc.request("get_state");
|
|
13378
|
+
if (this.active.has(this.key(project, id)) || state.isStreaming || state.isCompacting || (state.pendingMessageCount ?? 0) > 0) throw new Error("Wait for the active task before changing permission mode");
|
|
13379
|
+
session.permissionMode = p.permissionMode;
|
|
13380
|
+
await atomicJson(this.metadataPath(project, id), session);
|
|
13381
|
+
this.reconfiguring.add(this.key(project, id));
|
|
13382
|
+
try {
|
|
13383
|
+
await this.runtime.closeSession(project, id);
|
|
13384
|
+
rpc = await this.rpc(project, id);
|
|
13385
|
+
} finally {
|
|
13386
|
+
this.reconfiguring.delete(this.key(project, id));
|
|
13387
|
+
}
|
|
13388
|
+
}
|
|
13389
|
+
}
|
|
13273
13390
|
const model = p.model;
|
|
13274
13391
|
const level = selectedReasoningLevel(p, "level");
|
|
13275
13392
|
if (model && typeof model.providerID === "string" && typeof model.modelID === "string") await rpc.request("set_model", { provider: model.providerID, modelId: model.modelID });
|
|
@@ -13286,7 +13403,8 @@ var PiAdapter = class {
|
|
|
13286
13403
|
|
|
13287
13404
|
// src/tools/pi-runtime.ts
|
|
13288
13405
|
import { execFile as execFile5, spawn as spawn5 } from "node:child_process";
|
|
13289
|
-
import { mkdir as mkdir8, open as open8 } from "node:fs/promises";
|
|
13406
|
+
import { mkdir as mkdir8, open as open8, writeFile as writeFile6 } from "node:fs/promises";
|
|
13407
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
13290
13408
|
import { join as join13 } from "node:path";
|
|
13291
13409
|
import { promisify as promisify5 } from "node:util";
|
|
13292
13410
|
var run3 = promisify5(execFile5);
|
|
@@ -13405,32 +13523,58 @@ ${bytes.toString("utf8")}`);
|
|
|
13405
13523
|
await this.kill(key);
|
|
13406
13524
|
const directory = this.project(projectId).directory;
|
|
13407
13525
|
await mkdir8(join13(this.config.stateDirectory, "pi-sessions", projectId), { recursive: true, mode: 448 });
|
|
13526
|
+
const sessionFile = sessionId ? this.sessionFile(projectId, sessionId) : void 0;
|
|
13527
|
+
const policy = sessionFile ? await readJson(sessionFile.replace(/\.jsonl$/, ".json")) : void 0;
|
|
13528
|
+
const gated = policy?.permissionMode === "ask";
|
|
13529
|
+
const extension = sessionFile?.replace(/\.jsonl$/, ".permission.ts");
|
|
13530
|
+
if (gated) await writeFile6(extension, PI_PERMISSION_EXTENSION, { mode: 384 });
|
|
13531
|
+
const readyToken = gated ? `octopus-permissions-ready:${randomUUID16()}` : "";
|
|
13408
13532
|
const logPath = join13(this.config.stateDirectory, `pi-${key}.log`);
|
|
13409
13533
|
const log = await open8(logPath, "a", 384);
|
|
13410
13534
|
if (generation !== this.generation) {
|
|
13411
13535
|
await log.close();
|
|
13412
13536
|
throw new Error("Pi runtime stopped during startup");
|
|
13413
13537
|
}
|
|
13414
|
-
const args = [...describeTool("pi").serverArgs, ...
|
|
13415
|
-
const child = spawn5(this.config.tool.command, args, { cwd: directory, env: { ...process.env }, stdio: ["pipe", "pipe", log.fd] });
|
|
13538
|
+
const args = [...describeTool("pi").serverArgs, ...sessionFile ? ["--session", sessionFile] : ["--no-session"], ...gated ? ["--extension", extension] : []];
|
|
13539
|
+
const child = spawn5(this.config.tool.command, args, { cwd: directory, env: { ...process.env, OCTOPUS_PI_PERMISSION_READY: readyToken }, stdio: ["pipe", "pipe", log.fd] });
|
|
13416
13540
|
child.once("close", () => {
|
|
13417
13541
|
void log.close();
|
|
13418
13542
|
});
|
|
13419
13543
|
this.logPaths.add(logPath);
|
|
13544
|
+
let resolveReady = () => {
|
|
13545
|
+
}, rejectReady = (_error) => {
|
|
13546
|
+
};
|
|
13547
|
+
let readyTimeout;
|
|
13548
|
+
const ready = gated ? new Promise((resolve5, reject) => {
|
|
13549
|
+
resolveReady = resolve5;
|
|
13550
|
+
rejectReady = reject;
|
|
13551
|
+
readyTimeout = setTimeout(() => reject(new Error("Pi \u5DE5\u5177\u6388\u6743\u6269\u5C55\u672A\u5C31\u7EEA\uFF0C\u5DF2\u505C\u6B62\u4F1A\u8BDD\u4EE5\u907F\u514D\u8DF3\u8FC7\u5BA1\u6279")), 1e4);
|
|
13552
|
+
}) : Promise.resolve();
|
|
13420
13553
|
const rpc = new LineRpc(child, {
|
|
13421
13554
|
...handlers,
|
|
13555
|
+
onNotification: (type, data) => {
|
|
13556
|
+
const detail = data;
|
|
13557
|
+
if (gated && type === "extension_ui_request" && detail.method === "notify" && detail.message === readyToken) {
|
|
13558
|
+
resolveReady();
|
|
13559
|
+
return;
|
|
13560
|
+
}
|
|
13561
|
+
handlers.onNotification?.(type, data);
|
|
13562
|
+
},
|
|
13422
13563
|
onClose: (reason) => {
|
|
13564
|
+
rejectReady(new Error("Pi \u5DE5\u5177\u6388\u6743\u8FDE\u63A5\u5DF2\u5173\u95ED"));
|
|
13423
13565
|
handlers.onClose?.(reason);
|
|
13424
13566
|
}
|
|
13425
13567
|
}, "pi");
|
|
13426
13568
|
this.processes.set(key, { child, rpc, projectId, sessionId });
|
|
13427
13569
|
try {
|
|
13428
|
-
await rpc.request("get_state");
|
|
13570
|
+
await Promise.all([rpc.request("get_state"), ready]);
|
|
13429
13571
|
if (generation !== this.generation) throw new Error("Pi runtime stopped during startup");
|
|
13430
13572
|
return rpc;
|
|
13431
13573
|
} catch (error) {
|
|
13432
13574
|
await this.kill(key);
|
|
13433
13575
|
throw error;
|
|
13576
|
+
} finally {
|
|
13577
|
+
clearTimeout(readyTimeout);
|
|
13434
13578
|
}
|
|
13435
13579
|
}
|
|
13436
13580
|
async kill(key) {
|
|
@@ -13594,7 +13738,7 @@ var ProjectRegistry = class {
|
|
|
13594
13738
|
};
|
|
13595
13739
|
|
|
13596
13740
|
// src/terminal.ts
|
|
13597
|
-
import { randomUUID as
|
|
13741
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
13598
13742
|
import { access as access2, stat as stat9 } from "node:fs/promises";
|
|
13599
13743
|
import { constants as constants5 } from "node:fs";
|
|
13600
13744
|
import { platform as platform3, userInfo as userInfo3 } from "node:os";
|
|
@@ -13680,7 +13824,7 @@ var TerminalManager = class {
|
|
|
13680
13824
|
throw new Error(`\u7EC8\u7AEF Shell \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C\uFF1A${shell}\uFF1B\u8BF7\u4FEE\u6B63 Proxy \u670D\u52A1\u7684 SHELL \u73AF\u5883\u53D8\u91CF`);
|
|
13681
13825
|
}
|
|
13682
13826
|
const { helper } = await ensureTerminalSupport();
|
|
13683
|
-
const id =
|
|
13827
|
+
const id = randomUUID17();
|
|
13684
13828
|
const path = [
|
|
13685
13829
|
process.env.PATH,
|
|
13686
13830
|
"/usr/bin",
|
|
@@ -13779,7 +13923,7 @@ var PlatformDesktopSession = class {
|
|
|
13779
13923
|
|
|
13780
13924
|
// src/remote-manager.ts
|
|
13781
13925
|
import { fork } from "node:child_process";
|
|
13782
|
-
import { randomBytes as randomBytes2, randomUUID as
|
|
13926
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID18 } from "node:crypto";
|
|
13783
13927
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
13784
13928
|
|
|
13785
13929
|
// src/remote-stun.ts
|
|
@@ -13913,7 +14057,7 @@ var RemoteManager = class {
|
|
|
13913
14057
|
if (this.current) throw new Error("\u4E3B\u673A\u6B63\u5728\u88AB\u63A7\u5236");
|
|
13914
14058
|
const quality = RemoteQualitySchema.parse(payload.quality ?? "hd");
|
|
13915
14059
|
const budget = remoteSessionBudget(this.config, quality, payload.quality === void 0);
|
|
13916
|
-
const session = { remoteId:
|
|
14060
|
+
const session = { remoteId: randomUUID18(), owner, closed: false, signaling: Promise.resolve(), signals: 0, signalWindow: Date.now(), candidates: 0 };
|
|
13917
14061
|
this.current = session;
|
|
13918
14062
|
try {
|
|
13919
14063
|
const [permissions, stunUrls] = await Promise.all([
|
|
@@ -13992,7 +14136,7 @@ var RemoteManager = class {
|
|
|
13992
14136
|
};
|
|
13993
14137
|
|
|
13994
14138
|
// src/tool-selection.ts
|
|
13995
|
-
import { randomBytes as randomBytes3, randomUUID as
|
|
14139
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID19 } from "node:crypto";
|
|
13996
14140
|
import { access as access3, constants as constants6, readFile as readFile10 } from "node:fs/promises";
|
|
13997
14141
|
import { platform as platform5 } from "node:os";
|
|
13998
14142
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -14136,7 +14280,7 @@ var ToolSelection = class {
|
|
|
14136
14280
|
});
|
|
14137
14281
|
}
|
|
14138
14282
|
modelCommand(projectId) {
|
|
14139
|
-
return { type: "command", id:
|
|
14283
|
+
return { type: "command", id: randomUUID19(), botId: this.config.botId, operation: "configuration.models", projectId, payload: {} };
|
|
14140
14284
|
}
|
|
14141
14285
|
profiles(raw) {
|
|
14142
14286
|
return raw.toolProfiles && typeof raw.toolProfiles === "object" && !Array.isArray(raw.toolProfiles) ? raw.toolProfiles : {};
|
package/dist/cli.js
CHANGED
package/dist/main.js
CHANGED