flavor-code 1.2.1
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/LICENSE +21 -0
- package/README.md +1081 -0
- package/dist/agent/loop.d.ts +31 -0
- package/dist/agent/message-queue.d.ts +19 -0
- package/dist/agent/planner.d.ts +28 -0
- package/dist/agent/subagents.d.ts +50 -0
- package/dist/agent/task-plan.d.ts +60 -0
- package/dist/agent/task-tools.d.ts +11 -0
- package/dist/agent/types.d.ts +117 -0
- package/dist/app-WC2XXGLM.js +3185 -0
- package/dist/app-WC2XXGLM.js.map +1 -0
- package/dist/auth/oauth-config.d.ts +4 -0
- package/dist/auth/oauth.d.ts +21 -0
- package/dist/auth/store.d.ts +15 -0
- package/dist/auth/types.d.ts +33 -0
- package/dist/chunk-BU64GNXE.js +15771 -0
- package/dist/chunk-BU64GNXE.js.map +1 -0
- package/dist/chunk-DM64TM4Z.js +124 -0
- package/dist/chunk-DM64TM4Z.js.map +1 -0
- package/dist/chunk-JZ322RCJ.js +40 -0
- package/dist/chunk-JZ322RCJ.js.map +1 -0
- package/dist/chunk-M744K7MK.js +246 -0
- package/dist/chunk-M744K7MK.js.map +1 -0
- package/dist/chunk-MVIMQEQT.js +692 -0
- package/dist/chunk-MVIMQEQT.js.map +1 -0
- package/dist/chunk-VOCZVFP7.js +153 -0
- package/dist/chunk-VOCZVFP7.js.map +1 -0
- package/dist/chunk-XS2JU6S3.js +35557 -0
- package/dist/chunk-XS2JU6S3.js.map +1 -0
- package/dist/claude-ink-ZL4UZMFY.js +27 -0
- package/dist/claude-ink-ZL4UZMFY.js.map +1 -0
- package/dist/cli.js +915 -0
- package/dist/cli.js.map +1 -0
- package/dist/codeisland/activate.mjs +74 -0
- package/dist/codeisland/bridge.mjs +97 -0
- package/dist/codeisland/flavor-plugin.json +27 -0
- package/dist/config/load.d.ts +19 -0
- package/dist/config/protected-file.d.ts +15 -0
- package/dist/config/schema.d.ts +176 -0
- package/dist/config/secret-envelope.d.ts +8 -0
- package/dist/context/compaction.d.ts +44 -0
- package/dist/context/manager.d.ts +65 -0
- package/dist/context/summarizer.d.ts +15 -0
- package/dist/desktop/main.js +17778 -0
- package/dist/desktop/main.js.map +1 -0
- package/dist/desktop/preload.cjs +90 -0
- package/dist/desktop/preload.cjs.map +1 -0
- package/dist/desktop-renderer/assets/index-C66pDvHs.js +144 -0
- package/dist/desktop-renderer/assets/index-C66pDvHs.js.map +1 -0
- package/dist/desktop-renderer/assets/index-Cm_ktmXm.css +1 -0
- package/dist/desktop-renderer/index.html +16 -0
- package/dist/devtools-SXJBSV2N.js +3 -0
- package/dist/devtools-SXJBSV2N.js.map +1 -0
- package/dist/execution/docker.d.ts +29 -0
- package/dist/execution/factory.d.ts +3 -0
- package/dist/execution/types.d.ts +18 -0
- package/dist/goal/classifier.d.ts +12 -0
- package/dist/goal/orchestrator.d.ts +34 -0
- package/dist/goal/planner.d.ts +9 -0
- package/dist/goal/store.d.ts +9 -0
- package/dist/goal/types.d.ts +190 -0
- package/dist/hallucination/confidence.d.ts +12 -0
- package/dist/hallucination/evidence-ledger.d.ts +29 -0
- package/dist/hallucination/guard.d.ts +27 -0
- package/dist/hallucination/messages.d.ts +10 -0
- package/dist/hallucination/retry-monitor.d.ts +19 -0
- package/dist/hallucination/sliding-window.d.ts +13 -0
- package/dist/hallucination/types.d.ts +49 -0
- package/dist/harness/local.d.ts +54 -0
- package/dist/hooks/bus.d.ts +9 -0
- package/dist/hooks/types.d.ts +105 -0
- package/dist/ide/client.d.ts +35 -0
- package/dist/incidents/reporter.d.ts +38 -0
- package/dist/init/project.d.ts +25 -0
- package/dist/load-WQGNTCHC.js +20 -0
- package/dist/load-WQGNTCHC.js.map +1 -0
- package/dist/loop/budget.d.ts +11 -0
- package/dist/loop/isolation.d.ts +21 -0
- package/dist/loop/orchestrator.d.ts +82 -0
- package/dist/loop/store.d.ts +13 -0
- package/dist/loop/types.d.ts +142 -0
- package/dist/loop/verifier.d.ts +13 -0
- package/dist/mcp/client.d.ts +66 -0
- package/dist/mcp/sdk.d.ts +27 -0
- package/dist/memory/coordinator.d.ts +32 -0
- package/dist/memory/extractor.d.ts +15 -0
- package/dist/memory/intent.d.ts +2 -0
- package/dist/memory/retrieval.d.ts +13 -0
- package/dist/memory/review.d.ts +36 -0
- package/dist/memory/similarity.d.ts +5 -0
- package/dist/memory/store.d.ts +58 -0
- package/dist/memory/types.d.ts +37 -0
- package/dist/models/anthropic.d.ts +24 -0
- package/dist/models/cache-profile.d.ts +23 -0
- package/dist/models/openai.d.ts +24 -0
- package/dist/models/registry.d.ts +16 -0
- package/dist/models/structured.d.ts +75 -0
- package/dist/models/types.d.ts +93 -0
- package/dist/permissions/classifier.d.ts +9 -0
- package/dist/permissions/engine.d.ts +28 -0
- package/dist/plugins/host.d.ts +15 -0
- package/dist/plugins/types.d.ts +136 -0
- package/dist/production.d.ts +59 -0
- package/dist/prompts/system.d.ts +28 -0
- package/dist/rpc/schema.d.ts +61 -0
- package/dist/rpc/server.d.ts +54 -0
- package/dist/sdk/index.d.ts +19 -0
- package/dist/sdk/index.js +53 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/session/checkpoint.d.ts +25 -0
- package/dist/session/store.d.ts +154 -0
- package/dist/session/tree.d.ts +31 -0
- package/dist/skills/builtin-loop.d.ts +12 -0
- package/dist/skills/registry.d.ts +47 -0
- package/dist/skills/tool.d.ts +9 -0
- package/dist/sleep/organizer.d.ts +37 -0
- package/dist/tools/ask-user-question.d.ts +49 -0
- package/dist/tools/file-diff.d.ts +9 -0
- package/dist/tools/files.d.ts +49 -0
- package/dist/tools/lsp.d.ts +77 -0
- package/dist/tools/managed.d.ts +59 -0
- package/dist/tools/runtime.d.ts +58 -0
- package/dist/tools/search.d.ts +76 -0
- package/dist/tools/shell.d.ts +37 -0
- package/dist/tools/task-output.d.ts +53 -0
- package/dist/tools/todo-write.d.ts +35 -0
- package/dist/tools/types.d.ts +56 -0
- package/dist/trace/recorder.d.ts +14 -0
- package/dist/trace/replay.d.ts +3 -0
- package/dist/trace/schema.d.ts +26 -0
- package/dist/ui/commands.d.ts +67 -0
- package/dist/ui/session.d.ts +126 -0
- package/dist/ui/transcript.d.ts +89 -0
- package/dist/utils/async.d.ts +5 -0
- package/dist/utils/envUtils.d.ts +1 -0
- package/dist/utils/error.d.ts +2 -0
- package/dist/utils/execFileNoThrow.d.ts +13 -0
- package/dist/utils/intl.d.ts +18 -0
- package/dist/utils/json.d.ts +44 -0
- package/dist/utils/log.d.ts +28 -0
- package/dist/utils/redact.d.ts +10 -0
- package/dist/utils/usage-summary.d.ts +34 -0
- package/dist/utils/version.d.ts +6 -0
- package/package.json +124 -0
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +4797 -0
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/config/load.ts
|
|
5
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile } from "fs/promises";
|
|
6
|
+
import { join as join2 } from "path";
|
|
7
|
+
import { parse } from "dotenv";
|
|
8
|
+
|
|
9
|
+
// src/config/schema.ts
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
var PERMISSION_MODES = [
|
|
12
|
+
"default",
|
|
13
|
+
"acceptEdits",
|
|
14
|
+
"plan",
|
|
15
|
+
"bypassPermissions",
|
|
16
|
+
"auto",
|
|
17
|
+
"bubble"
|
|
18
|
+
];
|
|
19
|
+
function normalizePermissionMode(value) {
|
|
20
|
+
if (value === "safe" || value === "workspace") return "default";
|
|
21
|
+
if (value === "full") return "bypassPermissions";
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
var PermissionModeSchema = z.preprocess(
|
|
25
|
+
normalizePermissionMode,
|
|
26
|
+
z.enum(PERMISSION_MODES).default("default")
|
|
27
|
+
);
|
|
28
|
+
var ProviderConfigSchema = z.object({
|
|
29
|
+
type: z.string(),
|
|
30
|
+
baseURL: z.string().url().optional(),
|
|
31
|
+
apiKey: z.string().optional(),
|
|
32
|
+
defaultModel: z.string().min(1).optional(),
|
|
33
|
+
cheapModel: z.string().min(1).optional(),
|
|
34
|
+
models: z.array(z.string().min(1)).max(100).optional(),
|
|
35
|
+
maxOutputTokens: z.number().int().positive().optional(),
|
|
36
|
+
// OAuth PKCE fields
|
|
37
|
+
apiType: z.enum(["openai", "anthropic"]).optional(),
|
|
38
|
+
authorizationUrl: z.string().url().optional(),
|
|
39
|
+
tokenUrl: z.string().url().optional(),
|
|
40
|
+
clientId: z.string().min(1).optional(),
|
|
41
|
+
scope: z.string().optional()
|
|
42
|
+
});
|
|
43
|
+
var McpServerCommonShape = {
|
|
44
|
+
disabled: z.boolean().default(false),
|
|
45
|
+
timeoutMs: z.number().int().min(100).max(30 * 6e4).default(6e4)
|
|
46
|
+
};
|
|
47
|
+
var McpStdioServerConfigSchema = z.object({
|
|
48
|
+
command: z.string().min(1),
|
|
49
|
+
args: z.array(z.string()).default([]),
|
|
50
|
+
env: z.record(z.string(), z.string()).default({}),
|
|
51
|
+
cwd: z.string().min(1).optional(),
|
|
52
|
+
...McpServerCommonShape
|
|
53
|
+
}).strict();
|
|
54
|
+
var McpHttpServerConfigSchema = z.object({
|
|
55
|
+
url: z.string().url().refine((value) => {
|
|
56
|
+
const protocol = new URL(value).protocol;
|
|
57
|
+
return protocol === "http:" || protocol === "https:";
|
|
58
|
+
}, "MCP HTTP URLs must use http or https"),
|
|
59
|
+
headers: z.record(z.string(), z.string()).default({}),
|
|
60
|
+
...McpServerCommonShape
|
|
61
|
+
}).strict();
|
|
62
|
+
var McpServerConfigSchema = z.union([
|
|
63
|
+
McpStdioServerConfigSchema,
|
|
64
|
+
McpHttpServerConfigSchema
|
|
65
|
+
]);
|
|
66
|
+
var McpServerNameSchema = z.string().min(1).max(32).regex(/^[A-Za-z0-9_-]+$/, "MCP server names may contain only letters, digits, underscores, and hyphens");
|
|
67
|
+
var SkillNameSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Skill names must use lowercase letters, digits, and single hyphens");
|
|
68
|
+
var FlavorConfigSchema = z.object({
|
|
69
|
+
sleep: z.boolean().default(false),
|
|
70
|
+
providers: z.record(z.string(), ProviderConfigSchema).default({}),
|
|
71
|
+
mcpServers: z.record(McpServerNameSchema, McpServerConfigSchema).default({}),
|
|
72
|
+
skills: z.object({
|
|
73
|
+
disabled: z.array(SkillNameSchema).max(1e3).default([])
|
|
74
|
+
}).prefault({}),
|
|
75
|
+
agents: z.object({
|
|
76
|
+
main: z.object({ model: z.string() }).optional(),
|
|
77
|
+
subagent: z.object({ model: z.string() }).optional()
|
|
78
|
+
}).optional(),
|
|
79
|
+
maxSubagents: z.number().int().min(1).max(16).default(3),
|
|
80
|
+
maxSessions: z.number().int().min(1).max(1e3).default(50),
|
|
81
|
+
memory: z.object({
|
|
82
|
+
enabled: z.boolean().default(true),
|
|
83
|
+
autoExtract: z.boolean().default(true),
|
|
84
|
+
autoExtractMinChars: z.number().int().min(200).max(1e5).default(200),
|
|
85
|
+
scoreThreshold: z.number().int().min(0).max(12).default(9),
|
|
86
|
+
autoStoreThreshold: z.number().int().min(0).max(12).default(11),
|
|
87
|
+
ignoreStreakLimit: z.number().int().min(2).max(100).default(5),
|
|
88
|
+
reviewAutoDismissSeconds: z.number().int().min(0).max(300).default(5),
|
|
89
|
+
maxCandidatesPerTask: z.number().int().min(1).max(10).default(1),
|
|
90
|
+
retrievalTopK: z.number().int().min(1).max(20).default(5),
|
|
91
|
+
maxEntries: z.number().int().min(1).max(1e4).default(200),
|
|
92
|
+
maxEntryChars: z.number().int().min(32).max(2e4).default(1e3),
|
|
93
|
+
maxPromptChars: z.number().int().min(256).max(2e5).default(12e3)
|
|
94
|
+
}).prefault({}),
|
|
95
|
+
permissionMode: PermissionModeSchema,
|
|
96
|
+
execution: z.object({
|
|
97
|
+
mode: z.enum(["local", "docker"]).default("local"),
|
|
98
|
+
image: z.string().trim().min(1).default("node:24-bookworm-slim"),
|
|
99
|
+
network: z.boolean().default(false),
|
|
100
|
+
memory: z.string().regex(/^[1-9][0-9]*(?:[kKmMgG])?$/).default("2g"),
|
|
101
|
+
cpus: z.number().positive().max(64).default(2)
|
|
102
|
+
}).prefault({}),
|
|
103
|
+
language: z.string().regex(/^[a-z]{2}(-[A-Z]{2})?$/, "language must be a BCP47 tag like zh-CN or en-US").optional(),
|
|
104
|
+
hallucination: z.object({
|
|
105
|
+
showWarnings: z.boolean().default(false),
|
|
106
|
+
evaluationTimeoutMs: z.number().int().min(100).max(3e4).default(2e3)
|
|
107
|
+
}).prefault({}),
|
|
108
|
+
maxIterations: z.object({
|
|
109
|
+
main: z.number().int().min(10).max(500).default(80),
|
|
110
|
+
subagent: z.number().int().min(10).max(200).default(40),
|
|
111
|
+
softLimitFactor: z.number().min(0.5).max(1).default(0.8),
|
|
112
|
+
extendBy: z.number().int().min(5).max(100).default(20)
|
|
113
|
+
}).prefault({}),
|
|
114
|
+
loop: z.object({
|
|
115
|
+
maxCycles: z.number().int().positive().max(1e4).default(20),
|
|
116
|
+
maxTokens: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).default(5e5),
|
|
117
|
+
isolation: z.literal("auto").default("auto")
|
|
118
|
+
}).prefault({}),
|
|
119
|
+
context: z.object({
|
|
120
|
+
windowTokens: z.number().int().positive().default(2e5),
|
|
121
|
+
reservedOutputTokens: z.number().int().nonnegative().default(2e4),
|
|
122
|
+
autoCompactBufferTokens: z.number().int().nonnegative().default(13e3),
|
|
123
|
+
warningBufferTokens: z.number().int().nonnegative().default(2e4),
|
|
124
|
+
blockingBufferTokens: z.number().int().nonnegative().default(3e3),
|
|
125
|
+
microcompactKeepRecentToolResults: z.number().int().nonnegative().default(5),
|
|
126
|
+
recentTokens: z.number().int().nonnegative().default(1e4),
|
|
127
|
+
recentTextMessages: z.number().int().nonnegative().default(5),
|
|
128
|
+
maxRecentTokens: z.number().int().nonnegative().default(4e4),
|
|
129
|
+
/** @deprecated Character threshold retained for configuration compatibility. */
|
|
130
|
+
compactAtChars: z.number().int().positive().optional(),
|
|
131
|
+
toolOutputChars: z.number().int().positive().default(3e4)
|
|
132
|
+
}).superRefine((context, issue) => {
|
|
133
|
+
if (context.reservedOutputTokens >= context.windowTokens) issue.addIssue({
|
|
134
|
+
code: "custom",
|
|
135
|
+
path: ["reservedOutputTokens"],
|
|
136
|
+
message: "reservedOutputTokens must be below windowTokens"
|
|
137
|
+
});
|
|
138
|
+
if (context.maxRecentTokens < context.recentTokens) issue.addIssue({
|
|
139
|
+
code: "custom",
|
|
140
|
+
path: ["maxRecentTokens"],
|
|
141
|
+
message: "maxRecentTokens must be at least recentTokens"
|
|
142
|
+
});
|
|
143
|
+
}).prefault({}),
|
|
144
|
+
incidents: z.object({
|
|
145
|
+
enabled: z.boolean().default(false),
|
|
146
|
+
webhookUrl: z.string().url().optional()
|
|
147
|
+
}).prefault({})
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// src/config/protected-file.ts
|
|
151
|
+
import { randomUUID } from "crypto";
|
|
152
|
+
import { mkdir, open, readFile, rename, rm, stat } from "fs/promises";
|
|
153
|
+
import { dirname } from "path";
|
|
154
|
+
var LOCK_WAIT_MS = 20;
|
|
155
|
+
var LOCK_TIMEOUT_MS = 2e3;
|
|
156
|
+
var STALE_LOCK_MS = 3e4;
|
|
157
|
+
async function readRecoverableFile(path, decode) {
|
|
158
|
+
let primaryError;
|
|
159
|
+
try {
|
|
160
|
+
return { value: await decode(await readFile(path, "utf8")), source: path };
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (isCode(error, "ENOENT")) primaryError = error;
|
|
163
|
+
else primaryError = error;
|
|
164
|
+
}
|
|
165
|
+
const backup = `${path}.bak`;
|
|
166
|
+
try {
|
|
167
|
+
return { value: await decode(await readFile(backup, "utf8")), source: backup };
|
|
168
|
+
} catch (backupError) {
|
|
169
|
+
if (isCode(primaryError, "ENOENT") && isCode(backupError, "ENOENT")) return void 0;
|
|
170
|
+
if (isCode(backupError, "ENOENT")) throw primaryError;
|
|
171
|
+
const reason = backupError instanceof Error ? backupError.message : String(backupError);
|
|
172
|
+
const primary = primaryError instanceof Error ? primaryError.message : String(primaryError);
|
|
173
|
+
throw new Error(`Primary and backup files are invalid for ${path}: ${primary}; backup: ${reason}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function updateProtectedFile(options) {
|
|
177
|
+
await mkdir(dirname(options.path), { recursive: true, mode: 448 });
|
|
178
|
+
return withFileLock(options.path, async () => {
|
|
179
|
+
const current = await readRecoverableFile(options.path, options.decode);
|
|
180
|
+
const next = await options.update(current?.value);
|
|
181
|
+
if (current !== void 0) {
|
|
182
|
+
const backup = options.backupEncode === void 0 ? await options.encode(current.value) : await options.backupEncode(current.value);
|
|
183
|
+
await writeAtomic(`${options.path}.bak`, backup);
|
|
184
|
+
}
|
|
185
|
+
await writeAtomic(options.path, await options.encode(next));
|
|
186
|
+
return next;
|
|
187
|
+
}, options.lockTimeoutMs ?? LOCK_TIMEOUT_MS, options.staleLockMs ?? STALE_LOCK_MS);
|
|
188
|
+
}
|
|
189
|
+
async function withFileLock(path, operation, lockTimeoutMs, staleLockMs) {
|
|
190
|
+
const lockPath = `${path}.lock`;
|
|
191
|
+
const token = JSON.stringify({ pid: process.pid, nonce: randomUUID() });
|
|
192
|
+
const deadline = Date.now() + lockTimeoutMs;
|
|
193
|
+
let handle;
|
|
194
|
+
while (handle === void 0) {
|
|
195
|
+
try {
|
|
196
|
+
handle = await open(lockPath, "wx", 384);
|
|
197
|
+
await handle.writeFile(token, "utf8");
|
|
198
|
+
await handle.sync();
|
|
199
|
+
} catch (error) {
|
|
200
|
+
await handle?.close().catch(() => void 0);
|
|
201
|
+
handle = void 0;
|
|
202
|
+
if (!isCode(error, "EEXIST")) throw error;
|
|
203
|
+
await removeStaleLock(lockPath, staleLockMs);
|
|
204
|
+
if (Date.now() >= deadline) throw new Error(`Timed out waiting for configuration lock ${lockPath}`);
|
|
205
|
+
await delay(LOCK_WAIT_MS);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
await handle.close();
|
|
209
|
+
try {
|
|
210
|
+
return await operation();
|
|
211
|
+
} finally {
|
|
212
|
+
try {
|
|
213
|
+
if (await readFile(lockPath, "utf8") === token) await rm(lockPath, { force: true });
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (!isCode(error, "ENOENT")) throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function removeStaleLock(path, staleLockMs) {
|
|
220
|
+
try {
|
|
221
|
+
const token = await readFile(path, "utf8");
|
|
222
|
+
const metadata = await stat(path);
|
|
223
|
+
if (Date.now() - metadata.mtimeMs <= staleLockMs) return;
|
|
224
|
+
const owner = parseLockOwner(token);
|
|
225
|
+
if (owner !== void 0 && isProcessAlive(owner)) return;
|
|
226
|
+
if (await readFile(path, "utf8") !== token) return;
|
|
227
|
+
const stale = `${path}.stale-${process.pid}-${randomUUID()}`;
|
|
228
|
+
try {
|
|
229
|
+
await rename(path, stale);
|
|
230
|
+
await rm(stale, { force: true });
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (!isCode(error, "ENOENT")) return;
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (!isCode(error, "ENOENT")) throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function parseLockOwner(token) {
|
|
239
|
+
try {
|
|
240
|
+
const parsed = JSON.parse(token);
|
|
241
|
+
if (typeof parsed !== "object" || parsed === null || !("pid" in parsed)) return void 0;
|
|
242
|
+
const pid = parsed.pid;
|
|
243
|
+
return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
244
|
+
} catch {
|
|
245
|
+
const legacy = token.match(/^(\d+):/);
|
|
246
|
+
if (legacy?.[1] === void 0) return void 0;
|
|
247
|
+
const pid = Number(legacy[1]);
|
|
248
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function isProcessAlive(pid) {
|
|
252
|
+
try {
|
|
253
|
+
process.kill(pid, 0);
|
|
254
|
+
return true;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return isCode(error, "EPERM");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function writeAtomic(path, content) {
|
|
260
|
+
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
261
|
+
let handle;
|
|
262
|
+
try {
|
|
263
|
+
handle = await open(temporary, "wx", 384);
|
|
264
|
+
await handle.writeFile(content, "utf8");
|
|
265
|
+
await handle.sync();
|
|
266
|
+
await handle.close();
|
|
267
|
+
handle = void 0;
|
|
268
|
+
await rename(temporary, path);
|
|
269
|
+
} finally {
|
|
270
|
+
await handle?.close().catch(() => void 0);
|
|
271
|
+
await rm(temporary, { force: true }).catch(() => void 0);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function delay(ms) {
|
|
275
|
+
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
276
|
+
}
|
|
277
|
+
function isCode(error, code) {
|
|
278
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/config/secret-envelope.ts
|
|
282
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
283
|
+
import { mkdir as mkdir2, open as open2, readFile as readFile2 } from "fs/promises";
|
|
284
|
+
import { dirname as dirname2, join } from "path";
|
|
285
|
+
var FIELD_PREFIX = "flavor:v1:";
|
|
286
|
+
var FILE_PREFIX = "flavor-file:v1:";
|
|
287
|
+
var KEY_BYTES = 32;
|
|
288
|
+
function isSecretField(key) {
|
|
289
|
+
const normalized = key.replace(/[-_.]/g, "").toLowerCase();
|
|
290
|
+
return ["apikey", "authorization", "token", "password", "secret"].some((suffix) => normalized.endsWith(suffix));
|
|
291
|
+
}
|
|
292
|
+
function hasPlainSecretFields(value) {
|
|
293
|
+
if (Array.isArray(value)) return value.some(hasPlainSecretFields);
|
|
294
|
+
if (!isObject(value)) return false;
|
|
295
|
+
return Object.entries(value).some(
|
|
296
|
+
([key, item]) => isSecretField(key) && typeof item === "string" && !item.startsWith(FIELD_PREFIX) || hasPlainSecretFields(item)
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
function encryptSecretFields(value, key) {
|
|
300
|
+
if (Array.isArray(value)) return value.map((item) => encryptSecretFields(item, key));
|
|
301
|
+
if (!isObject(value)) return value;
|
|
302
|
+
return Object.fromEntries(Object.entries(value).map(([name, item]) => [
|
|
303
|
+
name,
|
|
304
|
+
isSecretField(name) && typeof item === "string" ? item.startsWith(FIELD_PREFIX) ? item : `${FIELD_PREFIX}${seal(item, key)}` : encryptSecretFields(item, key)
|
|
305
|
+
]));
|
|
306
|
+
}
|
|
307
|
+
function decryptSecretFields(value, key) {
|
|
308
|
+
if (Array.isArray(value)) return value.map((item) => decryptSecretFields(item, key));
|
|
309
|
+
if (!isObject(value)) return value;
|
|
310
|
+
return Object.fromEntries(Object.entries(value).map(([name, item]) => [
|
|
311
|
+
name,
|
|
312
|
+
isSecretField(name) && typeof item === "string" && item.startsWith(FIELD_PREFIX) ? openEnvelope(item.slice(FIELD_PREFIX.length), key) : decryptSecretFields(item, key)
|
|
313
|
+
]));
|
|
314
|
+
}
|
|
315
|
+
function encryptDocument(value, key) {
|
|
316
|
+
return JSON.stringify(`${FILE_PREFIX}${seal(JSON.stringify(value), key)}`);
|
|
317
|
+
}
|
|
318
|
+
function decryptDocument(raw, key) {
|
|
319
|
+
const parsed = JSON.parse(raw);
|
|
320
|
+
if (typeof parsed === "string" && parsed.startsWith(FILE_PREFIX)) {
|
|
321
|
+
return JSON.parse(openEnvelope(parsed.slice(FILE_PREFIX.length), key));
|
|
322
|
+
}
|
|
323
|
+
return parsed;
|
|
324
|
+
}
|
|
325
|
+
async function loadOrCreateConfigKey(directory) {
|
|
326
|
+
const path = join(directory, ".config.key");
|
|
327
|
+
try {
|
|
328
|
+
return parseKey(await readFile2(path, "utf8"));
|
|
329
|
+
} catch (error) {
|
|
330
|
+
if (!isCode2(error, "ENOENT")) throw error;
|
|
331
|
+
}
|
|
332
|
+
await mkdir2(dirname2(path), { recursive: true, mode: 448 });
|
|
333
|
+
const key = randomBytes(KEY_BYTES);
|
|
334
|
+
let handle;
|
|
335
|
+
try {
|
|
336
|
+
handle = await open2(path, "wx", 384);
|
|
337
|
+
await handle.writeFile(`${key.toString("base64")}
|
|
338
|
+
`, "utf8");
|
|
339
|
+
await handle.sync();
|
|
340
|
+
return key;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (!isCode2(error, "EEXIST")) throw error;
|
|
343
|
+
return parseKey(await readFile2(path, "utf8"));
|
|
344
|
+
} finally {
|
|
345
|
+
await handle?.close().catch(() => void 0);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function seal(plaintext, key) {
|
|
349
|
+
const iv = randomBytes(12);
|
|
350
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
351
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
352
|
+
return [iv, cipher.getAuthTag(), ciphertext].map((part) => part.toString("base64")).join(":");
|
|
353
|
+
}
|
|
354
|
+
function openEnvelope(envelope, key) {
|
|
355
|
+
const parts = envelope.split(":");
|
|
356
|
+
if (parts.length !== 3) throw new Error("Invalid encrypted value envelope");
|
|
357
|
+
const [ivText, tagText, ciphertextText] = parts;
|
|
358
|
+
try {
|
|
359
|
+
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(ivText, "base64"));
|
|
360
|
+
decipher.setAuthTag(Buffer.from(tagText, "base64"));
|
|
361
|
+
return Buffer.concat([
|
|
362
|
+
decipher.update(Buffer.from(ciphertextText, "base64")),
|
|
363
|
+
decipher.final()
|
|
364
|
+
]).toString("utf8");
|
|
365
|
+
} catch (error) {
|
|
366
|
+
throw new Error(`Encrypted value failed authentication: ${error instanceof Error ? error.message : String(error)}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function parseKey(raw) {
|
|
370
|
+
const key = Buffer.from(raw.trim(), "base64");
|
|
371
|
+
if (key.length !== KEY_BYTES) throw new Error("Invalid Flavor configuration key");
|
|
372
|
+
return key;
|
|
373
|
+
}
|
|
374
|
+
function isObject(value) {
|
|
375
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
376
|
+
}
|
|
377
|
+
function isCode2(error, code) {
|
|
378
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/config/load.ts
|
|
382
|
+
function parseConfigObject(path, raw) {
|
|
383
|
+
const parsed = JSON.parse(raw);
|
|
384
|
+
if (!isPlainObject(parsed)) {
|
|
385
|
+
throw new Error(`Configuration file ${path} must contain a JSON object`);
|
|
386
|
+
}
|
|
387
|
+
return parsed;
|
|
388
|
+
}
|
|
389
|
+
async function readJsonIfPresent(path) {
|
|
390
|
+
const result = await readRecoverableFile(path, (raw) => parseConfigObject(path, raw));
|
|
391
|
+
return result === void 0 ? void 0 : { value: result.value, source: result.source };
|
|
392
|
+
}
|
|
393
|
+
async function readTextIfPresent(path) {
|
|
394
|
+
try {
|
|
395
|
+
return await readFile3(path, "utf8");
|
|
396
|
+
} catch (error) {
|
|
397
|
+
if (isMissingFileError(error)) {
|
|
398
|
+
return void 0;
|
|
399
|
+
}
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function isMissingFileError(error) {
|
|
404
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
405
|
+
}
|
|
406
|
+
function isPlainObject(value) {
|
|
407
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
408
|
+
}
|
|
409
|
+
function mergeConfig(base, override) {
|
|
410
|
+
const result = { ...base };
|
|
411
|
+
for (const [key, value] of Object.entries(override)) {
|
|
412
|
+
if (value === void 0) {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const current = result[key];
|
|
416
|
+
result[key] = isPlainObject(current) && isPlainObject(value) ? mergeConfig(current, value) : value;
|
|
417
|
+
}
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
function interpolate(value, environment) {
|
|
421
|
+
if (typeof value === "string") {
|
|
422
|
+
const result = value.replace(
|
|
423
|
+
/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g,
|
|
424
|
+
(match, name) => environment[name] === void 0 ? match : environment[name]
|
|
425
|
+
);
|
|
426
|
+
if (/^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(value) && result === value) {
|
|
427
|
+
return void 0;
|
|
428
|
+
}
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
if (Array.isArray(value)) {
|
|
432
|
+
return value.map((item) => interpolate(item, environment));
|
|
433
|
+
}
|
|
434
|
+
if (isPlainObject(value)) {
|
|
435
|
+
return Object.fromEntries(
|
|
436
|
+
Object.entries(value).map(([key, item]) => [key, interpolate(item, environment)])
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
return value;
|
|
440
|
+
}
|
|
441
|
+
var FLAVOR_ENV_VARS = [
|
|
442
|
+
"ANTHROPIC_BASE_URL",
|
|
443
|
+
"ANTHROPIC_API_KEY",
|
|
444
|
+
"OPENAI_API_KEY",
|
|
445
|
+
"OAUTH_AUTHORIZATION_URL",
|
|
446
|
+
"OAUTH_TOKEN_URL",
|
|
447
|
+
"OAUTH_CALLBACK_HOST",
|
|
448
|
+
"OAUTH_CLIENT_ID",
|
|
449
|
+
"OAUTH_SCOPE"
|
|
450
|
+
];
|
|
451
|
+
async function seedGlobalEnv(globalEnvPath) {
|
|
452
|
+
try {
|
|
453
|
+
await readFile3(globalEnvPath, "utf8");
|
|
454
|
+
return;
|
|
455
|
+
} catch (error) {
|
|
456
|
+
if (!isMissingFileError(error)) return;
|
|
457
|
+
}
|
|
458
|
+
const lines = [];
|
|
459
|
+
for (const key of FLAVOR_ENV_VARS) {
|
|
460
|
+
const value = process.env[key];
|
|
461
|
+
if (value !== void 0 && value !== "") {
|
|
462
|
+
lines.push(`${key}=${value}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (lines.length === 0) return;
|
|
466
|
+
try {
|
|
467
|
+
await mkdir3(join2(globalEnvPath, ".."), { recursive: true });
|
|
468
|
+
await writeFile(globalEnvPath, `${lines.join("\n")}
|
|
469
|
+
`, "utf8");
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
async function loadConfig(options) {
|
|
474
|
+
const globalPath = join2(options.home, ".flavor-code", "flavor.json");
|
|
475
|
+
const globalEnvPath = join2(options.home, ".flavor-code", ".env");
|
|
476
|
+
const envPath = join2(options.cwd, ".env");
|
|
477
|
+
const projectPath = join2(options.cwd, ".flavor", "flavor.json");
|
|
478
|
+
const sources = [];
|
|
479
|
+
const keyDirectory = join2(options.home, ".flavor-code");
|
|
480
|
+
let keyPromise;
|
|
481
|
+
const getKey = () => keyPromise ??= loadOrCreateConfigKey(keyDirectory);
|
|
482
|
+
const decodeGlobal = async (raw) => {
|
|
483
|
+
const stored = parseConfigObject(globalPath, raw);
|
|
484
|
+
const hasPlainSecrets = hasPlainSecretFields(stored);
|
|
485
|
+
const config = decryptSecretFields(stored, await getKey());
|
|
486
|
+
return { config, hasPlainSecrets };
|
|
487
|
+
};
|
|
488
|
+
const globalRead = await readRecoverableFile(globalPath, decodeGlobal);
|
|
489
|
+
let globalConfig = globalRead?.value.config;
|
|
490
|
+
if (globalRead?.value.hasPlainSecrets) {
|
|
491
|
+
const key = await getKey();
|
|
492
|
+
const protectedConfig = await updateProtectedFile({
|
|
493
|
+
path: globalPath,
|
|
494
|
+
decode: decodeGlobal,
|
|
495
|
+
encode: (value) => `${JSON.stringify(encryptSecretFields(value.config, key), null, 2)}
|
|
496
|
+
`,
|
|
497
|
+
backupEncode: (value) => `${JSON.stringify(encryptSecretFields(value.config, key), null, 2)}
|
|
498
|
+
`,
|
|
499
|
+
update: (current) => current ?? { config: {}, hasPlainSecrets: false }
|
|
500
|
+
});
|
|
501
|
+
globalConfig = protectedConfig.config;
|
|
502
|
+
}
|
|
503
|
+
if (globalRead) sources.push(globalRead.source);
|
|
504
|
+
const globalEnvText = await readTextIfPresent(globalEnvPath);
|
|
505
|
+
const projectEnvText = await readTextIfPresent(envPath);
|
|
506
|
+
const projectEnvironment = {
|
|
507
|
+
...globalEnvText === void 0 ? {} : parse(globalEnvText),
|
|
508
|
+
...projectEnvText === void 0 ? {} : parse(projectEnvText)
|
|
509
|
+
};
|
|
510
|
+
if (globalEnvText !== void 0) sources.push(globalEnvPath);
|
|
511
|
+
if (projectEnvText !== void 0) sources.push(envPath);
|
|
512
|
+
for (const [key, value] of Object.entries(projectEnvironment)) {
|
|
513
|
+
if (value !== void 0 && process.env[key] === void 0) {
|
|
514
|
+
process.env[key] = value;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
await seedGlobalEnv(globalEnvPath);
|
|
518
|
+
const projectRead = await readJsonIfPresent(projectPath);
|
|
519
|
+
const projectConfig = projectRead?.value;
|
|
520
|
+
if (projectRead) sources.push(projectRead.source);
|
|
521
|
+
const merged = mergeConfig(
|
|
522
|
+
mergeConfig(globalConfig ?? {}, projectConfig ?? {}),
|
|
523
|
+
options.cli ?? {}
|
|
524
|
+
);
|
|
525
|
+
const interpolated = interpolate(merged, {
|
|
526
|
+
...options.environment ?? process.env,
|
|
527
|
+
...projectEnvironment
|
|
528
|
+
});
|
|
529
|
+
return {
|
|
530
|
+
config: FlavorConfigSchema.parse(interpolated),
|
|
531
|
+
sources
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
async function setProjectMcpServerDisabled(cwd, serverName, disabled) {
|
|
535
|
+
const directory = join2(cwd, ".flavor");
|
|
536
|
+
const path = join2(directory, "flavor.json");
|
|
537
|
+
await updateProtectedFile({
|
|
538
|
+
path,
|
|
539
|
+
decode: (raw) => parseConfigObject(path, raw),
|
|
540
|
+
encode: (value) => `${JSON.stringify(value, null, 2)}
|
|
541
|
+
`,
|
|
542
|
+
update: (current) => {
|
|
543
|
+
const projectConfig = { ...current ?? {} };
|
|
544
|
+
const currentServers = projectConfig["mcpServers"];
|
|
545
|
+
if (currentServers !== void 0 && !isPlainObject(currentServers)) {
|
|
546
|
+
throw new Error(`Configuration field mcpServers in ${path} must be an object`);
|
|
547
|
+
}
|
|
548
|
+
const servers = { ...currentServers ?? {} };
|
|
549
|
+
const currentServer = servers[serverName];
|
|
550
|
+
if (currentServer !== void 0 && !isPlainObject(currentServer)) {
|
|
551
|
+
throw new Error(`MCP server ${serverName} in ${path} must be an object`);
|
|
552
|
+
}
|
|
553
|
+
servers[serverName] = { ...currentServer ?? {}, disabled };
|
|
554
|
+
projectConfig["mcpServers"] = servers;
|
|
555
|
+
return projectConfig;
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
return path;
|
|
559
|
+
}
|
|
560
|
+
async function setProjectSkillDisabled(cwd, skillName, disabled, inheritedDisabled = []) {
|
|
561
|
+
const directory = join2(cwd, ".flavor");
|
|
562
|
+
const path = join2(directory, "flavor.json");
|
|
563
|
+
await updateProtectedFile({
|
|
564
|
+
path,
|
|
565
|
+
decode: (raw) => parseConfigObject(path, raw),
|
|
566
|
+
encode: (value) => `${JSON.stringify(value, null, 2)}
|
|
567
|
+
`,
|
|
568
|
+
update: (current) => {
|
|
569
|
+
const projectConfig = { ...current ?? {} };
|
|
570
|
+
const currentSkills = projectConfig["skills"];
|
|
571
|
+
if (currentSkills !== void 0 && !isPlainObject(currentSkills)) {
|
|
572
|
+
throw new Error(`Configuration field skills in ${path} must be an object`);
|
|
573
|
+
}
|
|
574
|
+
const skills = { ...currentSkills ?? {} };
|
|
575
|
+
const currentDisabled = skills["disabled"];
|
|
576
|
+
if (currentDisabled !== void 0 && (!Array.isArray(currentDisabled) || currentDisabled.some((name) => typeof name !== "string"))) {
|
|
577
|
+
throw new Error(`Configuration field skills.disabled in ${path} must be a string array`);
|
|
578
|
+
}
|
|
579
|
+
const names = new Set(currentDisabled ?? inheritedDisabled);
|
|
580
|
+
if (disabled) names.add(skillName);
|
|
581
|
+
else names.delete(skillName);
|
|
582
|
+
skills["disabled"] = [...names].sort();
|
|
583
|
+
projectConfig["skills"] = skills;
|
|
584
|
+
return projectConfig;
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
return path;
|
|
588
|
+
}
|
|
589
|
+
async function setGlobalProviderConfig(home, providerName, provider) {
|
|
590
|
+
const directory = join2(home, ".flavor-code");
|
|
591
|
+
const path = join2(directory, "flavor.json");
|
|
592
|
+
const key = await loadOrCreateConfigKey(directory);
|
|
593
|
+
const validatedProvider = ProviderConfigSchema.parse(provider);
|
|
594
|
+
await updateProtectedFile({
|
|
595
|
+
path,
|
|
596
|
+
decode: (raw) => decryptSecretFields(parseConfigObject(path, raw), key),
|
|
597
|
+
encode: (value) => `${JSON.stringify(encryptSecretFields(value, key), null, 2)}
|
|
598
|
+
`,
|
|
599
|
+
update: (current) => {
|
|
600
|
+
const globalConfig = { ...current ?? {} };
|
|
601
|
+
const currentProviders = globalConfig["providers"];
|
|
602
|
+
if (currentProviders !== void 0 && !isPlainObject(currentProviders)) {
|
|
603
|
+
throw new Error(`Configuration field providers in ${path} must be an object`);
|
|
604
|
+
}
|
|
605
|
+
const providers = { ...currentProviders ?? {} };
|
|
606
|
+
const existing = providers[providerName];
|
|
607
|
+
if (existing !== void 0 && !isPlainObject(existing)) {
|
|
608
|
+
throw new Error(`Provider ${providerName} in ${path} must be an object`);
|
|
609
|
+
}
|
|
610
|
+
const existingModels = isPlainObject(existing) && Array.isArray(existing["models"]) ? existing["models"].filter((model) => typeof model === "string") : [];
|
|
611
|
+
providers[providerName] = {
|
|
612
|
+
...existing ?? {},
|
|
613
|
+
...validatedProvider,
|
|
614
|
+
...validatedProvider.models === void 0 ? {} : { models: [.../* @__PURE__ */ new Set([...existingModels, ...validatedProvider.models])] }
|
|
615
|
+
};
|
|
616
|
+
globalConfig["providers"] = providers;
|
|
617
|
+
return globalConfig;
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
return path;
|
|
621
|
+
}
|
|
622
|
+
async function setProjectProviderConfig(cwd, providerName, provider) {
|
|
623
|
+
const directory = join2(cwd, ".flavor");
|
|
624
|
+
const path = join2(directory, "flavor.json");
|
|
625
|
+
const validatedProvider = ProviderConfigSchema.parse(provider);
|
|
626
|
+
const projectProvider = { ...validatedProvider };
|
|
627
|
+
delete projectProvider.apiKey;
|
|
628
|
+
await updateProtectedFile({
|
|
629
|
+
path,
|
|
630
|
+
decode: (raw) => parseConfigObject(path, raw),
|
|
631
|
+
encode: (value) => `${JSON.stringify(value, null, 2)}
|
|
632
|
+
`,
|
|
633
|
+
update: (current) => {
|
|
634
|
+
const projectConfig = { ...current ?? {} };
|
|
635
|
+
const currentProviders = projectConfig["providers"];
|
|
636
|
+
if (currentProviders !== void 0 && !isPlainObject(currentProviders)) {
|
|
637
|
+
throw new Error(`Configuration field providers in ${path} must be an object`);
|
|
638
|
+
}
|
|
639
|
+
const providers = { ...currentProviders ?? {} };
|
|
640
|
+
const existing = providers[providerName];
|
|
641
|
+
if (existing !== void 0 && !isPlainObject(existing)) {
|
|
642
|
+
throw new Error(`Provider ${providerName} in ${path} must be an object`);
|
|
643
|
+
}
|
|
644
|
+
const existingModels = isPlainObject(existing) && Array.isArray(existing["models"]) ? existing["models"].filter((model) => typeof model === "string") : [];
|
|
645
|
+
const nextProvider = {
|
|
646
|
+
...existing ?? {},
|
|
647
|
+
...projectProvider,
|
|
648
|
+
...projectProvider.models === void 0 ? {} : { models: [.../* @__PURE__ */ new Set([...existingModels, ...projectProvider.models])] }
|
|
649
|
+
};
|
|
650
|
+
delete nextProvider["apiKey"];
|
|
651
|
+
providers[providerName] = nextProvider;
|
|
652
|
+
projectConfig["providers"] = providers;
|
|
653
|
+
return projectConfig;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
return path;
|
|
657
|
+
}
|
|
658
|
+
function redactConfig(config) {
|
|
659
|
+
if (Array.isArray(config)) {
|
|
660
|
+
return config.map((item) => redactConfig(item));
|
|
661
|
+
}
|
|
662
|
+
if (!isPlainObject(config)) {
|
|
663
|
+
return config;
|
|
664
|
+
}
|
|
665
|
+
return Object.fromEntries(
|
|
666
|
+
Object.entries(config).map(([key, value]) => [
|
|
667
|
+
key,
|
|
668
|
+
isSecretField(key) ? "[redacted]" : redactConfig(value)
|
|
669
|
+
])
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
export {
|
|
674
|
+
PERMISSION_MODES,
|
|
675
|
+
normalizePermissionMode,
|
|
676
|
+
PermissionModeSchema,
|
|
677
|
+
McpServerConfigSchema,
|
|
678
|
+
McpServerNameSchema,
|
|
679
|
+
SkillNameSchema,
|
|
680
|
+
readRecoverableFile,
|
|
681
|
+
updateProtectedFile,
|
|
682
|
+
encryptDocument,
|
|
683
|
+
decryptDocument,
|
|
684
|
+
loadOrCreateConfigKey,
|
|
685
|
+
loadConfig,
|
|
686
|
+
setProjectMcpServerDisabled,
|
|
687
|
+
setProjectSkillDisabled,
|
|
688
|
+
setGlobalProviderConfig,
|
|
689
|
+
setProjectProviderConfig,
|
|
690
|
+
redactConfig
|
|
691
|
+
};
|
|
692
|
+
//# sourceMappingURL=chunk-MVIMQEQT.js.map
|