flavor-code 1.2.4 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +369 -367
- package/README.zh-CN.md +369 -0
- package/dist/{app-N3655H2E.js → app-3DXEGSS7.js} +10 -3
- package/dist/{chunk-MRISY6KK.js → chunk-3TYRDK34.js} +33 -28
- package/dist/{chunk-L5A2NDMT.js → chunk-Z3BW52MK.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/desktop/main.js +33 -28
- package/dist/sdk/index.js +2 -2
- package/package.json +2 -1
package/dist/desktop/main.js
CHANGED
|
@@ -2953,10 +2953,12 @@ function getToolCategory(name) {
|
|
|
2953
2953
|
return isNetworkTool(name) ? "network" : CATEGORY_MAP[name] ?? "unknown";
|
|
2954
2954
|
}
|
|
2955
2955
|
var PermissionEngine = class {
|
|
2956
|
+
#lexicalWorkspace;
|
|
2956
2957
|
#workspace;
|
|
2957
2958
|
#mode;
|
|
2958
2959
|
constructor(options) {
|
|
2959
2960
|
const root = resolve2(options.workspace);
|
|
2961
|
+
this.#lexicalWorkspace = root;
|
|
2960
2962
|
this.#workspace = existsSync(root) ? realpathSync.native(root) : root;
|
|
2961
2963
|
this.#mode = canonicalMode(options.mode ?? "default");
|
|
2962
2964
|
}
|
|
@@ -2977,13 +2979,13 @@ var PermissionEngine = class {
|
|
|
2977
2979
|
return { decision: "deny", reason: `${request.tool} requires source and destination paths` };
|
|
2978
2980
|
}
|
|
2979
2981
|
for (const path of paths) {
|
|
2980
|
-
const classification = classifyPath(this.#workspace, path);
|
|
2982
|
+
const classification = classifyPath(this.#lexicalWorkspace, this.#workspace, path);
|
|
2981
2983
|
if (classification.escape) return { decision: "deny", reason: classification.reason ?? "Path escapes the workspace" };
|
|
2982
2984
|
if (request.agent === "subagent" && !classification.inside) {
|
|
2983
2985
|
return { decision: "deny", reason: "Subagents are restricted to the workspace" };
|
|
2984
2986
|
}
|
|
2985
2987
|
}
|
|
2986
|
-
const inside = paths.every((path) => classifyPath(this.#workspace, path).inside);
|
|
2988
|
+
const inside = paths.every((path) => classifyPath(this.#lexicalWorkspace, this.#workspace, path).inside);
|
|
2987
2989
|
if (this.#mode === "plan") {
|
|
2988
2990
|
return READ_TOOLS.has(request.tool) ? { decision: "allow" } : { decision: "deny", reason: "Plan mode is read-only" };
|
|
2989
2991
|
}
|
|
@@ -3010,11 +3012,11 @@ var PermissionEngine = class {
|
|
|
3010
3012
|
const analysis = request.args === void 0 ? analyzeCommand(request.command ?? "") : analyzeArgumentCommand(request.command ?? "", request.args);
|
|
3011
3013
|
if (request.agent === "subagent") {
|
|
3012
3014
|
if (request.cwd === void 0) return { decision: "ask", reason: "Subagent shell commands require an explicit workspace cwd" };
|
|
3013
|
-
const cwd = classifyPath(this.#workspace, request.cwd);
|
|
3015
|
+
const cwd = classifyPath(this.#lexicalWorkspace, this.#workspace, request.cwd);
|
|
3014
3016
|
if (cwd.escape || !cwd.inside) return { decision: "deny", reason: "Subagent shell cwd must remain in the workspace" };
|
|
3015
3017
|
if (analysis.destructive) return { decision: "deny", reason: "Destructive commands are forbidden for subagents" };
|
|
3016
3018
|
if (analysis.wrapped || analysis.opaque || !isRoutineCommand(analysis.command)) return { decision: "ask", reason: "Subagent shell command requires main-Agent approval" };
|
|
3017
|
-
const argumentDecision = assessRoutineArguments(analysis.command, request.cwd, this.#workspace);
|
|
3019
|
+
const argumentDecision = assessRoutineArguments(analysis.command, request.cwd, this.#lexicalWorkspace, this.#workspace);
|
|
3018
3020
|
if (argumentDecision !== "allow") {
|
|
3019
3021
|
return argumentDecision === "deny" ? { decision: "deny", reason: "Subagent command arguments escape the workspace" } : { decision: "ask", reason: "Ambiguous subagent command arguments require main-Agent approval" };
|
|
3020
3022
|
}
|
|
@@ -3026,12 +3028,12 @@ var PermissionEngine = class {
|
|
|
3026
3028
|
if (analysis.wrapped || analysis.opaque) return ask(this.#mode, "Shell wrapper requires approval");
|
|
3027
3029
|
if (this.#mode === "bypassPermissions") return { decision: "allow" };
|
|
3028
3030
|
if ((this.#mode === "acceptEdits" || this.#mode === "auto") && isRoutineCommand(analysis.command)) {
|
|
3029
|
-
const cwd = request.cwd ?? this.#
|
|
3030
|
-
const cwdClassification = classifyPath(this.#workspace, cwd);
|
|
3031
|
+
const cwd = request.cwd ?? this.#lexicalWorkspace;
|
|
3032
|
+
const cwdClassification = classifyPath(this.#lexicalWorkspace, this.#workspace, cwd);
|
|
3031
3033
|
if (cwdClassification.escape || !cwdClassification.inside) {
|
|
3032
3034
|
return { decision: "deny", reason: "Routine command cwd must remain in the workspace" };
|
|
3033
3035
|
}
|
|
3034
|
-
const argumentDecision = assessRoutineArguments(analysis.command, cwd, this.#workspace);
|
|
3036
|
+
const argumentDecision = assessRoutineArguments(analysis.command, cwd, this.#lexicalWorkspace, this.#workspace);
|
|
3035
3037
|
if (argumentDecision === "deny") return { decision: "deny", reason: "Routine command arguments escape the workspace" };
|
|
3036
3038
|
if (argumentDecision === "ask") return ask(this.#mode, "Ambiguous routine command arguments require approval");
|
|
3037
3039
|
return { decision: "allow" };
|
|
@@ -3068,16 +3070,16 @@ function analyzeArgumentCommand(executable, args) {
|
|
|
3068
3070
|
}
|
|
3069
3071
|
return { command, destructive: isForbiddenCommand(command), opaque: false, wrapped: false };
|
|
3070
3072
|
}
|
|
3071
|
-
function classifyPath(
|
|
3072
|
-
const candidate = resolve2(
|
|
3073
|
-
const lexicalInside = isWithin(
|
|
3073
|
+
function classifyPath(lexicalWorkspace, physicalWorkspace, input) {
|
|
3074
|
+
const candidate = resolve2(lexicalWorkspace, input);
|
|
3075
|
+
const lexicalInside = isWithin(lexicalWorkspace, candidate);
|
|
3074
3076
|
const traversal = input.split(/[\\/]+/).includes("..");
|
|
3075
|
-
const startsInWorkspace = pathStartsWith(
|
|
3077
|
+
const startsInWorkspace = pathStartsWith(lexicalWorkspace, input);
|
|
3076
3078
|
if ((!isAbsolute(input) || startsInWorkspace) && traversal && !lexicalInside) {
|
|
3077
3079
|
return { inside: false, escape: true, reason: "Path traversal escapes the workspace" };
|
|
3078
3080
|
}
|
|
3079
3081
|
const physical = resolvePhysical(candidate);
|
|
3080
|
-
const physicalInside = isWithin(
|
|
3082
|
+
const physicalInside = isWithin(physicalWorkspace, physical);
|
|
3081
3083
|
if (lexicalInside && !physicalInside) return { inside: false, escape: true, reason: "Symlink escapes the workspace" };
|
|
3082
3084
|
return { inside: physicalInside, escape: false };
|
|
3083
3085
|
}
|
|
@@ -3167,7 +3169,7 @@ function isRoutineCommand(command) {
|
|
|
3167
3169
|
if (command.executable === "go") return first === "test";
|
|
3168
3170
|
return ["pytest", "vitest", "jest", "eslint", "tsc"].includes(command.executable);
|
|
3169
3171
|
}
|
|
3170
|
-
function assessRoutineArguments(command, cwd,
|
|
3172
|
+
function assessRoutineArguments(command, cwd, lexicalWorkspace, physicalWorkspace) {
|
|
3171
3173
|
const args = routineArguments(command);
|
|
3172
3174
|
if (args === void 0) return "ask";
|
|
3173
3175
|
const pathOptions = /* @__PURE__ */ new Set([
|
|
@@ -3192,7 +3194,7 @@ function assessRoutineArguments(command, cwd, workspace) {
|
|
|
3192
3194
|
const option2 = equals[1]?.toLowerCase() ?? "";
|
|
3193
3195
|
const value = equals[2] ?? "";
|
|
3194
3196
|
if (!pathOptions.has(option2)) return "ask";
|
|
3195
|
-
const decision = assessArgumentPath(value, cwd,
|
|
3197
|
+
const decision = assessArgumentPath(value, cwd, lexicalWorkspace, physicalWorkspace);
|
|
3196
3198
|
if (decision !== "allow") return decision;
|
|
3197
3199
|
continue;
|
|
3198
3200
|
}
|
|
@@ -3200,14 +3202,14 @@ function assessRoutineArguments(command, cwd, workspace) {
|
|
|
3200
3202
|
if (option !== void 0 && pathOptions.has(option)) {
|
|
3201
3203
|
const value = args[index + 1];
|
|
3202
3204
|
if (value === void 0 || value.startsWith("-")) return "ask";
|
|
3203
|
-
const decision = assessArgumentPath(value, cwd,
|
|
3205
|
+
const decision = assessArgumentPath(value, cwd, lexicalWorkspace, physicalWorkspace);
|
|
3204
3206
|
if (decision !== "allow") return decision;
|
|
3205
3207
|
index += 1;
|
|
3206
3208
|
continue;
|
|
3207
3209
|
}
|
|
3208
3210
|
if (arg.startsWith("-")) continue;
|
|
3209
3211
|
if (looksLikePath(arg)) {
|
|
3210
|
-
const decision = assessArgumentPath(arg, cwd,
|
|
3212
|
+
const decision = assessArgumentPath(arg, cwd, lexicalWorkspace, physicalWorkspace);
|
|
3211
3213
|
if (decision !== "allow") return decision;
|
|
3212
3214
|
continue;
|
|
3213
3215
|
}
|
|
@@ -3225,10 +3227,10 @@ function routineArguments(command) {
|
|
|
3225
3227
|
if (["pytest", "vitest", "jest", "eslint", "tsc"].includes(command.executable)) return command.args;
|
|
3226
3228
|
return void 0;
|
|
3227
3229
|
}
|
|
3228
|
-
function assessArgumentPath(value, cwd,
|
|
3230
|
+
function assessArgumentPath(value, cwd, lexicalWorkspace, physicalWorkspace) {
|
|
3229
3231
|
if (value.length === 0) return "ask";
|
|
3230
3232
|
const resolved = resolve2(cwd, value);
|
|
3231
|
-
const classification = classifyPath(
|
|
3233
|
+
const classification = classifyPath(lexicalWorkspace, physicalWorkspace, resolved);
|
|
3232
3234
|
return classification.escape || !classification.inside ? "deny" : "allow";
|
|
3233
3235
|
}
|
|
3234
3236
|
function looksLikePath(value) {
|
|
@@ -3477,7 +3479,8 @@ var ToolRuntime = class {
|
|
|
3477
3479
|
} catch {
|
|
3478
3480
|
}
|
|
3479
3481
|
}
|
|
3480
|
-
|
|
3482
|
+
const sessionAllowed = this.#alwaysAllowed.has(getToolCategory(tool.name));
|
|
3483
|
+
if (!sessionAllowed && (pre.decision === "ask" || permission.decision === "ask")) {
|
|
3481
3484
|
const reason = [pre.reason, permission.reason].filter((value) => value !== void 0).join("\n") || "Approval required";
|
|
3482
3485
|
const requestDecision = await this.#hooks.emit({
|
|
3483
3486
|
version: 1,
|
|
@@ -3502,8 +3505,7 @@ var ToolRuntime = class {
|
|
|
3502
3505
|
throw signal.reason;
|
|
3503
3506
|
} else {
|
|
3504
3507
|
const category = getToolCategory(tool.name);
|
|
3505
|
-
if (this.#
|
|
3506
|
-
} else if (this.#approve === void 0) {
|
|
3508
|
+
if (this.#approve === void 0) {
|
|
3507
3509
|
return this.#fail(tool.name, input, context.agent, "permission_denied", reason);
|
|
3508
3510
|
} else {
|
|
3509
3511
|
const decision = await this.#approve({ ...request, reason }, signal);
|
|
@@ -8251,9 +8253,11 @@ var SkillRegistry = class {
|
|
|
8251
8253
|
for (const configuredRoot of roots) {
|
|
8252
8254
|
const root = resolve11(configuredRoot);
|
|
8253
8255
|
let entries;
|
|
8256
|
+
let physicalRoot = root;
|
|
8254
8257
|
try {
|
|
8255
8258
|
const rootInfo = await lstat5(root);
|
|
8256
8259
|
if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory()) throw new Error("Skill root must be a real directory");
|
|
8260
|
+
physicalRoot = await realpath4(root);
|
|
8257
8261
|
entries = await readdir4(root, { withFileTypes: true });
|
|
8258
8262
|
} catch (error) {
|
|
8259
8263
|
if (isMissing(error)) continue;
|
|
@@ -8266,12 +8270,12 @@ var SkillRegistry = class {
|
|
|
8266
8270
|
try {
|
|
8267
8271
|
if (entry2.isSymbolicLink()) throw new Error("Symlinked skill directories are not allowed");
|
|
8268
8272
|
if (!entry2.isDirectory()) continue;
|
|
8269
|
-
const
|
|
8270
|
-
if (!isWithin3(
|
|
8273
|
+
const physicalSkillRoot = await realpath4(skillRoot);
|
|
8274
|
+
if (!isWithin3(physicalRoot, physicalSkillRoot)) throw new Error("Skill directory escapes its registry root");
|
|
8271
8275
|
const skillFile = resolve11(skillRoot, "SKILL.md");
|
|
8272
8276
|
const parsed = await readFrontmatterFile(
|
|
8273
8277
|
skillFile,
|
|
8274
|
-
|
|
8278
|
+
physicalSkillRoot,
|
|
8275
8279
|
this.#options.maxMetadataBytes,
|
|
8276
8280
|
this.#options.openFile
|
|
8277
8281
|
);
|
|
@@ -8282,7 +8286,7 @@ var SkillRegistry = class {
|
|
|
8282
8286
|
name: parsed.name,
|
|
8283
8287
|
description: parsed.description,
|
|
8284
8288
|
source,
|
|
8285
|
-
root:
|
|
8289
|
+
root: physicalSkillRoot,
|
|
8286
8290
|
disableModelInvocation: parsed.disableModelInvocation
|
|
8287
8291
|
}),
|
|
8288
8292
|
skillFile
|
|
@@ -10513,6 +10517,7 @@ function createEditTool(workspace, options = {}) {
|
|
|
10513
10517
|
paths: (input) => [guard.lexical(input.path)],
|
|
10514
10518
|
execute: async (input, signal) => {
|
|
10515
10519
|
abortIfNeeded(signal);
|
|
10520
|
+
const displayPath = guard.lexical(input.path);
|
|
10516
10521
|
const path = await guard.existing(input.path);
|
|
10517
10522
|
const contents = await readText(path);
|
|
10518
10523
|
const hasCRLF = contents.includes("\r\n");
|
|
@@ -10528,11 +10533,11 @@ function createEditTool(workspace, options = {}) {
|
|
|
10528
10533
|
}
|
|
10529
10534
|
const updatedLF = contentsLF.slice(0, first) + newTextLF + contentsLF.slice(first + oldTextLF.length);
|
|
10530
10535
|
const updated = hasCRLF ? updatedLF.replace(/\n/g, "\r\n") : updatedLF;
|
|
10531
|
-
await options.beforeCommit?.({ path, before: contents, after: updated, kind: "update" }, signal);
|
|
10536
|
+
await options.beforeCommit?.({ path: displayPath, before: contents, after: updated, kind: "update" }, signal);
|
|
10532
10537
|
await atomicWrite(path, updated, signal);
|
|
10533
10538
|
return withToolPresentation(
|
|
10534
|
-
{ path, replacements: 1 },
|
|
10535
|
-
buildFileChangePresentation(
|
|
10539
|
+
{ path: displayPath, replacements: 1 },
|
|
10540
|
+
buildFileChangePresentation(displayPath, contents, updated, "update")
|
|
10536
10541
|
);
|
|
10537
10542
|
}
|
|
10538
10543
|
};
|
package/dist/sdk/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
FlavorRpcServer,
|
|
5
5
|
TraceRecordSchema,
|
|
6
6
|
TraceRecorder
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-Z3BW52MK.js";
|
|
8
8
|
import {
|
|
9
9
|
AgentMessageQueue,
|
|
10
10
|
DockerExecutionEnvironment,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
WorkspaceCheckpointStore,
|
|
14
14
|
buildDockerInvocation,
|
|
15
15
|
createProductionRuntime
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-3TYRDK34.js";
|
|
17
17
|
import "../chunk-HFR2WS6T.js";
|
|
18
18
|
import "../chunk-N2S7USST.js";
|
|
19
19
|
import "../chunk-S32JRDZK.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flavor-code",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"author": "YachuanWzh",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"dist/",
|
|
22
22
|
"!dist/**/*.map",
|
|
23
23
|
"README.md",
|
|
24
|
+
"README.zh-CN.md",
|
|
24
25
|
"技术方案报告.md",
|
|
25
26
|
"LICENSE"
|
|
26
27
|
],
|