flavor-code 1.3.4 → 1.3.7
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 +22 -19
- package/README.zh-CN.md +22 -19
- package/dist/{app-EHB3XAQY.js → app-NHRRG3YA.js} +1 -1
- package/dist/{chunk-YKSMZDUZ.js → chunk-K2AOT2BI.js} +452 -183
- package/dist/{chunk-RDBTC35M.js → chunk-VPJ7KY3T.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/desktop/main.js +678 -400
- package/dist/hooks/bus.d.ts +7 -0
- package/dist/island/control-server.d.ts +21 -0
- package/dist/production.d.ts +4 -0
- package/dist/sdk/index.js +2 -2
- package/dist/tools/ask-user-question.d.ts +4 -1
- package/dist/tools/types.d.ts +2 -0
- package/package.json +2 -2
package/dist/desktop/main.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/desktop/main.ts
|
|
2
2
|
import { appendFileSync } from "fs";
|
|
3
|
-
import { createHash as
|
|
3
|
+
import { createHash as createHash28, randomUUID as randomUUID29 } from "crypto";
|
|
4
4
|
import { readFile as readFile39, stat as stat18, writeFile as writeFile19 } from "fs/promises";
|
|
5
|
-
import { basename as basename11, dirname as dirname24, join as
|
|
5
|
+
import { basename as basename11, dirname as dirname24, join as join47, resolve as resolve40 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
7
7
|
import { app, BrowserWindow as BrowserWindow2, clipboard, dialog, ipcMain, Menu, nativeImage, Notification, shell } from "electron";
|
|
8
8
|
|
|
@@ -4218,9 +4218,9 @@ ${summarizeReport(report2)}`).join("\n\n")
|
|
|
4218
4218
|
|
|
4219
4219
|
// src/production.ts
|
|
4220
4220
|
import { readFile as readFile27 } from "fs/promises";
|
|
4221
|
-
import { createHash as
|
|
4222
|
-
import { homedir as homedir2, release as osRelease, tmpdir as
|
|
4223
|
-
import { basename as basename8, join as
|
|
4221
|
+
import { createHash as createHash21, randomUUID as randomUUID25 } from "crypto";
|
|
4222
|
+
import { homedir as homedir2, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
4223
|
+
import { basename as basename8, join as join32, relative as relative23, resolve as resolve32, sep as sep23 } from "path";
|
|
4224
4224
|
|
|
4225
4225
|
// src/agent/subagents.ts
|
|
4226
4226
|
import { z as z10 } from "zod";
|
|
@@ -6568,6 +6568,7 @@ var AgentLoop = class {
|
|
|
6568
6568
|
attempt,
|
|
6569
6569
|
messages: modelRequest.messages
|
|
6570
6570
|
});
|
|
6571
|
+
const modelStartedAt = Date.now();
|
|
6571
6572
|
yield { type: "model-start", id: modelCallId };
|
|
6572
6573
|
try {
|
|
6573
6574
|
for await (const event of adapter.stream(modelRequest)) {
|
|
@@ -6626,6 +6627,13 @@ var AgentLoop = class {
|
|
|
6626
6627
|
completed,
|
|
6627
6628
|
agent: this.#options.agent,
|
|
6628
6629
|
providerError,
|
|
6630
|
+
durationMs: Math.max(0, Date.now() - modelStartedAt),
|
|
6631
|
+
...usage === void 0 ? {} : {
|
|
6632
|
+
inputTokens: usage.inputTokens,
|
|
6633
|
+
outputTokens: usage.outputTokens,
|
|
6634
|
+
...usage.cacheReadTokens === void 0 ? {} : { cacheReadTokens: usage.cacheReadTokens },
|
|
6635
|
+
...usage.cacheCreationTokens === void 0 ? {} : { cacheCreationTokens: usage.cacheCreationTokens }
|
|
6636
|
+
},
|
|
6629
6637
|
...terminalError === void 0 ? {} : {
|
|
6630
6638
|
errorCode: terminalError.code,
|
|
6631
6639
|
errorMessage: terminalError.message
|
|
@@ -7524,11 +7532,18 @@ var DEFAULT_TOOL_OUTPUT_LIMITS = Object.freeze({
|
|
|
7524
7532
|
perTurnChars: 2e5
|
|
7525
7533
|
});
|
|
7526
7534
|
var PreToolUsePayload = z14.object({
|
|
7535
|
+
// Optional for backwards-compatible hook updatedInput payloads; runtime
|
|
7536
|
+
// emissions always populate it.
|
|
7537
|
+
toolCallId: z14.string().optional(),
|
|
7527
7538
|
tool: z14.string(),
|
|
7528
7539
|
input: z14.unknown(),
|
|
7529
7540
|
agent: z14.enum(["main", "subagent"])
|
|
7530
7541
|
});
|
|
7531
|
-
var PermissionRequestPayload = PreToolUsePayload.extend({
|
|
7542
|
+
var PermissionRequestPayload = PreToolUsePayload.extend({
|
|
7543
|
+
reason: z14.string().optional(),
|
|
7544
|
+
toolCategory: z14.string(),
|
|
7545
|
+
allowAlways: z14.boolean()
|
|
7546
|
+
});
|
|
7532
7547
|
var PostToolUsePayload = PreToolUsePayload.extend({ output: z14.unknown() });
|
|
7533
7548
|
var PostToolUseFailurePayload = PreToolUsePayload.extend({
|
|
7534
7549
|
error: z14.object({ code: z14.string(), message: z14.string() })
|
|
@@ -7645,30 +7660,31 @@ var ToolRuntime = class {
|
|
|
7645
7660
|
const tool = this.#tools.get(call.name);
|
|
7646
7661
|
if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
|
|
7647
7662
|
const signal = context.signal ?? new AbortController().signal;
|
|
7663
|
+
const toolCallId = call.id ?? randomUUID7();
|
|
7648
7664
|
const validation = this.validate(call);
|
|
7649
7665
|
if (!validation.ok) {
|
|
7650
|
-
return this.#fail(call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
7666
|
+
return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
7651
7667
|
}
|
|
7652
7668
|
let input = validation.input;
|
|
7653
7669
|
try {
|
|
7654
7670
|
const pre = await this.#hooks.emit({
|
|
7655
7671
|
version: 1,
|
|
7656
7672
|
type: "PreToolUse",
|
|
7657
|
-
payload: { tool: tool.name, input, agent: context.agent }
|
|
7673
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent }
|
|
7658
7674
|
});
|
|
7659
7675
|
if (pre.updatedInput !== void 0) {
|
|
7660
7676
|
const payload = PreToolUsePayload.parse(pre.updatedInput);
|
|
7661
7677
|
if (payload.tool !== tool.name || payload.agent !== context.agent) {
|
|
7662
|
-
return this.#fail(tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
|
|
7678
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
|
|
7663
7679
|
}
|
|
7664
7680
|
try {
|
|
7665
7681
|
input = tool.inputSchema.parse(payload.input);
|
|
7666
7682
|
} catch (error) {
|
|
7667
|
-
return this.#fail(tool.name, payload.input, context.agent, "invalid_input", message(error));
|
|
7683
|
+
return this.#fail(toolCallId, tool.name, payload.input, context.agent, "invalid_input", message(error));
|
|
7668
7684
|
}
|
|
7669
7685
|
}
|
|
7670
7686
|
if (pre.decision === "deny") {
|
|
7671
|
-
return this.#fail(tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
|
|
7687
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
|
|
7672
7688
|
}
|
|
7673
7689
|
const request = {
|
|
7674
7690
|
agent: context.agent,
|
|
@@ -7679,13 +7695,13 @@ var ToolRuntime = class {
|
|
|
7679
7695
|
let permission = this.#permissions.decide(request);
|
|
7680
7696
|
const nonCacheableApproval = request.allowAlways === false || permission.allowAlways === false;
|
|
7681
7697
|
if (permission.decision === "deny") {
|
|
7682
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
|
|
7698
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
|
|
7683
7699
|
}
|
|
7684
7700
|
if (permission.decision === "ask" && context.agent === "main" && !nonCacheableApproval && this.#permissions.mode === "auto" && this.#permissions.profile === "standard" && this.#classify !== void 0) {
|
|
7685
7701
|
try {
|
|
7686
7702
|
const classified = await this.#classify(request, signal);
|
|
7687
7703
|
if (classified.decision === "deny") {
|
|
7688
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
|
|
7704
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
|
|
7689
7705
|
}
|
|
7690
7706
|
if (classified.decision === "allow") permission = classified;
|
|
7691
7707
|
else {
|
|
@@ -7702,13 +7718,21 @@ var ToolRuntime = class {
|
|
|
7702
7718
|
const requestDecision = await this.#hooks.emit({
|
|
7703
7719
|
version: 1,
|
|
7704
7720
|
type: "PermissionRequest",
|
|
7705
|
-
payload: {
|
|
7721
|
+
payload: {
|
|
7722
|
+
toolCallId,
|
|
7723
|
+
tool: tool.name,
|
|
7724
|
+
input: tool.permissionInput?.(input) ?? input,
|
|
7725
|
+
agent: context.agent,
|
|
7726
|
+
reason,
|
|
7727
|
+
toolCategory: getToolCategory(tool.name),
|
|
7728
|
+
allowAlways
|
|
7729
|
+
}
|
|
7706
7730
|
});
|
|
7707
7731
|
if (requestDecision.decision === "deny") {
|
|
7708
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
|
|
7732
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
|
|
7709
7733
|
}
|
|
7710
7734
|
if (requestDecision.updatedInput !== void 0) {
|
|
7711
|
-
return this.#fail(tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
|
|
7735
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
|
|
7712
7736
|
}
|
|
7713
7737
|
if (this.#hooks.hasListeners("PermissionRequest") && requestDecision.decision === "allow") {
|
|
7714
7738
|
const ctx = requestDecision.additionalContext ?? "";
|
|
@@ -7717,18 +7741,18 @@ var ToolRuntime = class {
|
|
|
7717
7741
|
if (allowAlways && category !== "destructive") this.#alwaysAllowed.add(category);
|
|
7718
7742
|
}
|
|
7719
7743
|
} else if (context.agent !== "main" && this.#permissions.mode !== "bubble") {
|
|
7720
|
-
return this.#fail(tool.name, input, context.agent, "approval_required", reason);
|
|
7744
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "approval_required", reason);
|
|
7721
7745
|
} else if (signal.aborted) {
|
|
7722
7746
|
throw signal.reason;
|
|
7723
7747
|
} else {
|
|
7724
7748
|
const category = getToolCategory(tool.name);
|
|
7725
7749
|
if (allowAlways && this.#alwaysAllowed.has(category)) {
|
|
7726
7750
|
} else if (this.#approve === void 0) {
|
|
7727
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", reason);
|
|
7751
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", reason);
|
|
7728
7752
|
} else {
|
|
7729
7753
|
const decision = await this.#approve({ ...request, reason, ...allowAlways ? {} : { allowAlways: false } }, signal);
|
|
7730
7754
|
if (decision === "deny") {
|
|
7731
|
-
return this.#fail(tool.name, input, context.agent, "user_denied", reason);
|
|
7755
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "user_denied", reason);
|
|
7732
7756
|
}
|
|
7733
7757
|
if (decision === "always" && allowAlways && category !== "destructive") {
|
|
7734
7758
|
this.#alwaysAllowed.add(category);
|
|
@@ -7742,7 +7766,7 @@ var ToolRuntime = class {
|
|
|
7742
7766
|
try {
|
|
7743
7767
|
rawOutput = tool.outputSchema === void 0 ? executed : tool.outputSchema.parse(executed);
|
|
7744
7768
|
} catch (error) {
|
|
7745
|
-
return this.#fail(tool.name, input, context.agent, "invalid_output", message(error));
|
|
7769
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_output", message(error));
|
|
7746
7770
|
}
|
|
7747
7771
|
const declaredPresentation = tool.presentResult?.(rawOutput, input);
|
|
7748
7772
|
const presentation = declaredPresentation ?? getToolPresentation(rawOutput);
|
|
@@ -7753,7 +7777,7 @@ var ToolRuntime = class {
|
|
|
7753
7777
|
await this.#hooks.emit({
|
|
7754
7778
|
version: 1,
|
|
7755
7779
|
type: "PostToolUse",
|
|
7756
|
-
payload: { tool: tool.name, input, agent: context.agent, output }
|
|
7780
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
|
|
7757
7781
|
});
|
|
7758
7782
|
return {
|
|
7759
7783
|
ok: true,
|
|
@@ -7763,7 +7787,7 @@ var ToolRuntime = class {
|
|
|
7763
7787
|
...additionalContext === void 0 || additionalContext.length === 0 ? {} : { additionalContext }
|
|
7764
7788
|
};
|
|
7765
7789
|
} catch (error) {
|
|
7766
|
-
return this.#fail(tool.name, input, context.agent, "tool_error", message(error));
|
|
7790
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "tool_error", message(error));
|
|
7767
7791
|
}
|
|
7768
7792
|
}
|
|
7769
7793
|
async #limitOutput(tool, output) {
|
|
@@ -7811,13 +7835,13 @@ var ToolRuntime = class {
|
|
|
7811
7835
|
const limited = await this.#limitOutput(tool, content);
|
|
7812
7836
|
return typeof limited === "string" ? limited : serializeToolOutput(limited);
|
|
7813
7837
|
}
|
|
7814
|
-
async #fail(tool, input, agent, code, errorMessage3) {
|
|
7838
|
+
async #fail(toolCallId, tool, input, agent, code, errorMessage3) {
|
|
7815
7839
|
const result = { ok: false, error: { code, message: errorMessage3 } };
|
|
7816
7840
|
try {
|
|
7817
7841
|
await this.#hooks.emit({
|
|
7818
7842
|
version: 1,
|
|
7819
7843
|
type: "PostToolUseFailure",
|
|
7820
|
-
payload: { tool, input, agent, error: result.error }
|
|
7844
|
+
payload: { toolCallId, tool, input, agent, error: result.error }
|
|
7821
7845
|
});
|
|
7822
7846
|
} catch {
|
|
7823
7847
|
}
|
|
@@ -8438,6 +8462,7 @@ function isWithin2(root, target) {
|
|
|
8438
8462
|
|
|
8439
8463
|
// src/hooks/bus.ts
|
|
8440
8464
|
import { spawn } from "child_process";
|
|
8465
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
8441
8466
|
|
|
8442
8467
|
// src/hooks/types.ts
|
|
8443
8468
|
import { z as z16 } from "zod";
|
|
@@ -8504,6 +8529,11 @@ var DEFAULT_MAX_OUTPUT_BYTES = 1048576;
|
|
|
8504
8529
|
var HookBus = class {
|
|
8505
8530
|
#handlers = /* @__PURE__ */ new Map();
|
|
8506
8531
|
#payloadSchemas = /* @__PURE__ */ new Map();
|
|
8532
|
+
#options;
|
|
8533
|
+
#sequence = 0;
|
|
8534
|
+
constructor(options = {}) {
|
|
8535
|
+
this.#options = options;
|
|
8536
|
+
}
|
|
8507
8537
|
registerPayloadSchema(type, schema) {
|
|
8508
8538
|
const registrations = this.#payloadSchemas.get(type) ?? [];
|
|
8509
8539
|
const registration = { schema };
|
|
@@ -8540,7 +8570,9 @@ var HookBus = class {
|
|
|
8540
8570
|
async emit(rawEvent, externalSignal) {
|
|
8541
8571
|
externalSignal?.throwIfAborted();
|
|
8542
8572
|
let event = HookEventSchema.parse(rawEvent);
|
|
8543
|
-
|
|
8573
|
+
const validatedPayload = this.#validatePayload(event.type, event.payload);
|
|
8574
|
+
const envelope = this.#eventEnvelope();
|
|
8575
|
+
event = { ...event, payload: decoratePayload(validatedPayload, envelope) };
|
|
8544
8576
|
let aggregate2 = { decision: "allow" };
|
|
8545
8577
|
for (const registered of this.#handlers.get(event.type) ?? []) {
|
|
8546
8578
|
externalSignal?.throwIfAborted();
|
|
@@ -8555,8 +8587,8 @@ var HookBus = class {
|
|
|
8555
8587
|
externalSignal?.throwIfAborted();
|
|
8556
8588
|
if (decision.updatedInput !== void 0) {
|
|
8557
8589
|
const payload = this.#validatePayload(event.type, decision.updatedInput);
|
|
8558
|
-
event = HookEventSchema.parse({ ...event, payload });
|
|
8559
|
-
aggregate2 = { ...aggregate2, updatedInput:
|
|
8590
|
+
event = HookEventSchema.parse({ ...event, payload: decoratePayload(payload, envelope) });
|
|
8591
|
+
aggregate2 = { ...aggregate2, updatedInput: payload };
|
|
8560
8592
|
}
|
|
8561
8593
|
if (decision.additionalContext !== void 0) {
|
|
8562
8594
|
aggregate2 = {
|
|
@@ -8573,6 +8605,15 @@ var HookBus = class {
|
|
|
8573
8605
|
const schema = this.#payloadSchemas.get(type)?.at(-1)?.schema;
|
|
8574
8606
|
return schema ? schema.parse(payload) : zodRecord(payload);
|
|
8575
8607
|
}
|
|
8608
|
+
#eventEnvelope() {
|
|
8609
|
+
if (this.#options.eventContext === void 0) return void 0;
|
|
8610
|
+
return {
|
|
8611
|
+
...this.#options.eventContext(),
|
|
8612
|
+
eventId: (this.#options.eventId ?? randomUUID9)(),
|
|
8613
|
+
sequence: ++this.#sequence,
|
|
8614
|
+
timestamp: (this.#options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))()
|
|
8615
|
+
};
|
|
8616
|
+
}
|
|
8576
8617
|
async #invoke(registered, event, externalSignal) {
|
|
8577
8618
|
const timeoutSignal = AbortSignal.timeout(registered.timeoutMs);
|
|
8578
8619
|
const signal = externalSignal === void 0 ? timeoutSignal : AbortSignal.any([timeoutSignal, externalSignal]);
|
|
@@ -8584,6 +8625,9 @@ var HookBus = class {
|
|
|
8584
8625
|
return HookDecisionSchema.parse(await Promise.race([invocation, timeout]));
|
|
8585
8626
|
}
|
|
8586
8627
|
};
|
|
8628
|
+
function decoratePayload(payload, envelope) {
|
|
8629
|
+
return envelope === void 0 ? payload : { ...payload, ...envelope };
|
|
8630
|
+
}
|
|
8587
8631
|
function runShellHandler(descriptor, event, signal) {
|
|
8588
8632
|
return new Promise((resolve41, reject) => {
|
|
8589
8633
|
const child = spawn(descriptor.command, [...descriptor.args ?? []], {
|
|
@@ -8798,19 +8842,155 @@ function createIncidentReporter(options) {
|
|
|
8798
8842
|
};
|
|
8799
8843
|
}
|
|
8800
8844
|
|
|
8845
|
+
// src/island/control-server.ts
|
|
8846
|
+
import { createHash as createHash7, randomUUID as randomUUID10 } from "crypto";
|
|
8847
|
+
import { chmod, unlink as unlink2 } from "fs/promises";
|
|
8848
|
+
import { tmpdir } from "os";
|
|
8849
|
+
import { join as join9 } from "path";
|
|
8850
|
+
import { createServer as createServer2 } from "net";
|
|
8851
|
+
var MAX_REQUEST_BYTES = 64 * 1024;
|
|
8852
|
+
var SOCKET_TIMEOUT_MS = 1e4;
|
|
8853
|
+
function islandControlEndpoint(sessionId, platform = process.platform) {
|
|
8854
|
+
const digest = createHash7("sha256").update(`${process.pid}\0${sessionId}`).digest("hex").slice(0, 20);
|
|
8855
|
+
if (platform === "win32") return `\\\\.\\pipe\\flavor-island-control-${digest}`;
|
|
8856
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
8857
|
+
return join9(tmpdir(), `flavor-island-control-${uid}-${digest}.sock`);
|
|
8858
|
+
}
|
|
8859
|
+
async function createIslandControlServer(options) {
|
|
8860
|
+
const platform = options.platform ?? process.platform;
|
|
8861
|
+
const endpoint2 = options.endpoint ?? islandControlEndpoint(options.sessionId, platform);
|
|
8862
|
+
const token = options.token ?? randomUUID10();
|
|
8863
|
+
const capabilities = [
|
|
8864
|
+
"abort",
|
|
8865
|
+
"steer",
|
|
8866
|
+
"follow_up",
|
|
8867
|
+
...options.focus === void 0 ? [] : ["focus"]
|
|
8868
|
+
];
|
|
8869
|
+
if (platform !== "win32") await unlink2(endpoint2).catch(() => void 0);
|
|
8870
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
8871
|
+
const server = createServer2((socket) => {
|
|
8872
|
+
sockets.add(socket);
|
|
8873
|
+
socket.setTimeout(SOCKET_TIMEOUT_MS, () => socket.destroy());
|
|
8874
|
+
socket.on("close", () => sockets.delete(socket));
|
|
8875
|
+
socket.on("error", () => void 0);
|
|
8876
|
+
let buffer = Buffer.alloc(0);
|
|
8877
|
+
let handled = false;
|
|
8878
|
+
socket.on("data", (chunk) => {
|
|
8879
|
+
if (handled) return;
|
|
8880
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
8881
|
+
if (buffer.length + bytes.length > MAX_REQUEST_BYTES) {
|
|
8882
|
+
handled = true;
|
|
8883
|
+
write(socket, { ok: false, error: "request_too_large" });
|
|
8884
|
+
return;
|
|
8885
|
+
}
|
|
8886
|
+
buffer = Buffer.concat([buffer, bytes]);
|
|
8887
|
+
const newline = buffer.indexOf(10);
|
|
8888
|
+
if (newline < 0) return;
|
|
8889
|
+
handled = true;
|
|
8890
|
+
const line = buffer.subarray(0, newline).toString("utf8");
|
|
8891
|
+
void dispatch(line, socket, options, token);
|
|
8892
|
+
});
|
|
8893
|
+
});
|
|
8894
|
+
await new Promise((resolve41, reject) => {
|
|
8895
|
+
const onError = (error) => {
|
|
8896
|
+
server.removeListener("listening", onListening);
|
|
8897
|
+
reject(error);
|
|
8898
|
+
};
|
|
8899
|
+
const onListening = () => {
|
|
8900
|
+
server.removeListener("error", onError);
|
|
8901
|
+
resolve41();
|
|
8902
|
+
};
|
|
8903
|
+
server.once("error", onError);
|
|
8904
|
+
server.once("listening", onListening);
|
|
8905
|
+
server.listen(endpoint2);
|
|
8906
|
+
});
|
|
8907
|
+
if (platform !== "win32") await chmod(endpoint2, 384).catch(() => void 0);
|
|
8908
|
+
let closed = false;
|
|
8909
|
+
return {
|
|
8910
|
+
endpoint: endpoint2,
|
|
8911
|
+
token,
|
|
8912
|
+
capabilities,
|
|
8913
|
+
async close() {
|
|
8914
|
+
if (closed) return;
|
|
8915
|
+
closed = true;
|
|
8916
|
+
for (const socket of sockets) socket.destroy();
|
|
8917
|
+
await closeServer(server);
|
|
8918
|
+
if (platform !== "win32") await unlink2(endpoint2).catch(() => void 0);
|
|
8919
|
+
}
|
|
8920
|
+
};
|
|
8921
|
+
}
|
|
8922
|
+
async function dispatch(line, socket, options, token) {
|
|
8923
|
+
let request;
|
|
8924
|
+
try {
|
|
8925
|
+
const parsed = JSON.parse(line);
|
|
8926
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("invalid request");
|
|
8927
|
+
request = parsed;
|
|
8928
|
+
} catch {
|
|
8929
|
+
write(socket, { ok: false, error: "invalid_json" });
|
|
8930
|
+
return;
|
|
8931
|
+
}
|
|
8932
|
+
if (request.token !== token) {
|
|
8933
|
+
write(socket, { ok: false, error: "unauthorized" });
|
|
8934
|
+
return;
|
|
8935
|
+
}
|
|
8936
|
+
const command = request.command;
|
|
8937
|
+
try {
|
|
8938
|
+
if (command === "abort") {
|
|
8939
|
+
write(socket, { ok: true, result: options.session.interrupt() });
|
|
8940
|
+
} else if (command === "steer" || command === "follow_up") {
|
|
8941
|
+
const message2 = typeof request.message === "string" ? request.message.trim() : "";
|
|
8942
|
+
if (!message2 || message2.length > 1e5) {
|
|
8943
|
+
write(socket, { ok: false, error: "invalid_message" });
|
|
8944
|
+
return;
|
|
8945
|
+
}
|
|
8946
|
+
if (command === "steer") options.session.steer(message2);
|
|
8947
|
+
else options.session.followUp(message2);
|
|
8948
|
+
write(socket, { ok: true });
|
|
8949
|
+
} else if (command === "focus") {
|
|
8950
|
+
if (options.focus === void 0) {
|
|
8951
|
+
write(socket, { ok: false, error: "focus_unavailable" });
|
|
8952
|
+
return;
|
|
8953
|
+
}
|
|
8954
|
+
await options.focus();
|
|
8955
|
+
write(socket, { ok: true });
|
|
8956
|
+
} else {
|
|
8957
|
+
write(socket, { ok: false, error: "unknown_command" });
|
|
8958
|
+
}
|
|
8959
|
+
} catch (error) {
|
|
8960
|
+
write(socket, { ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
8961
|
+
}
|
|
8962
|
+
}
|
|
8963
|
+
function write(socket, payload) {
|
|
8964
|
+
try {
|
|
8965
|
+
socket.end(`${JSON.stringify(payload)}
|
|
8966
|
+
`);
|
|
8967
|
+
} catch {
|
|
8968
|
+
socket.destroy();
|
|
8969
|
+
}
|
|
8970
|
+
}
|
|
8971
|
+
function closeServer(server) {
|
|
8972
|
+
return new Promise((resolve41) => {
|
|
8973
|
+
try {
|
|
8974
|
+
server.close(() => resolve41());
|
|
8975
|
+
} catch {
|
|
8976
|
+
resolve41();
|
|
8977
|
+
}
|
|
8978
|
+
});
|
|
8979
|
+
}
|
|
8980
|
+
|
|
8801
8981
|
// src/evolve/service.ts
|
|
8802
8982
|
import { spawn as spawn2 } from "child_process";
|
|
8803
8983
|
import { writeFile as writeFile7 } from "fs/promises";
|
|
8804
|
-
import { join as
|
|
8984
|
+
import { join as join12 } from "path";
|
|
8805
8985
|
import { z as z19 } from "zod";
|
|
8806
8986
|
|
|
8807
8987
|
// src/evolve/loader.ts
|
|
8808
8988
|
import { cp as cp2, mkdir as mkdir7, mkdtemp, readdir as readdir3, rm as rm4, stat as stat5, writeFile as writeFile5 } from "fs/promises";
|
|
8809
|
-
import { tmpdir } from "os";
|
|
8810
|
-
import { join as
|
|
8989
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
8990
|
+
import { join as join10, resolve as resolve10 } from "path";
|
|
8811
8991
|
|
|
8812
8992
|
// src/plugins/host.ts
|
|
8813
|
-
import { createHash as
|
|
8993
|
+
import { createHash as createHash8 } from "crypto";
|
|
8814
8994
|
import { lstatSync, realpathSync as realpathSync3 } from "fs";
|
|
8815
8995
|
import { lstat as lstat3, open as open4, readFile as readFile7, readdir as readdir2, realpath as realpath6, stat as stat4, writeFile as writeFile4 } from "fs/promises";
|
|
8816
8996
|
import { isAbsolute as isAbsolute6, relative as relative7, resolve as resolve9, sep as sep7 } from "path";
|
|
@@ -9171,7 +9351,7 @@ var PluginHost = class {
|
|
|
9171
9351
|
const manifest = PluginManifestSchema.parse(rawManifest);
|
|
9172
9352
|
const entry2 = resolveContained(root, manifest.main, "Plugin entry");
|
|
9173
9353
|
const entrySnapshot = await snapshotEntry(root, entry2);
|
|
9174
|
-
const fingerprint2 =
|
|
9354
|
+
const fingerprint2 = createHash8("sha256").update(JSON.stringify(manifest)).update(await readFile7(entrySnapshot.physicalPath)).digest("hex");
|
|
9175
9355
|
return { manifest, root, source, entry: entry2, entrySnapshot, fingerprint: fingerprint2 };
|
|
9176
9356
|
} catch (error) {
|
|
9177
9357
|
this.#diagnose(diagnosticLabel, error, inputRoot);
|
|
@@ -9530,8 +9710,8 @@ function compare(left, right) {
|
|
|
9530
9710
|
}
|
|
9531
9711
|
|
|
9532
9712
|
// src/evolve/loader.ts
|
|
9533
|
-
var PLUGIN_DIR =
|
|
9534
|
-
var VERSIONS_DIR =
|
|
9713
|
+
var PLUGIN_DIR = join10(".flavor", "plugins");
|
|
9714
|
+
var VERSIONS_DIR = join10(".flavor", "plugins", ".versions");
|
|
9535
9715
|
function sanitizePluginName(tool) {
|
|
9536
9716
|
const base = String(tool ?? "fix").replace(/[^a-z0-9-]/gi, "-").toLowerCase();
|
|
9537
9717
|
return base.startsWith("fix-") ? base : `fix-${base}`;
|
|
@@ -9575,8 +9755,8 @@ export function activate(ctx) {
|
|
|
9575
9755
|
async function scaffoldFixPlugin(workspace, name) {
|
|
9576
9756
|
const dir = fixPluginDir(workspace, name);
|
|
9577
9757
|
await mkdir7(dir, { recursive: true });
|
|
9578
|
-
const manifestPath =
|
|
9579
|
-
const entryPath =
|
|
9758
|
+
const manifestPath = join10(dir, "flavor-plugin.json");
|
|
9759
|
+
const entryPath = join10(dir, "index.js");
|
|
9580
9760
|
try {
|
|
9581
9761
|
await writeFile5(manifestPath, SCAFFOLD_MANIFEST(name), { encoding: "utf8", flag: "wx" });
|
|
9582
9762
|
await writeFile5(entryPath, SCAFFOLD_ENTRY(name), { encoding: "utf8", flag: "wx" });
|
|
@@ -9596,9 +9776,9 @@ async function verifyFixPlugin(workspace, name) {
|
|
|
9596
9776
|
return { ok: false, provided: [], tools: [], commands: [], error: `Plugin "${name}" not found.` };
|
|
9597
9777
|
}
|
|
9598
9778
|
const collected = { tools: [], commands: [] };
|
|
9599
|
-
const sandbox = await mkdtemp(
|
|
9779
|
+
const sandbox = await mkdtemp(join10(tmpdir2(), "flavor-evolve-verify-"));
|
|
9600
9780
|
try {
|
|
9601
|
-
await cp2(pluginRoot,
|
|
9781
|
+
await cp2(pluginRoot, join10(sandbox, name), { recursive: true });
|
|
9602
9782
|
const host = new PluginHost({
|
|
9603
9783
|
projectPluginDirs: [sandbox],
|
|
9604
9784
|
sandbox: true,
|
|
@@ -9649,7 +9829,7 @@ async function verifyFixPlugin(workspace, name) {
|
|
|
9649
9829
|
}
|
|
9650
9830
|
async function snapshotFixPlugin(workspace, name) {
|
|
9651
9831
|
const pluginRoot = fixPluginDir(workspace, name);
|
|
9652
|
-
const snapshotRoot =
|
|
9832
|
+
const snapshotRoot = join10(versionsDirFor(workspace, name), timestamp());
|
|
9653
9833
|
await mkdir7(snapshotRoot, { recursive: true });
|
|
9654
9834
|
await cp2(pluginRoot, snapshotRoot, { recursive: true });
|
|
9655
9835
|
return snapshotRoot;
|
|
@@ -9670,7 +9850,7 @@ async function revertFixPlugin(workspace, name) {
|
|
|
9670
9850
|
}
|
|
9671
9851
|
const snapshots = entries.filter((entry2) => !entry2.startsWith(".")).sort();
|
|
9672
9852
|
if (snapshots.length === 0) throw new Error(`Plugin "${name}" has no snapshot to restore.`);
|
|
9673
|
-
const latest =
|
|
9853
|
+
const latest = join10(versionsRoot, snapshots[snapshots.length - 1]);
|
|
9674
9854
|
await rm4(pluginRoot, { recursive: true, force: true });
|
|
9675
9855
|
await mkdir7(pluginRoot, { recursive: true });
|
|
9676
9856
|
await cp2(latest, pluginRoot, { recursive: true });
|
|
@@ -9678,14 +9858,14 @@ async function revertFixPlugin(workspace, name) {
|
|
|
9678
9858
|
}
|
|
9679
9859
|
|
|
9680
9860
|
// src/evolve/store.ts
|
|
9681
|
-
import { createHash as
|
|
9861
|
+
import { createHash as createHash9 } from "crypto";
|
|
9682
9862
|
import { appendFile, mkdir as mkdir8, readFile as readFile8, rm as rm5, writeFile as writeFile6 } from "fs/promises";
|
|
9683
|
-
import { dirname as dirname8, join as
|
|
9863
|
+
import { dirname as dirname8, join as join11 } from "path";
|
|
9684
9864
|
function normalizeError(message2) {
|
|
9685
9865
|
return String(message2 ?? "").replace(/\s+/g, " ").replace(/"[^"]*"/g, '"\u2026"').replace(/`[^`]*`/g, '"\u2026"').trim().slice(0, 160);
|
|
9686
9866
|
}
|
|
9687
9867
|
function fingerprint(tool, errorCode, error) {
|
|
9688
|
-
return
|
|
9868
|
+
return createHash9("sha1").update(`${tool}::${errorCode ?? ""}::${normalizeError(error)}`).digest("hex").slice(0, 12);
|
|
9689
9869
|
}
|
|
9690
9870
|
function argKeys(args) {
|
|
9691
9871
|
if (args === null || typeof args !== "object") return [];
|
|
@@ -9753,12 +9933,12 @@ var EvolveStore = class {
|
|
|
9753
9933
|
#queue = Promise.resolve();
|
|
9754
9934
|
constructor(options) {
|
|
9755
9935
|
const { workspace, maxSignals = 400, maxRules = 20 } = options;
|
|
9756
|
-
this.dir =
|
|
9757
|
-
this.signalsFile =
|
|
9758
|
-
this.reflectionsFile =
|
|
9759
|
-
this.doneFile =
|
|
9760
|
-
this.verifiedFile =
|
|
9761
|
-
this.rulesFile =
|
|
9936
|
+
this.dir = join11(workspace, ".flavor", "evolve");
|
|
9937
|
+
this.signalsFile = join11(this.dir, "signals.jsonl");
|
|
9938
|
+
this.reflectionsFile = join11(this.dir, "reflections.jsonl");
|
|
9939
|
+
this.doneFile = join11(this.dir, "done.json");
|
|
9940
|
+
this.verifiedFile = join11(this.dir, "verified.json");
|
|
9941
|
+
this.rulesFile = join11(this.dir, "rules.json");
|
|
9762
9942
|
this.maxSignals = maxSignals;
|
|
9763
9943
|
this.maxRules = maxRules;
|
|
9764
9944
|
}
|
|
@@ -10297,7 +10477,7 @@ function createEvolveService(options) {
|
|
|
10297
10477
|
if (suggestion === void 0) return `No open suggestion with id "${suggestionId}". Use /evolve suggest to list them.`;
|
|
10298
10478
|
const name = sanitizePluginName(suggestion.tool);
|
|
10299
10479
|
const dir = await scaffoldFixPlugin(workspace, name);
|
|
10300
|
-
await writeFile7(
|
|
10480
|
+
await writeFile7(join12(dir, "PLAN.md"), buildPlan(suggestion, name, "See the repeated failure below; describe your fix in PLAN.md."), "utf8");
|
|
10301
10481
|
await snapshotFixPlugin(workspace, name);
|
|
10302
10482
|
return [
|
|
10303
10483
|
`suggestion ${suggestion.id}: ${suggestion.tool} x${suggestion.count} \u2014 ${suggestion.error}`,
|
|
@@ -10388,7 +10568,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
10388
10568
|
}
|
|
10389
10569
|
const name = sanitizePluginName(suggestion.tool);
|
|
10390
10570
|
const dir = await scaffoldFixPlugin(workspace, name);
|
|
10391
|
-
await writeFile7(
|
|
10571
|
+
await writeFile7(join12(dir, "PLAN.md"), buildPlan(suggestion, name, implementation), "utf8");
|
|
10392
10572
|
await snapshotFixPlugin(workspace, name);
|
|
10393
10573
|
return [
|
|
10394
10574
|
`Scaffolded fix plugin at ${dir} for suggestion [${suggestion.id}] (${suggestion.tool} x${suggestion.count}).`,
|
|
@@ -10413,7 +10593,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
10413
10593
|
// src/init/project.ts
|
|
10414
10594
|
import { lstat as lstat4, mkdir as mkdir9, readdir as readdir4, readFile as readFile9, realpath as realpath7, writeFile as writeFile8 } from "fs/promises";
|
|
10415
10595
|
import { copyFileSync, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
10416
|
-
import { basename as basename4, dirname as dirname9, isAbsolute as isAbsolute7, join as
|
|
10596
|
+
import { basename as basename4, dirname as dirname9, isAbsolute as isAbsolute7, join as join13, relative as relative8, sep as sep8 } from "path";
|
|
10417
10597
|
import { fileURLToPath } from "url";
|
|
10418
10598
|
var GENERATED_START = "<!-- flavor-code:start -->";
|
|
10419
10599
|
var GENERATED_END = "<!-- flavor-code:end -->";
|
|
@@ -10535,7 +10715,7 @@ async function sampleSourceExtensions(cwd, root, directories) {
|
|
|
10535
10715
|
const extensions = /* @__PURE__ */ new Set();
|
|
10536
10716
|
const sampleDirectories = ["", ...directories].slice(0, 12);
|
|
10537
10717
|
for (const relativeDirectory of sampleDirectories) {
|
|
10538
|
-
const absoluteDirectory =
|
|
10718
|
+
const absoluteDirectory = join13(cwd, relativeDirectory);
|
|
10539
10719
|
if (!isContainedPath(root, await realpath7(absoluteDirectory))) continue;
|
|
10540
10720
|
let entries;
|
|
10541
10721
|
try {
|
|
@@ -10556,15 +10736,15 @@ async function inspectProject(cwd) {
|
|
|
10556
10736
|
const root = await realpath7(cwd);
|
|
10557
10737
|
const sourceDirectories = [];
|
|
10558
10738
|
for (const directory of SOURCE_DIRECTORIES) {
|
|
10559
|
-
if (await pathKind(
|
|
10739
|
+
if (await pathKind(join13(cwd, directory), root) === "directory") sourceDirectories.push(directory);
|
|
10560
10740
|
}
|
|
10561
10741
|
const instructionFiles = [];
|
|
10562
10742
|
for (const file of INSTRUCTION_FILES) {
|
|
10563
|
-
if (await pathKind(
|
|
10743
|
+
if (await pathKind(join13(cwd, file), root) === "file") instructionFiles.push(file);
|
|
10564
10744
|
}
|
|
10565
10745
|
const configFiles = [];
|
|
10566
10746
|
for (const file of CONFIG_FILES) {
|
|
10567
|
-
if (await pathKind(
|
|
10747
|
+
if (await pathKind(join13(cwd, file), root) === "file") configFiles.push(file);
|
|
10568
10748
|
}
|
|
10569
10749
|
const manifestNames = [
|
|
10570
10750
|
"package.json",
|
|
@@ -10577,7 +10757,7 @@ async function inspectProject(cwd) {
|
|
|
10577
10757
|
];
|
|
10578
10758
|
const present = /* @__PURE__ */ new Set();
|
|
10579
10759
|
for (const manifest of manifestNames) {
|
|
10580
|
-
if (await pathKind(
|
|
10760
|
+
if (await pathKind(join13(cwd, manifest), root) === "file") present.add(manifest);
|
|
10581
10761
|
}
|
|
10582
10762
|
const detectedLockfiles = JAVASCRIPT_LOCKFILES.filter(({ name }) => present.has(name));
|
|
10583
10763
|
const detectedJsManagers = [
|
|
@@ -10603,7 +10783,7 @@ async function inspectProject(cwd) {
|
|
|
10603
10783
|
let projectName = sanitizeProjectName(basename4(cwd)) ?? "project";
|
|
10604
10784
|
let declaredJsManager;
|
|
10605
10785
|
if (present.has("package.json")) {
|
|
10606
|
-
const text = await readTextIfSmall(
|
|
10786
|
+
const text = await readTextIfSmall(join13(cwd, "package.json"), root);
|
|
10607
10787
|
if (text === void 0) {
|
|
10608
10788
|
cautions.push("package.json was too large to inspect; its scripts were ignored.");
|
|
10609
10789
|
} else {
|
|
@@ -10657,8 +10837,8 @@ async function inspectProject(cwd) {
|
|
|
10657
10837
|
addScriptCommand(testCommands, scripts, jsManager, ["test"]);
|
|
10658
10838
|
addScriptCommand(lintCommands, scripts, jsManager, ["lint"]);
|
|
10659
10839
|
addScriptCommand(formatCommands, scripts, jsManager, ["format", "format:check"]);
|
|
10660
|
-
const pyproject = present.has("pyproject.toml") ? await readTextIfSmall(
|
|
10661
|
-
const requirements = present.has("requirements.txt") ? await readTextIfSmall(
|
|
10840
|
+
const pyproject = present.has("pyproject.toml") ? await readTextIfSmall(join13(cwd, "pyproject.toml"), root) : void 0;
|
|
10841
|
+
const requirements = present.has("requirements.txt") ? await readTextIfSmall(join13(cwd, "requirements.txt"), root) : void 0;
|
|
10662
10842
|
const pythonTools = detectPythonTools(pyproject, requirements, configFiles);
|
|
10663
10843
|
const pythonManager = pythonManagers[0];
|
|
10664
10844
|
const pythonPrefix = pythonManager === "uv" ? "uv run " : pythonManager === "poetry" ? "poetry run " : pythonManager === "pipenv" ? "pipenv run " : "";
|
|
@@ -10676,8 +10856,8 @@ async function inspectProject(cwd) {
|
|
|
10676
10856
|
if (present.has("pyproject.toml") || present.has("requirements.txt") || extensions.has("py")) {
|
|
10677
10857
|
languages.add("Python");
|
|
10678
10858
|
}
|
|
10679
|
-
const gitignorePath = await pathKind(
|
|
10680
|
-
const hasCodeGraph = await pathKind(
|
|
10859
|
+
const gitignorePath = await pathKind(join13(cwd, ".gitignore"), root) === "file" ? ".gitignore" : void 0;
|
|
10860
|
+
const hasCodeGraph = await pathKind(join13(cwd, ".flavor", "astgraph", "index.db"), root) === "file";
|
|
10681
10861
|
return {
|
|
10682
10862
|
cwd,
|
|
10683
10863
|
projectName,
|
|
@@ -10786,7 +10966,7 @@ function isEntryIgnored(lines, entry2) {
|
|
|
10786
10966
|
});
|
|
10787
10967
|
}
|
|
10788
10968
|
async function addSessionsToGitignore(cwd) {
|
|
10789
|
-
const path =
|
|
10969
|
+
const path = join13(cwd, ".gitignore");
|
|
10790
10970
|
const root = await realpath7(cwd);
|
|
10791
10971
|
await assertSafeManagedPath(path, root);
|
|
10792
10972
|
let existing;
|
|
@@ -10820,9 +11000,9 @@ async function assertSafeManagedPath(path, root) {
|
|
|
10820
11000
|
}
|
|
10821
11001
|
async function ensureFlavorDirectories(cwd) {
|
|
10822
11002
|
const root = await realpath7(cwd);
|
|
10823
|
-
const sessionsDir =
|
|
10824
|
-
const skillsDir =
|
|
10825
|
-
const pluginsDir =
|
|
11003
|
+
const sessionsDir = join13(cwd, ".flavor", "sessions");
|
|
11004
|
+
const skillsDir = join13(cwd, ".flavor", "skills");
|
|
11005
|
+
const pluginsDir = join13(cwd, ".flavor", "plugins");
|
|
10826
11006
|
await mkdir9(sessionsDir, { recursive: true, mode: 448 });
|
|
10827
11007
|
await mkdir9(skillsDir, { recursive: true, mode: 448 });
|
|
10828
11008
|
await mkdir9(pluginsDir, { recursive: true, mode: 448 });
|
|
@@ -10835,7 +11015,7 @@ async function ensureFlavorDirectories(cwd) {
|
|
|
10835
11015
|
}
|
|
10836
11016
|
}
|
|
10837
11017
|
async function createExampleFlavorConfig(cwd) {
|
|
10838
|
-
const configPath =
|
|
11018
|
+
const configPath = join13(cwd, ".flavor", "flavor.json");
|
|
10839
11019
|
const root = await realpath7(cwd);
|
|
10840
11020
|
await assertSafeManagedPath(configPath, root);
|
|
10841
11021
|
try {
|
|
@@ -10865,9 +11045,9 @@ async function createExampleFlavorConfig(cwd) {
|
|
|
10865
11045
|
};
|
|
10866
11046
|
await writeFile8(configPath, JSON.stringify(example, null, 2) + "\n");
|
|
10867
11047
|
}
|
|
10868
|
-
var ASTGRAPH_SRC_DIR =
|
|
11048
|
+
var ASTGRAPH_SRC_DIR = join13(dirname9(fileURLToPath(import.meta.url)), "astgraph");
|
|
10869
11049
|
async function copyAstgraphPlugin(cwd) {
|
|
10870
|
-
const pluginDir =
|
|
11050
|
+
const pluginDir = join13(cwd, ".flavor", "plugins", "astgraph");
|
|
10871
11051
|
try {
|
|
10872
11052
|
await lstat4(pluginDir);
|
|
10873
11053
|
return;
|
|
@@ -10879,8 +11059,8 @@ async function copyAstgraphPlugin(cwd) {
|
|
|
10879
11059
|
function copyDirectory(from, to) {
|
|
10880
11060
|
mkdirSync2(to, { recursive: true, mode: 448 });
|
|
10881
11061
|
for (const entry2 of readdirSync(from, { withFileTypes: true })) {
|
|
10882
|
-
const source =
|
|
10883
|
-
const target =
|
|
11062
|
+
const source = join13(from, entry2.name);
|
|
11063
|
+
const target = join13(to, entry2.name);
|
|
10884
11064
|
if (entry2.isDirectory()) copyDirectory(source, target);
|
|
10885
11065
|
else if (entry2.isFile()) copyFileSync(source, target);
|
|
10886
11066
|
}
|
|
@@ -10890,7 +11070,7 @@ async function initializeFlavor(cwd) {
|
|
|
10890
11070
|
await copyAstgraphPlugin(cwd);
|
|
10891
11071
|
await createExampleFlavorConfig(cwd);
|
|
10892
11072
|
const facts = await inspectProject(cwd);
|
|
10893
|
-
const path =
|
|
11073
|
+
const path = join13(cwd, "FLAVOR.md");
|
|
10894
11074
|
const root = await realpath7(cwd);
|
|
10895
11075
|
await assertSafeManagedPath(path, root);
|
|
10896
11076
|
let existing;
|
|
@@ -11306,9 +11486,9 @@ ${evidence.summary}`;
|
|
|
11306
11486
|
};
|
|
11307
11487
|
|
|
11308
11488
|
// src/goal/orchestrator.ts
|
|
11309
|
-
import { createHash as
|
|
11489
|
+
import { createHash as createHash11 } from "crypto";
|
|
11310
11490
|
import { writeFile as writeFile9, mkdir as mkdir10 } from "fs/promises";
|
|
11311
|
-
import { join as
|
|
11491
|
+
import { join as join15 } from "path";
|
|
11312
11492
|
|
|
11313
11493
|
// src/goal/types.ts
|
|
11314
11494
|
import { z as z21 } from "zod";
|
|
@@ -11471,9 +11651,9 @@ function extractJson(text) {
|
|
|
11471
11651
|
}
|
|
11472
11652
|
|
|
11473
11653
|
// src/goal/classifier.ts
|
|
11474
|
-
import { createHash as
|
|
11654
|
+
import { createHash as createHash10 } from "crypto";
|
|
11475
11655
|
import { readFile as readFile10 } from "fs/promises";
|
|
11476
|
-
import { join as
|
|
11656
|
+
import { join as join14 } from "path";
|
|
11477
11657
|
function buildSkepticPrompt(evidence, plan, index) {
|
|
11478
11658
|
const criteriaList = plan.criteria.map((c) => ` ${c.id}. [${c.type}] ${c.description}`).join("\n");
|
|
11479
11659
|
return [
|
|
@@ -11646,11 +11826,11 @@ function deduplicateGaps(gaps) {
|
|
|
11646
11826
|
}
|
|
11647
11827
|
function computeFingerprint(gaps) {
|
|
11648
11828
|
const content = gaps.map((g) => `${g.criterion}:${g.description}`).sort().join("\n");
|
|
11649
|
-
return
|
|
11829
|
+
return createHash10("sha256").update(content).digest("hex").slice(0, 16);
|
|
11650
11830
|
}
|
|
11651
|
-
async function collectEvidence(workspace, objective, finalResponse, priorGaps, contractHash2 =
|
|
11831
|
+
async function collectEvidence(workspace, objective, finalResponse, priorGaps, contractHash2 = createHash10("sha256").update(objective).digest("hex"), hostVerification) {
|
|
11652
11832
|
const workspaceState = await collectWorkspaceState(workspace);
|
|
11653
|
-
const planPath2 =
|
|
11833
|
+
const planPath2 = join14(workspace, ".flavor", "goal-plan.md");
|
|
11654
11834
|
let planFile = null;
|
|
11655
11835
|
try {
|
|
11656
11836
|
planFile = await readFile10(planPath2, "utf8");
|
|
@@ -11677,7 +11857,7 @@ async function collectWorkspaceState(workspace) {
|
|
|
11677
11857
|
return {
|
|
11678
11858
|
changedFiles: [],
|
|
11679
11859
|
status: ["not-a-git-worktree"],
|
|
11680
|
-
diffHash:
|
|
11860
|
+
diffHash: createHash10("sha256").update("not-a-git-worktree").digest("hex")
|
|
11681
11861
|
};
|
|
11682
11862
|
}
|
|
11683
11863
|
const status = statusResult.stdout.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean);
|
|
@@ -11694,7 +11874,7 @@ async function collectWorkspaceState(workspace) {
|
|
|
11694
11874
|
return {
|
|
11695
11875
|
changedFiles,
|
|
11696
11876
|
status,
|
|
11697
|
-
diffHash:
|
|
11877
|
+
diffHash: createHash10("sha256").update(diff.stdout).update("\nUNTRACKED\n").update(untracked).digest("hex")
|
|
11698
11878
|
};
|
|
11699
11879
|
}
|
|
11700
11880
|
|
|
@@ -11826,7 +12006,7 @@ var GoalOrchestrator = class {
|
|
|
11826
12006
|
type: "not_achieved",
|
|
11827
12007
|
gaps,
|
|
11828
12008
|
summary: hostVerification.summary,
|
|
11829
|
-
fingerprint:
|
|
12009
|
+
fingerprint: createHash11("sha256").update(hostVerification.summary).digest("hex").slice(0, 16)
|
|
11830
12010
|
};
|
|
11831
12011
|
}
|
|
11832
12012
|
} else try {
|
|
@@ -11900,11 +12080,11 @@ var GoalOrchestrator = class {
|
|
|
11900
12080
|
}
|
|
11901
12081
|
};
|
|
11902
12082
|
function contractHash(objective, plan) {
|
|
11903
|
-
return
|
|
12083
|
+
return createHash11("sha256").update(JSON.stringify({ objective, plan })).digest("hex");
|
|
11904
12084
|
}
|
|
11905
12085
|
async function writePlanFile(workspace, plan) {
|
|
11906
|
-
const dir =
|
|
11907
|
-
const planPath2 =
|
|
12086
|
+
const dir = join15(workspace, ".flavor");
|
|
12087
|
+
const planPath2 = join15(dir, "goal-plan.md");
|
|
11908
12088
|
await mkdir10(dir, { recursive: true });
|
|
11909
12089
|
const criteriaText = plan.criteria.map((c) => `- [${c.type}] AC-${c.id}: ${c.description}`).join("\n");
|
|
11910
12090
|
const content = [
|
|
@@ -11980,9 +12160,9 @@ function formatPriorGaps(gaps) {
|
|
|
11980
12160
|
|
|
11981
12161
|
// src/goal/store.ts
|
|
11982
12162
|
import { constants as constants3 } from "fs";
|
|
11983
|
-
import { randomUUID as
|
|
12163
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
11984
12164
|
import { lstat as lstat5, mkdir as mkdir11, open as open5, rename as rename4, rm as rm6 } from "fs/promises";
|
|
11985
|
-
import { dirname as dirname10, join as
|
|
12165
|
+
import { dirname as dirname10, join as join16, relative as relative9, resolve as resolve11, sep as sep9 } from "path";
|
|
11986
12166
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
11987
12167
|
var GoalStore = class {
|
|
11988
12168
|
#workspace;
|
|
@@ -11990,7 +12170,7 @@ var GoalStore = class {
|
|
|
11990
12170
|
#maxBytes;
|
|
11991
12171
|
constructor(options) {
|
|
11992
12172
|
this.#workspace = resolve11(options.workspace);
|
|
11993
|
-
this.#root =
|
|
12173
|
+
this.#root = join16(this.#workspace, ".flavor", "goals");
|
|
11994
12174
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
11995
12175
|
}
|
|
11996
12176
|
async save(input) {
|
|
@@ -11998,13 +12178,13 @@ var GoalStore = class {
|
|
|
11998
12178
|
await assertNoSymlink(this.#workspace, dirname10(this.#root));
|
|
11999
12179
|
await mkdir11(this.#root, { recursive: true, mode: 448 });
|
|
12000
12180
|
await assertNoSymlink(this.#workspace, this.#root);
|
|
12001
|
-
const target =
|
|
12181
|
+
const target = join16(this.#root, `${state.id}.json`);
|
|
12002
12182
|
const body = `${JSON.stringify(state, null, 2)}
|
|
12003
12183
|
`;
|
|
12004
12184
|
if (Buffer.byteLength(body) > this.#maxBytes) {
|
|
12005
12185
|
throw new Error(`Goal state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
12006
12186
|
}
|
|
12007
|
-
const temporary =
|
|
12187
|
+
const temporary = join16(this.#root, `.${state.id}.${process.pid}.${randomUUID11()}.tmp`);
|
|
12008
12188
|
let handle;
|
|
12009
12189
|
try {
|
|
12010
12190
|
handle = await open5(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
|
|
@@ -12045,7 +12225,7 @@ function isCode4(error, code) {
|
|
|
12045
12225
|
|
|
12046
12226
|
// src/loop/isolation.ts
|
|
12047
12227
|
import { appendFile as appendFile2, mkdir as mkdir12, readFile as readFile11 } from "fs/promises";
|
|
12048
|
-
import { join as
|
|
12228
|
+
import { join as join17, resolve as resolve12 } from "path";
|
|
12049
12229
|
var CODE_CHANGE = /\b(?:fix|implement|add|update|change|modify|refactor|build|create|remove|delete|write|migrate)\b|修复|实现|添加|更新|修改|重构|创建|删除|迁移/i;
|
|
12050
12230
|
var READ_ONLY = /\b(?:review|analy[sz]e|inspect|explain|research|investigate|audit|summari[sz]e|report)\b|调研|分析|审查|检查|解释|研究|总结|报告/i;
|
|
12051
12231
|
function inferGoalIntent(goal) {
|
|
@@ -12068,8 +12248,8 @@ async function prepareLoopWorkspace(options) {
|
|
|
12068
12248
|
return { kind: "needs_human", reason: "The workspace has uncommitted changes that a new worktree would omit." };
|
|
12069
12249
|
}
|
|
12070
12250
|
options.signal?.throwIfAborted();
|
|
12071
|
-
const worktrees =
|
|
12072
|
-
const path =
|
|
12251
|
+
const worktrees = join17(root, ".worktrees");
|
|
12252
|
+
const path = join17(worktrees, `loop-${options.loopId}`);
|
|
12073
12253
|
const branch = `loop/${options.loopId}`;
|
|
12074
12254
|
try {
|
|
12075
12255
|
await ensureWorktreeDirectoryIgnored(root);
|
|
@@ -12099,8 +12279,8 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
12099
12279
|
const commonDirectory = await git(root, ["rev-parse", "--git-common-dir"]);
|
|
12100
12280
|
if (commonDirectory.code !== 0) throw new Error(commonDirectory.stderr.trim() || "Git common directory is unavailable");
|
|
12101
12281
|
const gitDirectory = resolve12(root, commonDirectory.stdout.trim());
|
|
12102
|
-
const infoDirectory =
|
|
12103
|
-
const excludePath =
|
|
12282
|
+
const infoDirectory = join17(gitDirectory, "info");
|
|
12283
|
+
const excludePath = join17(infoDirectory, "exclude");
|
|
12104
12284
|
await mkdir12(infoDirectory, { recursive: true });
|
|
12105
12285
|
let existing = "";
|
|
12106
12286
|
try {
|
|
@@ -12116,8 +12296,8 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
12116
12296
|
// src/loop/store.ts
|
|
12117
12297
|
import { constants as constants4 } from "fs";
|
|
12118
12298
|
import { lstat as lstat6, mkdir as mkdir13, open as open6, readFile as readFile12, realpath as realpath8, rename as rename5, rm as rm7, stat as stat6 } from "fs/promises";
|
|
12119
|
-
import { dirname as dirname11, join as
|
|
12120
|
-
import { randomUUID as
|
|
12299
|
+
import { dirname as dirname11, join as join18, relative as relative10, resolve as resolve13, sep as sep10 } from "path";
|
|
12300
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
12121
12301
|
var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
12122
12302
|
var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
|
|
12123
12303
|
var LoopStore = class {
|
|
@@ -12127,7 +12307,7 @@ var LoopStore = class {
|
|
|
12127
12307
|
#appendTails = /* @__PURE__ */ new Map();
|
|
12128
12308
|
constructor(options) {
|
|
12129
12309
|
this.#workspace = resolve13(options.workspace);
|
|
12130
|
-
this.#root =
|
|
12310
|
+
this.#root = join18(this.#workspace, ".flavor", "loops");
|
|
12131
12311
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES2;
|
|
12132
12312
|
}
|
|
12133
12313
|
async create(state) {
|
|
@@ -12144,11 +12324,11 @@ var LoopStore = class {
|
|
|
12144
12324
|
const state = LoopStateSchema.parse(input);
|
|
12145
12325
|
await this.#assertWorkspace(state.workspace);
|
|
12146
12326
|
const directory = await this.#prepare(state.loopId);
|
|
12147
|
-
const target =
|
|
12327
|
+
const target = join18(directory, "state.json");
|
|
12148
12328
|
const body = `${JSON.stringify(state, null, 2)}
|
|
12149
12329
|
`;
|
|
12150
12330
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Loop state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
12151
|
-
const temporary =
|
|
12331
|
+
const temporary = join18(directory, `.state.${process.pid}.${randomUUID12()}.tmp`);
|
|
12152
12332
|
let handle;
|
|
12153
12333
|
try {
|
|
12154
12334
|
handle = await open6(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY, 384);
|
|
@@ -12164,7 +12344,7 @@ var LoopStore = class {
|
|
|
12164
12344
|
}
|
|
12165
12345
|
async load(loopId) {
|
|
12166
12346
|
const directory = await this.#safeExisting(loopId);
|
|
12167
|
-
const target =
|
|
12347
|
+
const target = join18(directory, "state.json");
|
|
12168
12348
|
try {
|
|
12169
12349
|
const metadata = await stat6(target);
|
|
12170
12350
|
if (!metadata.isFile() || metadata.size > this.#maxBytes) throw new Error("Loop state is not a bounded regular file");
|
|
@@ -12174,7 +12354,7 @@ var LoopStore = class {
|
|
|
12174
12354
|
return state;
|
|
12175
12355
|
} catch (error) {
|
|
12176
12356
|
if (isCode5(error, "ENOENT")) throw new Error(`Loop "${loopId}" was not found`);
|
|
12177
|
-
const quarantine = `${target}.corrupt-${Date.now()}-${
|
|
12357
|
+
const quarantine = `${target}.corrupt-${Date.now()}-${randomUUID12().slice(0, 8)}`;
|
|
12178
12358
|
await rename5(target, quarantine).catch(() => void 0);
|
|
12179
12359
|
throw new Error(`Loop "${loopId}" is corrupt and was quarantined: ${message(error)}`);
|
|
12180
12360
|
}
|
|
@@ -12184,7 +12364,7 @@ var LoopStore = class {
|
|
|
12184
12364
|
const previous = this.#appendTails.get(event.loopId) ?? Promise.resolve();
|
|
12185
12365
|
const next = previous.catch(() => void 0).then(async () => {
|
|
12186
12366
|
const directory = await this.#prepare(event.loopId);
|
|
12187
|
-
const path =
|
|
12367
|
+
const path = join18(directory, "events.jsonl");
|
|
12188
12368
|
const handle = await open6(path, constants4.O_CREAT | constants4.O_APPEND | constants4.O_WRONLY, 384);
|
|
12189
12369
|
try {
|
|
12190
12370
|
await handle.writeFile(`${JSON.stringify(event)}
|
|
@@ -12201,7 +12381,7 @@ var LoopStore = class {
|
|
|
12201
12381
|
await assertNoSymlink2(this.#workspace, dirname11(this.#root));
|
|
12202
12382
|
await mkdir13(this.#root, { recursive: true, mode: 448 });
|
|
12203
12383
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
12204
|
-
const directory =
|
|
12384
|
+
const directory = join18(this.#root, loopId);
|
|
12205
12385
|
await mkdir13(directory, { recursive: true, mode: 448 });
|
|
12206
12386
|
await assertNoSymlink2(this.#workspace, directory);
|
|
12207
12387
|
return directory;
|
|
@@ -12209,7 +12389,7 @@ var LoopStore = class {
|
|
|
12209
12389
|
async #safeExisting(loopId) {
|
|
12210
12390
|
validateId(loopId);
|
|
12211
12391
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
12212
|
-
const directory =
|
|
12392
|
+
const directory = join18(this.#root, loopId);
|
|
12213
12393
|
await assertNoSymlink2(this.#workspace, directory);
|
|
12214
12394
|
return directory;
|
|
12215
12395
|
}
|
|
@@ -12258,7 +12438,7 @@ function isCode5(error, code) {
|
|
|
12258
12438
|
|
|
12259
12439
|
// src/loop/verifier.ts
|
|
12260
12440
|
import { readFile as readFile13 } from "fs/promises";
|
|
12261
|
-
import { join as
|
|
12441
|
+
import { join as join19 } from "path";
|
|
12262
12442
|
|
|
12263
12443
|
// src/tools/shell.ts
|
|
12264
12444
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
@@ -12816,13 +12996,13 @@ var SCRIPT_PRIORITY = ["test", "typecheck", "lint", "build", "smoke:install"];
|
|
|
12816
12996
|
async function inferVerificationPlan(workspace) {
|
|
12817
12997
|
const commands = [];
|
|
12818
12998
|
const seen = /* @__PURE__ */ new Set();
|
|
12819
|
-
const packageJson = await optionalJson(
|
|
12999
|
+
const packageJson = await optionalJson(join19(workspace, "package.json"));
|
|
12820
13000
|
const scripts = isRecord3(packageJson?.scripts) ? packageJson.scripts : {};
|
|
12821
13001
|
for (const name of SCRIPT_PRIORITY) {
|
|
12822
13002
|
if (typeof scripts[name] !== "string" || isUnconditionalPassThrough(scripts[name])) continue;
|
|
12823
13003
|
add(commands, seen, npmScript(name));
|
|
12824
13004
|
}
|
|
12825
|
-
const flavor = await optionalText(
|
|
13005
|
+
const flavor = await optionalText(join19(workspace, "FLAVOR.md"));
|
|
12826
13006
|
if (flavor !== void 0) {
|
|
12827
13007
|
for (const match of flavor.matchAll(/^- `([^`]+)`\s*$/gm)) {
|
|
12828
13008
|
const parsed = parseTrustedCommand(match[1] ?? "");
|
|
@@ -12959,11 +13139,11 @@ function isEnvTruthy(value) {
|
|
|
12959
13139
|
|
|
12960
13140
|
// src/utils/log.ts
|
|
12961
13141
|
import { appendFile as appendFile3, mkdir as mkdir14 } from "fs/promises";
|
|
12962
|
-
import { dirname as dirname12, join as
|
|
13142
|
+
import { dirname as dirname12, join as join20, resolve as resolve15 } from "path";
|
|
12963
13143
|
var pending = Promise.resolve();
|
|
12964
13144
|
var usagePending = Promise.resolve();
|
|
12965
13145
|
function usageLogPath() {
|
|
12966
|
-
return process.env.FLAVOR_USAGE_FILE ??
|
|
13146
|
+
return process.env.FLAVOR_USAGE_FILE ?? join20(process.cwd(), ".flavor", "usage.jsonl");
|
|
12967
13147
|
}
|
|
12968
13148
|
var usageSessionId = "unknown";
|
|
12969
13149
|
var truncateOnNextAppend = false;
|
|
@@ -12992,7 +13172,7 @@ var AuditLogger = class {
|
|
|
12992
13172
|
#path;
|
|
12993
13173
|
#closed = false;
|
|
12994
13174
|
constructor(workspace) {
|
|
12995
|
-
this.#path =
|
|
13175
|
+
this.#path = join20(resolve15(workspace), ".flavor", "audit.jsonl");
|
|
12996
13176
|
}
|
|
12997
13177
|
get path() {
|
|
12998
13178
|
return this.#path;
|
|
@@ -13590,7 +13770,7 @@ var ModelRegistry = class {
|
|
|
13590
13770
|
};
|
|
13591
13771
|
|
|
13592
13772
|
// src/mcp/client.ts
|
|
13593
|
-
import { createHash as
|
|
13773
|
+
import { createHash as createHash12 } from "crypto";
|
|
13594
13774
|
import { z as z24 } from "zod";
|
|
13595
13775
|
var McpManager = class {
|
|
13596
13776
|
#workspace;
|
|
@@ -13757,7 +13937,7 @@ function mcpToolName(serverName, remoteName) {
|
|
|
13757
13937
|
const safe = remoteName.replace(/[^A-Za-z0-9_-]/g, "_") || "tool";
|
|
13758
13938
|
const direct = `${prefix}${safe}`;
|
|
13759
13939
|
if (direct.length <= 64 && safe === remoteName) return direct;
|
|
13760
|
-
const suffix = `__${
|
|
13940
|
+
const suffix = `__${createHash12("sha256").update(remoteName).digest("hex").slice(0, 8)}`;
|
|
13761
13941
|
return `${prefix}${safe.slice(0, Math.max(1, 64 - prefix.length - suffix.length))}${suffix}`;
|
|
13762
13942
|
}
|
|
13763
13943
|
function mcpErrorText(content) {
|
|
@@ -13788,7 +13968,7 @@ import {
|
|
|
13788
13968
|
|
|
13789
13969
|
// src/utils/version.ts
|
|
13790
13970
|
import { readFileSync as readFileSync2 } from "fs";
|
|
13791
|
-
import { dirname as dirname13, join as
|
|
13971
|
+
import { dirname as dirname13, join as join21 } from "path";
|
|
13792
13972
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13793
13973
|
var cached;
|
|
13794
13974
|
function packageVersion() {
|
|
@@ -13796,7 +13976,7 @@ function packageVersion() {
|
|
|
13796
13976
|
let directory = dirname13(fileURLToPath2(import.meta.url));
|
|
13797
13977
|
for (; ; ) {
|
|
13798
13978
|
try {
|
|
13799
|
-
const manifest = JSON.parse(readFileSync2(
|
|
13979
|
+
const manifest = JSON.parse(readFileSync2(join21(directory, "package.json"), "utf8"));
|
|
13800
13980
|
if (typeof manifest.version === "string" && manifest.version.length > 0) {
|
|
13801
13981
|
cached = manifest.version;
|
|
13802
13982
|
return cached;
|
|
@@ -13849,14 +14029,14 @@ async function connectSdkMcpClient(input, dependencies2 = DEFAULT_DEPENDENCIES)
|
|
|
13849
14029
|
}
|
|
13850
14030
|
|
|
13851
14031
|
// src/auth/oauth.ts
|
|
13852
|
-
import { randomBytes as randomBytes2, createHash as
|
|
13853
|
-
import { createServer as
|
|
14032
|
+
import { randomBytes as randomBytes2, createHash as createHash14 } from "crypto";
|
|
14033
|
+
import { createServer as createServer3 } from "http";
|
|
13854
14034
|
import { URL as URL2, URLSearchParams } from "url";
|
|
13855
14035
|
|
|
13856
14036
|
// src/auth/oauth-config.ts
|
|
13857
|
-
import { createHash as
|
|
14037
|
+
import { createHash as createHash13 } from "crypto";
|
|
13858
14038
|
function oauthCredentialId(tokenUrl, clientId) {
|
|
13859
|
-
const digest =
|
|
14039
|
+
const digest = createHash13("sha256").update(`${tokenUrl}\0${clientId}`, "utf8").digest("base64url");
|
|
13860
14040
|
return `oauth:${digest}`;
|
|
13861
14041
|
}
|
|
13862
14042
|
function parseOAuthLlmConfig(value) {
|
|
@@ -14033,7 +14213,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14033
14213
|
serverResolve = resolve41;
|
|
14034
14214
|
serverReject = reject;
|
|
14035
14215
|
});
|
|
14036
|
-
const server =
|
|
14216
|
+
const server = createServer3((req, res) => {
|
|
14037
14217
|
const url = new URL2(req.url ?? "/", `http://${this.#callbackHost}:${server.address().port}`);
|
|
14038
14218
|
if (url.searchParams.has("error")) {
|
|
14039
14219
|
const error = url.searchParams.get("error") ?? "access_denied";
|
|
@@ -14113,7 +14293,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14113
14293
|
return randomBytes2(32).toString("base64url");
|
|
14114
14294
|
}
|
|
14115
14295
|
#computeCodeChallenge(verifier) {
|
|
14116
|
-
return
|
|
14296
|
+
return createHash14("sha256").update(verifier).digest("base64url");
|
|
14117
14297
|
}
|
|
14118
14298
|
#generateState() {
|
|
14119
14299
|
return randomBytes2(32).toString("base64url");
|
|
@@ -14224,7 +14404,7 @@ function isStoredToken(value) {
|
|
|
14224
14404
|
|
|
14225
14405
|
// src/permissions/policy.ts
|
|
14226
14406
|
import { readFile as readFile16 } from "fs/promises";
|
|
14227
|
-
import { join as
|
|
14407
|
+
import { join as join22, resolve as resolve17 } from "path";
|
|
14228
14408
|
import { z as z25 } from "zod";
|
|
14229
14409
|
var DecisionSchema2 = z25.enum(["allow", "ask", "deny"]);
|
|
14230
14410
|
var PrefixSchema = z25.array(z25.string().min(1).max(1024)).min(1).max(64);
|
|
@@ -14261,9 +14441,9 @@ var CompiledPermissionPolicy = class {
|
|
|
14261
14441
|
async function loadPermissionPolicy(options) {
|
|
14262
14442
|
const sources = [
|
|
14263
14443
|
...options.managedPath === void 0 ? [] : [{ tier: "managed", path: resolve17(options.managedPath) }],
|
|
14264
|
-
{ tier: "user", path:
|
|
14265
|
-
{ tier: "project", path:
|
|
14266
|
-
{ tier: "local", path:
|
|
14444
|
+
{ tier: "user", path: join22(resolve17(options.home), ".flavor-code", "permissions.json") },
|
|
14445
|
+
{ tier: "project", path: join22(resolve17(options.workspace), ".flavor", "permissions.json") },
|
|
14446
|
+
{ tier: "local", path: join22(resolve17(options.workspace), ".flavor", "permissions.local.json") }
|
|
14267
14447
|
];
|
|
14268
14448
|
const rules = [];
|
|
14269
14449
|
for (const source of sources) {
|
|
@@ -15032,8 +15212,8 @@ function createSkillResourceTool(registry) {
|
|
|
15032
15212
|
// src/session/store.ts
|
|
15033
15213
|
import { constants as constants6 } from "fs";
|
|
15034
15214
|
import { lstat as lstat8, mkdir as mkdir15, open as open7, readFile as readFile17, readdir as readdir6, realpath as realpath10, rename as rename6, rm as rm8, stat as stat7 } from "fs/promises";
|
|
15035
|
-
import { basename as basename5, dirname as dirname15, join as
|
|
15036
|
-
import { randomUUID as
|
|
15215
|
+
import { basename as basename5, dirname as dirname15, join as join23, relative as relative13, resolve as resolve19, sep as sep13 } from "path";
|
|
15216
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
15037
15217
|
import { z as z27 } from "zod";
|
|
15038
15218
|
|
|
15039
15219
|
// src/ui/transcript.ts
|
|
@@ -15689,7 +15869,7 @@ var SessionStore = class {
|
|
|
15689
15869
|
#maxSessions;
|
|
15690
15870
|
constructor(options) {
|
|
15691
15871
|
this.#workspace = resolve19(options.workspace);
|
|
15692
|
-
this.#sessions =
|
|
15872
|
+
this.#sessions = join23(this.#workspace, ".flavor", "sessions");
|
|
15693
15873
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_SESSION_BYTES;
|
|
15694
15874
|
if (!Number.isSafeInteger(this.#maxBytes) || this.#maxBytes < 256) throw new Error("maxBytes must be an integer of at least 256");
|
|
15695
15875
|
this.#maxSessions = options.maxSessions ?? 50;
|
|
@@ -15721,7 +15901,7 @@ var SessionStore = class {
|
|
|
15721
15901
|
const body = `${lines.join("\n")}
|
|
15722
15902
|
`;
|
|
15723
15903
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Session exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
15724
|
-
const temporary =
|
|
15904
|
+
const temporary = join23(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID13()}.tmp`);
|
|
15725
15905
|
let handle;
|
|
15726
15906
|
try {
|
|
15727
15907
|
handle = await open7(temporary, constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY, 384);
|
|
@@ -15838,7 +16018,7 @@ var SessionStore = class {
|
|
|
15838
16018
|
const metadata = await lstat8(path);
|
|
15839
16019
|
if (!metadata.isFile()) throw new Error("Session path is not a regular file");
|
|
15840
16020
|
await rm8(path);
|
|
15841
|
-
await rm8(
|
|
16021
|
+
await rm8(join23(this.#sessions, `${sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
|
|
15842
16022
|
} catch (error) {
|
|
15843
16023
|
if (!isCode8(error, "ENOENT")) throw error;
|
|
15844
16024
|
}
|
|
@@ -15855,7 +16035,7 @@ var SessionStore = class {
|
|
|
15855
16035
|
const excess = entries.slice(this.#maxSessions);
|
|
15856
16036
|
for (const entry2 of excess) {
|
|
15857
16037
|
await rm8(this.#path(entry2.sessionId), { force: true }).catch(() => void 0);
|
|
15858
|
-
await rm8(
|
|
16038
|
+
await rm8(join23(this.#sessions, `${entry2.sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
|
|
15859
16039
|
}
|
|
15860
16040
|
}
|
|
15861
16041
|
async #assertWorkspace(stored) {
|
|
@@ -15877,7 +16057,7 @@ var SessionStore = class {
|
|
|
15877
16057
|
if (!isWithin5(canonicalRoot, canonicalSessions)) throw new Error("Session directory escapes the workspace");
|
|
15878
16058
|
}
|
|
15879
16059
|
async #quarantine(path) {
|
|
15880
|
-
const target = `${path}.corrupt-${Date.now()}-${
|
|
16060
|
+
const target = `${path}.corrupt-${Date.now()}-${randomUUID13().slice(0, 8)}`;
|
|
15881
16061
|
await rename6(path, target);
|
|
15882
16062
|
return target;
|
|
15883
16063
|
}
|
|
@@ -16096,15 +16276,15 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
16096
16276
|
}
|
|
16097
16277
|
|
|
16098
16278
|
// src/session/tree.ts
|
|
16099
|
-
import { randomUUID as
|
|
16279
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
16100
16280
|
import { mkdir as mkdir17, readFile as readFile19, rename as rename8, writeFile as writeFile11 } from "fs/promises";
|
|
16101
|
-
import { dirname as dirname17, join as
|
|
16281
|
+
import { dirname as dirname17, join as join25, resolve as resolve21 } from "path";
|
|
16102
16282
|
|
|
16103
16283
|
// src/session/checkpoint.ts
|
|
16104
|
-
import { createHash as
|
|
16284
|
+
import { createHash as createHash15, randomUUID as randomUUID14 } from "crypto";
|
|
16105
16285
|
import { execFile as execFile2 } from "child_process";
|
|
16106
16286
|
import { lstat as lstat9, mkdir as mkdir16, open as open8, readFile as readFile18, readdir as readdir7, rename as rename7, rm as rm9, writeFile as writeFile10 } from "fs/promises";
|
|
16107
|
-
import { dirname as dirname16, join as
|
|
16287
|
+
import { dirname as dirname16, join as join24, relative as relative14, resolve as resolve20, sep as sep14 } from "path";
|
|
16108
16288
|
var EXCLUDED = /* @__PURE__ */ new Set([".git", ".flavor", "node_modules", "dist", "build", "release", ".worktrees"]);
|
|
16109
16289
|
var MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
16110
16290
|
var MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
@@ -16117,9 +16297,9 @@ var WorkspaceCheckpointStore = class {
|
|
|
16117
16297
|
#maxTotalBytes;
|
|
16118
16298
|
constructor(options) {
|
|
16119
16299
|
this.#workspace = resolve20(options.workspace);
|
|
16120
|
-
this.#root =
|
|
16121
|
-
this.#objects =
|
|
16122
|
-
this.#manifests =
|
|
16300
|
+
this.#root = join24(this.#workspace, ".flavor", "checkpoints");
|
|
16301
|
+
this.#objects = join24(this.#root, "objects");
|
|
16302
|
+
this.#manifests = join24(this.#root, "manifests");
|
|
16123
16303
|
this.#maxFileBytes = options.maxFileBytes ?? MAX_FILE_BYTES;
|
|
16124
16304
|
this.#maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
|
|
16125
16305
|
}
|
|
@@ -16138,8 +16318,8 @@ var WorkspaceCheckpointStore = class {
|
|
|
16138
16318
|
total += info.size;
|
|
16139
16319
|
if (total > this.#maxTotalBytes) throw new Error("Checkpoint exceeds total size limit");
|
|
16140
16320
|
const content = await readFile18(absolute);
|
|
16141
|
-
const digest =
|
|
16142
|
-
const objectPath =
|
|
16321
|
+
const digest = createHash15("sha256").update(content).digest("hex");
|
|
16322
|
+
const objectPath = join24(this.#objects, digest);
|
|
16143
16323
|
try {
|
|
16144
16324
|
const handle = await open8(objectPath, "wx");
|
|
16145
16325
|
try {
|
|
@@ -16154,17 +16334,17 @@ var WorkspaceCheckpointStore = class {
|
|
|
16154
16334
|
}
|
|
16155
16335
|
const checkpoint = {
|
|
16156
16336
|
version: 1,
|
|
16157
|
-
id: `checkpoint-${Date.now()}-${
|
|
16337
|
+
id: `checkpoint-${Date.now()}-${randomUUID14().slice(0, 8)}`,
|
|
16158
16338
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16159
16339
|
...label === void 0 || label.trim().length === 0 ? {} : { label: label.trim() },
|
|
16160
16340
|
files
|
|
16161
16341
|
};
|
|
16162
|
-
await atomicJson(
|
|
16342
|
+
await atomicJson(join24(this.#manifests, `${checkpoint.id}.json`), checkpoint);
|
|
16163
16343
|
return checkpoint;
|
|
16164
16344
|
}
|
|
16165
16345
|
async load(id) {
|
|
16166
16346
|
if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
|
|
16167
|
-
const value = JSON.parse(await readFile18(
|
|
16347
|
+
const value = JSON.parse(await readFile18(join24(this.#manifests, `${id}.json`), "utf8"));
|
|
16168
16348
|
if (value.version !== 1 || value.id !== id || !Array.isArray(value.files)) throw new Error("Invalid checkpoint manifest");
|
|
16169
16349
|
for (const file of value.files) {
|
|
16170
16350
|
contained(this.#workspace, file.path);
|
|
@@ -16176,8 +16356,8 @@ var WorkspaceCheckpointStore = class {
|
|
|
16176
16356
|
const checkpoint = await this.load(id);
|
|
16177
16357
|
const objects = /* @__PURE__ */ new Map();
|
|
16178
16358
|
for (const file of checkpoint.files) {
|
|
16179
|
-
const object = await readFile18(
|
|
16180
|
-
if (object.length !== file.size ||
|
|
16359
|
+
const object = await readFile18(join24(this.#objects, file.digest));
|
|
16360
|
+
if (object.length !== file.size || createHash15("sha256").update(object).digest("hex") !== file.digest) {
|
|
16181
16361
|
throw new Error(`Checkpoint object is corrupt: ${file.digest}`);
|
|
16182
16362
|
}
|
|
16183
16363
|
objects.set(file.digest, object);
|
|
@@ -16191,7 +16371,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
16191
16371
|
const target = contained(this.#workspace, file.path);
|
|
16192
16372
|
const object = objects.get(file.digest);
|
|
16193
16373
|
await mkdir16(dirname16(target), { recursive: true });
|
|
16194
|
-
const temporary = `${target}.${process.pid}.${
|
|
16374
|
+
const temporary = `${target}.${process.pid}.${randomUUID14()}.tmp`;
|
|
16195
16375
|
await writeFile10(temporary, object, { mode: file.mode });
|
|
16196
16376
|
await rename7(temporary, target);
|
|
16197
16377
|
}
|
|
@@ -16199,7 +16379,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
16199
16379
|
}
|
|
16200
16380
|
};
|
|
16201
16381
|
async function discoverFiles(workspace) {
|
|
16202
|
-
if (await exists2(
|
|
16382
|
+
if (await exists2(join24(workspace, ".git"))) {
|
|
16203
16383
|
const listed = await gitFiles(workspace);
|
|
16204
16384
|
return listed.filter((path) => !EXCLUDED.has(path.split("/")[0] ?? "")).sort();
|
|
16205
16385
|
}
|
|
@@ -16208,7 +16388,7 @@ async function discoverFiles(workspace) {
|
|
|
16208
16388
|
const entries = await readdir7(directory, { withFileTypes: true });
|
|
16209
16389
|
for (const entry2 of entries) {
|
|
16210
16390
|
if (EXCLUDED.has(entry2.name)) continue;
|
|
16211
|
-
const absolute =
|
|
16391
|
+
const absolute = join24(directory, entry2.name);
|
|
16212
16392
|
const path = normalizeRelative(relative14(workspace, absolute));
|
|
16213
16393
|
if (entry2.isSymbolicLink()) throw new Error(`Checkpoint refuses symbolic link: ${path}`);
|
|
16214
16394
|
if (entry2.isDirectory()) await walk(absolute);
|
|
@@ -16263,7 +16443,7 @@ function contained(workspace, path) {
|
|
|
16263
16443
|
}
|
|
16264
16444
|
async function atomicJson(path, value) {
|
|
16265
16445
|
await mkdir16(dirname16(path), { recursive: true });
|
|
16266
|
-
const temporary = `${path}.${process.pid}.${
|
|
16446
|
+
const temporary = `${path}.${process.pid}.${randomUUID14()}.tmp`;
|
|
16267
16447
|
await writeFile10(temporary, `${JSON.stringify(value)}
|
|
16268
16448
|
`, { encoding: "utf8", mode: 384 });
|
|
16269
16449
|
await rename7(temporary, path);
|
|
@@ -16281,14 +16461,14 @@ var SessionHistory = class _SessionHistory {
|
|
|
16281
16461
|
#leafId;
|
|
16282
16462
|
#unrevert;
|
|
16283
16463
|
constructor(options, document) {
|
|
16284
|
-
this.#path =
|
|
16464
|
+
this.#path = join25(resolve21(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
16285
16465
|
this.#checkpoints = new WorkspaceCheckpointStore({ workspace: options.workspace });
|
|
16286
16466
|
this.#restoreContext = options.restoreContext;
|
|
16287
16467
|
this.#nodes = document.nodes;
|
|
16288
16468
|
this.#leafId = document.leafId;
|
|
16289
16469
|
}
|
|
16290
16470
|
static async open(options) {
|
|
16291
|
-
const path =
|
|
16471
|
+
const path = join25(resolve21(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
16292
16472
|
let document = { version: 1, sessionId: options.sessionId, leafId: null, nodes: [] };
|
|
16293
16473
|
try {
|
|
16294
16474
|
const parsed = JSON.parse(await readFile19(path, "utf8"));
|
|
@@ -16308,7 +16488,7 @@ var SessionHistory = class _SessionHistory {
|
|
|
16308
16488
|
async append(input) {
|
|
16309
16489
|
const checkpoint = await this.#checkpoints.create(input.label);
|
|
16310
16490
|
const node = {
|
|
16311
|
-
id: `turn-${Date.now()}-${
|
|
16491
|
+
id: `turn-${Date.now()}-${randomUUID15().slice(0, 8)}`,
|
|
16312
16492
|
parentId: this.#leafId,
|
|
16313
16493
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16314
16494
|
prompt: input.prompt,
|
|
@@ -16366,7 +16546,7 @@ var SessionHistory = class _SessionHistory {
|
|
|
16366
16546
|
nodes: this.#nodes
|
|
16367
16547
|
};
|
|
16368
16548
|
await mkdir17(dirname17(this.#path), { recursive: true });
|
|
16369
|
-
const temporary = `${this.#path}.${process.pid}.${
|
|
16549
|
+
const temporary = `${this.#path}.${process.pid}.${randomUUID15()}.tmp`;
|
|
16370
16550
|
await writeFile11(temporary, `${JSON.stringify(document)}
|
|
16371
16551
|
`, { encoding: "utf8", mode: 384 });
|
|
16372
16552
|
await rename8(temporary, this.#path);
|
|
@@ -16392,9 +16572,9 @@ function isCode10(error, code) {
|
|
|
16392
16572
|
|
|
16393
16573
|
// src/sleep/organizer.ts
|
|
16394
16574
|
import { constants as constants7 } from "fs";
|
|
16395
|
-
import { randomUUID as
|
|
16575
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
16396
16576
|
import { lstat as lstat10, mkdir as mkdir18, open as open9, readdir as readdir8, realpath as realpath11, rename as rename9, rm as rm10 } from "fs/promises";
|
|
16397
|
-
import { isAbsolute as isAbsolute10, join as
|
|
16577
|
+
import { isAbsolute as isAbsolute10, join as join26, relative as relative15, resolve as resolve22, sep as sep15 } from "path";
|
|
16398
16578
|
import { z as z28 } from "zod";
|
|
16399
16579
|
var DateKeySchema = z28.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
16400
16580
|
var ReviewSchema = z28.object({
|
|
@@ -16449,7 +16629,7 @@ var ProjectSleepOrganizer = class {
|
|
|
16449
16629
|
#generate;
|
|
16450
16630
|
constructor(options) {
|
|
16451
16631
|
this.#workspace = resolve22(options.workspace);
|
|
16452
|
-
this.#sleepDirectory =
|
|
16632
|
+
this.#sleepDirectory = join26(this.#workspace, ".flavor", "sleep");
|
|
16453
16633
|
this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
|
|
16454
16634
|
this.#generate = options.generate;
|
|
16455
16635
|
}
|
|
@@ -16458,7 +16638,7 @@ var ProjectSleepOrganizer = class {
|
|
|
16458
16638
|
signal.throwIfAborted();
|
|
16459
16639
|
await this.#prepareDirectory();
|
|
16460
16640
|
if (await this.#hasReport(date)) return { status: "exists", date };
|
|
16461
|
-
const lockPath =
|
|
16641
|
+
const lockPath = join26(this.#sleepDirectory, `.${date}.lock`);
|
|
16462
16642
|
let lock;
|
|
16463
16643
|
try {
|
|
16464
16644
|
lock = await open9(lockPath, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
@@ -16493,7 +16673,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16493
16673
|
}
|
|
16494
16674
|
if (review === void 0) throw lastError;
|
|
16495
16675
|
const filename2 = `${date}-${filenameSummary(review.title)}.md`;
|
|
16496
|
-
const path =
|
|
16676
|
+
const path = join26(this.#sleepDirectory, filename2);
|
|
16497
16677
|
const markdown = renderSleepReport(date, review, documents);
|
|
16498
16678
|
await this.#atomicWrite(path, markdown);
|
|
16499
16679
|
return { status: "written", date, path, sessionCount: documents.length };
|
|
@@ -16535,7 +16715,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16535
16715
|
});
|
|
16536
16716
|
}
|
|
16537
16717
|
async #prepareDirectory() {
|
|
16538
|
-
const flavorDirectory =
|
|
16718
|
+
const flavorDirectory = join26(this.#workspace, ".flavor");
|
|
16539
16719
|
await assertNotSymlink(flavorDirectory);
|
|
16540
16720
|
await mkdir18(flavorDirectory, { recursive: true, mode: 448 });
|
|
16541
16721
|
await assertNotSymlink(this.#sleepDirectory);
|
|
@@ -16549,7 +16729,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16549
16729
|
return (await readdir8(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
|
|
16550
16730
|
}
|
|
16551
16731
|
async #atomicWrite(path, content) {
|
|
16552
|
-
const temporary =
|
|
16732
|
+
const temporary = join26(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
|
|
16553
16733
|
let handle;
|
|
16554
16734
|
try {
|
|
16555
16735
|
handle = await open9(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
@@ -16925,7 +17105,7 @@ function computeSessionStats(documents) {
|
|
|
16925
17105
|
import { constants as constants8 } from "fs";
|
|
16926
17106
|
import { mkdir as mkdir19, open as open10, readFile as readFile20, realpath as realpath12, rename as rename10, rm as rm11, stat as stat8 } from "fs/promises";
|
|
16927
17107
|
import { dirname as dirname18, isAbsolute as isAbsolute11, relative as relative16, resolve as resolve23, sep as sep16 } from "path";
|
|
16928
|
-
import { randomUUID as
|
|
17108
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
16929
17109
|
import { z as z29 } from "zod";
|
|
16930
17110
|
|
|
16931
17111
|
// src/tools/file-diff.ts
|
|
@@ -17278,7 +17458,7 @@ function createPathGuard(workspace) {
|
|
|
17278
17458
|
async function atomicWrite(path, content, signal, expectedVersion) {
|
|
17279
17459
|
abortIfNeeded(signal);
|
|
17280
17460
|
await mkdir19(dirname18(path), { recursive: true });
|
|
17281
|
-
const temporary = `${path}.${
|
|
17461
|
+
const temporary = `${path}.${randomUUID17()}.tmp`;
|
|
17282
17462
|
try {
|
|
17283
17463
|
const handle = await open10(temporary, constants8.O_CREAT | constants8.O_EXCL | constants8.O_WRONLY, 384);
|
|
17284
17464
|
try {
|
|
@@ -19340,18 +19520,40 @@ function hookAnswersFromUpdatedInput(updatedInput, questions) {
|
|
|
19340
19520
|
var QuestionBridge = class {
|
|
19341
19521
|
#pending;
|
|
19342
19522
|
#onChange;
|
|
19343
|
-
|
|
19523
|
+
#relay;
|
|
19524
|
+
#active = false;
|
|
19525
|
+
constructor(onChange, relay) {
|
|
19344
19526
|
this.#onChange = onChange;
|
|
19527
|
+
this.#relay = relay;
|
|
19345
19528
|
}
|
|
19346
19529
|
get pending() {
|
|
19347
19530
|
return this.#pending?.questions;
|
|
19348
19531
|
}
|
|
19349
19532
|
ask(questions, signal) {
|
|
19350
|
-
if (this.#
|
|
19533
|
+
if (this.#active) return Promise.reject(new Error("A question is already pending"));
|
|
19351
19534
|
if (signal.aborted) return Promise.reject(signal.reason);
|
|
19535
|
+
this.#active = true;
|
|
19536
|
+
if (this.#relay !== void 0) return this.#askRelayed(questions, signal);
|
|
19537
|
+
return this.#askLocally(questions, signal);
|
|
19538
|
+
}
|
|
19539
|
+
async #askRelayed(questions, signal) {
|
|
19540
|
+
try {
|
|
19541
|
+
const answers = await this.#relay(questions, signal);
|
|
19542
|
+
if (answers !== void 0) {
|
|
19543
|
+
this.#active = false;
|
|
19544
|
+
return answers;
|
|
19545
|
+
}
|
|
19546
|
+
return await this.#askLocally(questions, signal);
|
|
19547
|
+
} catch (error) {
|
|
19548
|
+
this.#active = false;
|
|
19549
|
+
throw error;
|
|
19550
|
+
}
|
|
19551
|
+
}
|
|
19552
|
+
#askLocally(questions, signal) {
|
|
19352
19553
|
return new Promise((resolve41, reject) => {
|
|
19353
19554
|
const onAbort = () => {
|
|
19354
19555
|
this.#pending = void 0;
|
|
19556
|
+
this.#active = false;
|
|
19355
19557
|
reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
|
|
19356
19558
|
};
|
|
19357
19559
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -19360,12 +19562,14 @@ var QuestionBridge = class {
|
|
|
19360
19562
|
resolve: (answers) => {
|
|
19361
19563
|
signal.removeEventListener("abort", onAbort);
|
|
19362
19564
|
this.#pending = void 0;
|
|
19565
|
+
this.#active = false;
|
|
19363
19566
|
this.#onChange?.();
|
|
19364
19567
|
resolve41(answers);
|
|
19365
19568
|
},
|
|
19366
19569
|
reject: (error) => {
|
|
19367
19570
|
signal.removeEventListener("abort", onAbort);
|
|
19368
19571
|
this.#pending = void 0;
|
|
19572
|
+
this.#active = false;
|
|
19369
19573
|
this.#onChange?.();
|
|
19370
19574
|
reject(error);
|
|
19371
19575
|
}
|
|
@@ -19451,9 +19655,9 @@ function createTodoWriteTool() {
|
|
|
19451
19655
|
}
|
|
19452
19656
|
|
|
19453
19657
|
// src/tools/managed.ts
|
|
19454
|
-
import { randomUUID as
|
|
19455
|
-
import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile22, readdir as readdir9, unlink as
|
|
19456
|
-
import { dirname as dirname21, join as
|
|
19658
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
19659
|
+
import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile22, readdir as readdir9, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
|
|
19660
|
+
import { dirname as dirname21, join as join27, resolve as resolve26 } from "path";
|
|
19457
19661
|
import { z as z37 } from "zod";
|
|
19458
19662
|
var MANAGED_TOOL_VERSION = 1;
|
|
19459
19663
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -19496,8 +19700,8 @@ var ManagedToolStore = class {
|
|
|
19496
19700
|
#diagnostics = [];
|
|
19497
19701
|
constructor(options) {
|
|
19498
19702
|
this.#workspace = resolve26(options.workspace);
|
|
19499
|
-
this.#projectRoot =
|
|
19500
|
-
this.#globalRoot =
|
|
19703
|
+
this.#projectRoot = join27(this.#workspace, ".flavor", "tools");
|
|
19704
|
+
this.#globalRoot = join27(resolve26(options.home), ".flavor-code", "tools");
|
|
19501
19705
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
19502
19706
|
}
|
|
19503
19707
|
get diagnostics() {
|
|
@@ -19544,7 +19748,7 @@ var ManagedToolStore = class {
|
|
|
19544
19748
|
version: MANAGED_TOOL_VERSION,
|
|
19545
19749
|
createdAt: this.#now()
|
|
19546
19750
|
});
|
|
19547
|
-
const path =
|
|
19751
|
+
const path = join27(this.directory(input.scope), filename(input.name));
|
|
19548
19752
|
const definition = compileDefinition(record3, input.scope, this.#workspace);
|
|
19549
19753
|
await writeExclusive(path, `${JSON.stringify(record3, void 0, 2)}
|
|
19550
19754
|
`);
|
|
@@ -19560,7 +19764,7 @@ var ManagedToolStore = class {
|
|
|
19560
19764
|
const entry2 = matches[0];
|
|
19561
19765
|
const info = await lstat11(entry2.path);
|
|
19562
19766
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Managed tool path is no longer a regular file: ${entry2.path}`);
|
|
19563
|
-
await
|
|
19767
|
+
await unlink3(entry2.path);
|
|
19564
19768
|
this.#entries.delete(scopedKey(entry2.scope, entry2.record.name));
|
|
19565
19769
|
return summary(entry2, false);
|
|
19566
19770
|
}
|
|
@@ -19569,7 +19773,7 @@ var ManagedToolStore = class {
|
|
|
19569
19773
|
if (!parsed.success) return [];
|
|
19570
19774
|
const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
|
|
19571
19775
|
if (matches.length > 0) return matches.map((entry2) => entry2.path);
|
|
19572
|
-
return [
|
|
19776
|
+
return [join27(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
|
|
19573
19777
|
}
|
|
19574
19778
|
async #loadDirectory(scope2) {
|
|
19575
19779
|
const root = this.directory(scope2);
|
|
@@ -19582,7 +19786,7 @@ var ManagedToolStore = class {
|
|
|
19582
19786
|
return;
|
|
19583
19787
|
}
|
|
19584
19788
|
for (const name of names) {
|
|
19585
|
-
const path =
|
|
19789
|
+
const path = join27(root, name);
|
|
19586
19790
|
try {
|
|
19587
19791
|
const info = await lstat11(path);
|
|
19588
19792
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
|
|
@@ -19738,13 +19942,13 @@ ${sourceUrl}`
|
|
|
19738
19942
|
}
|
|
19739
19943
|
async function writeExclusive(path, content) {
|
|
19740
19944
|
await mkdir20(dirname21(path), { recursive: true });
|
|
19741
|
-
const temporary = `${path}.${
|
|
19945
|
+
const temporary = `${path}.${randomUUID18()}.tmp`;
|
|
19742
19946
|
try {
|
|
19743
19947
|
await writeFile12(temporary, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
19744
19948
|
await link(temporary, path);
|
|
19745
19949
|
} finally {
|
|
19746
19950
|
try {
|
|
19747
|
-
await
|
|
19951
|
+
await unlink3(temporary);
|
|
19748
19952
|
} catch {
|
|
19749
19953
|
}
|
|
19750
19954
|
}
|
|
@@ -20692,6 +20896,8 @@ var FlavorSession = class {
|
|
|
20692
20896
|
this.#activeSubmission = submission;
|
|
20693
20897
|
this.#interrupted = false;
|
|
20694
20898
|
let outcome = "completed";
|
|
20899
|
+
let assistantSummary = "";
|
|
20900
|
+
let deliverables = [];
|
|
20695
20901
|
try {
|
|
20696
20902
|
const decision = await this.#services.hooks.emit({
|
|
20697
20903
|
version: 1,
|
|
@@ -20718,6 +20924,8 @@ var FlavorSession = class {
|
|
|
20718
20924
|
...submission.initialUserMessage === void 0 ? {} : { initialUserMessage: submission.initialUserMessage }
|
|
20719
20925
|
})) {
|
|
20720
20926
|
this.#services.output(event);
|
|
20927
|
+
if (event.type === "text") assistantSummary = `${assistantSummary}${event.text}`.slice(-2e3);
|
|
20928
|
+
if (event.type === "deliverables") deliverables = event.files.slice(0, 100);
|
|
20721
20929
|
if (event.type === "error") outcome = "failed";
|
|
20722
20930
|
}
|
|
20723
20931
|
if (controller2.signal.aborted) outcome = "cancelled";
|
|
@@ -20742,7 +20950,15 @@ var FlavorSession = class {
|
|
|
20742
20950
|
this.#activeSubmission = void 0;
|
|
20743
20951
|
if (submission.coWorkPlanningKey !== void 0) this.#finishActivePlanning(submission.coWorkPlanningKey);
|
|
20744
20952
|
try {
|
|
20745
|
-
await this.#services.hooks.emit({
|
|
20953
|
+
await this.#services.hooks.emit({
|
|
20954
|
+
version: 1,
|
|
20955
|
+
type: "Stop",
|
|
20956
|
+
payload: {
|
|
20957
|
+
outcome,
|
|
20958
|
+
...assistantSummary.trim() ? { summary: assistantSummary.trim() } : {},
|
|
20959
|
+
...deliverables.length === 0 ? {} : { deliverables }
|
|
20960
|
+
}
|
|
20961
|
+
});
|
|
20746
20962
|
} catch (error) {
|
|
20747
20963
|
try {
|
|
20748
20964
|
this.#services.output({ type: "error", error: { code: "unknown", message: message(error) } });
|
|
@@ -21475,7 +21691,7 @@ function buildWarningMessages(report, languageTag) {
|
|
|
21475
21691
|
}
|
|
21476
21692
|
|
|
21477
21693
|
// src/hallucination/sliding-window.ts
|
|
21478
|
-
import { createHash as
|
|
21694
|
+
import { createHash as createHash16 } from "crypto";
|
|
21479
21695
|
var SlidingWindow = class {
|
|
21480
21696
|
#windowSize;
|
|
21481
21697
|
#threshold;
|
|
@@ -21491,7 +21707,7 @@ var SlidingWindow = class {
|
|
|
21491
21707
|
}
|
|
21492
21708
|
hash(toolName, params) {
|
|
21493
21709
|
const sorted = JSON.stringify(params, sortedKeys(params));
|
|
21494
|
-
return
|
|
21710
|
+
return createHash16("sha256").update(`${toolName}:${sorted}`).digest("hex");
|
|
21495
21711
|
}
|
|
21496
21712
|
push(toolName, params) {
|
|
21497
21713
|
const hash = this.hash(toolName, params);
|
|
@@ -21846,9 +22062,9 @@ ${total}`;
|
|
|
21846
22062
|
}
|
|
21847
22063
|
|
|
21848
22064
|
// src/memory/store.ts
|
|
21849
|
-
import { createHash as
|
|
22065
|
+
import { createHash as createHash17 } from "crypto";
|
|
21850
22066
|
import { readFile as readFile23, rm as rm12 } from "fs/promises";
|
|
21851
|
-
import { resolve as resolve27, join as
|
|
22067
|
+
import { resolve as resolve27, join as join28, relative as relative19, sep as sep19 } from "path";
|
|
21852
22068
|
|
|
21853
22069
|
// src/memory/similarity.ts
|
|
21854
22070
|
function normalizeForSimilarity(value) {
|
|
@@ -21951,9 +22167,9 @@ var MemoryStore = class {
|
|
|
21951
22167
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
21952
22168
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
21953
22169
|
this.workspace = resolve27(options.workspace);
|
|
21954
|
-
this.#memoryRoot =
|
|
21955
|
-
this.path =
|
|
21956
|
-
this.#behaviorPath =
|
|
22170
|
+
this.#memoryRoot = join28(this.workspace, ".flavor", "memory");
|
|
22171
|
+
this.path = join28(this.#memoryRoot, "MEMORY.md");
|
|
22172
|
+
this.#behaviorPath = join28(this.#memoryRoot, "behavior.json");
|
|
21957
22173
|
this.#maxEntries = options.maxEntries;
|
|
21958
22174
|
this.#maxEntryChars = options.maxEntryChars;
|
|
21959
22175
|
}
|
|
@@ -22332,7 +22548,7 @@ function sanitizeTopicKey(value) {
|
|
|
22332
22548
|
return value.normalize("NFKC").toLocaleLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 128);
|
|
22333
22549
|
}
|
|
22334
22550
|
function memoryId(type, content) {
|
|
22335
|
-
return
|
|
22551
|
+
return createHash17("sha256").update(`${type}\0${normalizeMemoryContent(content).toLocaleLowerCase()}`, "utf8").digest("hex").slice(0, 12);
|
|
22336
22552
|
}
|
|
22337
22553
|
function assertTaskId(taskId) {
|
|
22338
22554
|
if (!TASK_ID.test(taskId)) throw new Error(`Invalid memory task id: ${taskId}`);
|
|
@@ -22742,7 +22958,7 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
22742
22958
|
|
|
22743
22959
|
// src/ide/client.ts
|
|
22744
22960
|
import { readFile as readFile24, readdir as readdir10, stat as stat10 } from "fs/promises";
|
|
22745
|
-
import { basename as basename7, join as
|
|
22961
|
+
import { basename as basename7, join as join29, relative as relative21, resolve as resolve29, sep as sep21 } from "path";
|
|
22746
22962
|
var FlavorIdeClient = class {
|
|
22747
22963
|
#workspace;
|
|
22748
22964
|
#home;
|
|
@@ -22838,7 +23054,7 @@ var FlavorIdeClient = class {
|
|
|
22838
23054
|
};
|
|
22839
23055
|
if (await this.#healthy(configured)) return configured;
|
|
22840
23056
|
}
|
|
22841
|
-
const directory =
|
|
23057
|
+
const directory = join29(this.#home, ".flavor-code", "ide");
|
|
22842
23058
|
let entries;
|
|
22843
23059
|
try {
|
|
22844
23060
|
entries = (await readdir10(directory)).filter((name) => name.endsWith(".lock"));
|
|
@@ -22846,7 +23062,7 @@ var FlavorIdeClient = class {
|
|
|
22846
23062
|
return void 0;
|
|
22847
23063
|
}
|
|
22848
23064
|
const candidates = (await Promise.all(entries.map(async (name) => {
|
|
22849
|
-
const path =
|
|
23065
|
+
const path = join29(directory, name);
|
|
22850
23066
|
try {
|
|
22851
23067
|
const [contents, info] = await Promise.all([readFile24(path, "utf8"), stat10(path)]);
|
|
22852
23068
|
const lock = parseLockfile(contents);
|
|
@@ -22966,7 +23182,7 @@ function relativeDisplay(workspace, filePath) {
|
|
|
22966
23182
|
}
|
|
22967
23183
|
|
|
22968
23184
|
// src/jobs/registry.ts
|
|
22969
|
-
import { randomUUID as
|
|
23185
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
22970
23186
|
var JobRegistry = class {
|
|
22971
23187
|
#jobs = /* @__PURE__ */ new Map();
|
|
22972
23188
|
#maxOutputChars;
|
|
@@ -22980,7 +23196,7 @@ var JobRegistry = class {
|
|
|
22980
23196
|
}
|
|
22981
23197
|
create(input) {
|
|
22982
23198
|
this.#pruneCompleted();
|
|
22983
|
-
const id = `job-${
|
|
23199
|
+
const id = `job-${randomUUID19().slice(0, 12)}`;
|
|
22984
23200
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22985
23201
|
let settle;
|
|
22986
23202
|
const done = new Promise((resolve41) => {
|
|
@@ -23106,7 +23322,7 @@ function snapshot2(job) {
|
|
|
23106
23322
|
}
|
|
23107
23323
|
|
|
23108
23324
|
// src/terminal/service.ts
|
|
23109
|
-
import { randomUUID as
|
|
23325
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
23110
23326
|
import { createRequire as nodeCreateRequire } from "module";
|
|
23111
23327
|
import { isAbsolute as isAbsolute14, relative as relative22, resolve as resolve30, sep as sep22 } from "path";
|
|
23112
23328
|
var TerminalService = class {
|
|
@@ -23124,7 +23340,7 @@ var TerminalService = class {
|
|
|
23124
23340
|
open(input) {
|
|
23125
23341
|
const cwd = workspacePath(this.#root, input.cwd ?? ".");
|
|
23126
23342
|
const shell2 = input.shell ?? defaultShell();
|
|
23127
|
-
const id = `term-${
|
|
23343
|
+
const id = `term-${randomUUID20().slice(0, 12)}`;
|
|
23128
23344
|
const pty = this.#factory(shell2, input.args ?? [], {
|
|
23129
23345
|
cwd,
|
|
23130
23346
|
cols: input.columns ?? 100,
|
|
@@ -23334,10 +23550,10 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
23334
23550
|
}
|
|
23335
23551
|
|
|
23336
23552
|
// src/pals/address.ts
|
|
23337
|
-
import { createHash as
|
|
23553
|
+
import { createHash as createHash18 } from "crypto";
|
|
23338
23554
|
function opaqueUserScope(userScope) {
|
|
23339
23555
|
if (userScope.length === 0) throw new Error("User scope must not be empty");
|
|
23340
|
-
const digest =
|
|
23556
|
+
const digest = createHash18("sha256").update(userScope, "utf8").digest("hex").slice(0, 16);
|
|
23341
23557
|
return `u-${digest}`;
|
|
23342
23558
|
}
|
|
23343
23559
|
function palSocketAddress(options) {
|
|
@@ -23350,32 +23566,32 @@ function palSocketAddress(options) {
|
|
|
23350
23566
|
}
|
|
23351
23567
|
|
|
23352
23568
|
// src/pals/broker-cli.ts
|
|
23353
|
-
import { createHash as
|
|
23569
|
+
import { createHash as createHash20 } from "crypto";
|
|
23354
23570
|
import { Socket as Socket2 } from "net";
|
|
23355
|
-
import { tmpdir as
|
|
23356
|
-
import { join as
|
|
23571
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
23572
|
+
import { join as join31 } from "path";
|
|
23357
23573
|
import { spawn as spawn7 } from "child_process";
|
|
23358
23574
|
|
|
23359
23575
|
// src/pals/broker.ts
|
|
23360
|
-
import { createHash as
|
|
23361
|
-
import { chmod as
|
|
23362
|
-
import { createServer as
|
|
23576
|
+
import { createHash as createHash19, randomUUID as randomUUID23, timingSafeEqual } from "crypto";
|
|
23577
|
+
import { chmod as chmod3, lstat as lstat12, mkdir as mkdir23, unlink as unlink6 } from "fs/promises";
|
|
23578
|
+
import { createServer as createServer4, Socket } from "net";
|
|
23363
23579
|
import { dirname as dirname23 } from "path";
|
|
23364
23580
|
|
|
23365
23581
|
// src/pals/auth.ts
|
|
23366
|
-
import { randomBytes as randomBytes3, randomUUID as
|
|
23367
|
-
import { chmod, link as link2, mkdir as mkdir21, open as open12, readFile as readFile25, unlink as
|
|
23582
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID21 } from "crypto";
|
|
23583
|
+
import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as readFile25, unlink as unlink4 } from "fs/promises";
|
|
23368
23584
|
import { homedir } from "os";
|
|
23369
|
-
import { join as
|
|
23585
|
+
import { join as join30 } from "path";
|
|
23370
23586
|
var PAL_AUTH_TOKEN_BYTES = 32;
|
|
23371
23587
|
var TOKEN_PATTERN = /^[a-f0-9]{64}$/;
|
|
23372
23588
|
async function loadOrCreatePalAuthToken(options = {}) {
|
|
23373
|
-
const directory =
|
|
23374
|
-
const tokenPath =
|
|
23589
|
+
const directory = join30(options.home ?? homedir(), ".flavor-code", "pals");
|
|
23590
|
+
const tokenPath = join30(directory, "auth-token");
|
|
23375
23591
|
await mkdir21(directory, { recursive: true, mode: 448 });
|
|
23376
|
-
await
|
|
23592
|
+
await chmod2(directory, 448);
|
|
23377
23593
|
const token = (options.random ?? randomBytes3)(PAL_AUTH_TOKEN_BYTES).toString("hex");
|
|
23378
|
-
const temporaryPath =
|
|
23594
|
+
const temporaryPath = join30(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
|
|
23379
23595
|
let handle;
|
|
23380
23596
|
try {
|
|
23381
23597
|
handle = await open12(temporaryPath, "wx", 384);
|
|
@@ -23394,13 +23610,13 @@ async function loadOrCreatePalAuthToken(options = {}) {
|
|
|
23394
23610
|
}
|
|
23395
23611
|
} finally {
|
|
23396
23612
|
await handle?.close();
|
|
23397
|
-
await
|
|
23613
|
+
await unlink4(temporaryPath).catch((error) => {
|
|
23398
23614
|
if (error.code !== "ENOENT") throw error;
|
|
23399
23615
|
});
|
|
23400
23616
|
}
|
|
23401
23617
|
const stored = (await readFile25(tokenPath, "utf8")).trim();
|
|
23402
23618
|
if (!TOKEN_PATTERN.test(stored)) throw new Error("Pals authentication token store is invalid");
|
|
23403
|
-
await
|
|
23619
|
+
await chmod2(tokenPath, 384);
|
|
23404
23620
|
return stored;
|
|
23405
23621
|
}
|
|
23406
23622
|
function validatePalAuthToken(token) {
|
|
@@ -23409,8 +23625,8 @@ function validatePalAuthToken(token) {
|
|
|
23409
23625
|
}
|
|
23410
23626
|
|
|
23411
23627
|
// src/pals/lifecycle.ts
|
|
23412
|
-
import { randomUUID as
|
|
23413
|
-
import { mkdir as mkdir22, open as open13, readFile as readFile26, rename as rename11, stat as stat11, unlink as
|
|
23628
|
+
import { randomUUID as randomUUID22 } from "crypto";
|
|
23629
|
+
import { mkdir as mkdir22, open as open13, readFile as readFile26, rename as rename11, stat as stat11, unlink as unlink5 } from "fs/promises";
|
|
23414
23630
|
import { dirname as dirname22 } from "path";
|
|
23415
23631
|
function defaultProcessAlive(pid) {
|
|
23416
23632
|
if (!Number.isSafeInteger(pid) || pid <= 0) return true;
|
|
@@ -23435,7 +23651,7 @@ async function acquirePalFileLock(options) {
|
|
|
23435
23651
|
await mkdir22(dirname22(options.path), { recursive: true, mode: 448 });
|
|
23436
23652
|
const pid = options.pid ?? process.pid;
|
|
23437
23653
|
const now = options.now ?? Date.now;
|
|
23438
|
-
const nonce =
|
|
23654
|
+
const nonce = randomUUID22();
|
|
23439
23655
|
const metadata = { pid, createdAt: now(), nonce };
|
|
23440
23656
|
let handle;
|
|
23441
23657
|
try {
|
|
@@ -23460,7 +23676,7 @@ async function acquirePalFileLock(options) {
|
|
|
23460
23676
|
if (openError.code !== "EEXIST") throw openError;
|
|
23461
23677
|
return void 0;
|
|
23462
23678
|
} finally {
|
|
23463
|
-
await
|
|
23679
|
+
await unlink5(tombstone).catch((unlinkError) => {
|
|
23464
23680
|
if (unlinkError.code !== "ENOENT") throw unlinkError;
|
|
23465
23681
|
});
|
|
23466
23682
|
}
|
|
@@ -23474,7 +23690,7 @@ async function acquirePalFileLock(options) {
|
|
|
23474
23690
|
const release = async () => {
|
|
23475
23691
|
const current = parseMetadata(await readFile26(options.path, "utf8").catch(() => ""));
|
|
23476
23692
|
if (current?.nonce !== nonce) return;
|
|
23477
|
-
await
|
|
23693
|
+
await unlink5(options.path).catch((error) => {
|
|
23478
23694
|
if (error.code !== "ENOENT") throw error;
|
|
23479
23695
|
});
|
|
23480
23696
|
};
|
|
@@ -23504,8 +23720,8 @@ function canConnect(address) {
|
|
|
23504
23720
|
});
|
|
23505
23721
|
}
|
|
23506
23722
|
function startupLockPath(address) {
|
|
23507
|
-
const digest =
|
|
23508
|
-
return
|
|
23723
|
+
const digest = createHash20("sha256").update(address, "utf8").digest("hex").slice(0, 24);
|
|
23724
|
+
return join31(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
|
|
23509
23725
|
}
|
|
23510
23726
|
async function spawnProductionBroker(address) {
|
|
23511
23727
|
const entry2 = process.argv[1];
|
|
@@ -23554,7 +23770,7 @@ async function ensureOnce(options) {
|
|
|
23554
23770
|
}
|
|
23555
23771
|
|
|
23556
23772
|
// src/pals/client.ts
|
|
23557
|
-
import { randomUUID as
|
|
23773
|
+
import { randomUUID as randomUUID24 } from "crypto";
|
|
23558
23774
|
import { Socket as Socket3 } from "net";
|
|
23559
23775
|
var DEFAULT_REQUEST_TIMEOUT_MS = 5e3;
|
|
23560
23776
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -23630,7 +23846,7 @@ var PalClient = class {
|
|
|
23630
23846
|
return this.starting;
|
|
23631
23847
|
}
|
|
23632
23848
|
async list() {
|
|
23633
|
-
const data2 = await this.request({ version: 1, type: "list", requestId:
|
|
23849
|
+
const data2 = await this.request({ version: 1, type: "list", requestId: randomUUID24() });
|
|
23634
23850
|
return PalPresenceListSchema.parse(data2);
|
|
23635
23851
|
}
|
|
23636
23852
|
async rename(alias) {
|
|
@@ -23643,38 +23859,38 @@ var PalClient = class {
|
|
|
23643
23859
|
throw error;
|
|
23644
23860
|
}
|
|
23645
23861
|
}
|
|
23646
|
-
async sendTask(target, message2, messageId =
|
|
23647
|
-
const data2 = await this.request({ version: 1, type: "task", requestId:
|
|
23862
|
+
async sendTask(target, message2, messageId = randomUUID24()) {
|
|
23863
|
+
const data2 = await this.request({ version: 1, type: "task", requestId: randomUUID24(), messageId, target, goal: message2 });
|
|
23648
23864
|
return DeliveryReceiptSchema.parse(data2);
|
|
23649
23865
|
}
|
|
23650
|
-
async sendChat(target, message2, messageId =
|
|
23651
|
-
const data2 = await this.request({ version: 1, type: "chat", requestId:
|
|
23866
|
+
async sendChat(target, message2, messageId = randomUUID24()) {
|
|
23867
|
+
const data2 = await this.request({ version: 1, type: "chat", requestId: randomUUID24(), messageId, target, message: message2 });
|
|
23652
23868
|
return DeliveryReceiptSchema.parse(data2);
|
|
23653
23869
|
}
|
|
23654
23870
|
async startCoWork(input) {
|
|
23655
23871
|
const data2 = await this.request({
|
|
23656
23872
|
version: 1,
|
|
23657
23873
|
type: "cowork-propose",
|
|
23658
|
-
requestId:
|
|
23659
|
-
coWorkId: input.coWorkId ??
|
|
23874
|
+
requestId: randomUUID24(),
|
|
23875
|
+
coWorkId: input.coWorkId ?? randomUUID24(),
|
|
23660
23876
|
goal: input.goal,
|
|
23661
23877
|
participants: input.participants
|
|
23662
23878
|
});
|
|
23663
23879
|
return CoWorkSnapshotSchema.parse(data2);
|
|
23664
23880
|
}
|
|
23665
23881
|
async coWorkAction(action) {
|
|
23666
|
-
const request = { version: 1, requestId:
|
|
23882
|
+
const request = { version: 1, requestId: randomUUID24(), ...action };
|
|
23667
23883
|
return CoWorkSnapshotSchema.parse(await this.request(request));
|
|
23668
23884
|
}
|
|
23669
23885
|
async coWorkStatus(coWorkId) {
|
|
23670
|
-
const data2 = await this.request({ version: 1, type: "cowork-get", requestId:
|
|
23886
|
+
const data2 = await this.request({ version: 1, type: "cowork-get", requestId: randomUUID24(), coWorkId });
|
|
23671
23887
|
return CoWorkSnapshotSchema.parse(data2);
|
|
23672
23888
|
}
|
|
23673
23889
|
async integrateCoWork(input) {
|
|
23674
23890
|
return this.coWorkAction({ type: "cowork-integration", ...input });
|
|
23675
23891
|
}
|
|
23676
23892
|
async cancelCoWork(coWorkId, reason) {
|
|
23677
|
-
const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId:
|
|
23893
|
+
const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId: randomUUID24(), coWorkId, reason });
|
|
23678
23894
|
return CoWorkSnapshotSchema.parse(data2);
|
|
23679
23895
|
}
|
|
23680
23896
|
subscribe(listener) {
|
|
@@ -23686,7 +23902,7 @@ var PalClient = class {
|
|
|
23686
23902
|
if (this.registered && this.socket !== void 0) {
|
|
23687
23903
|
let disconnectTimer;
|
|
23688
23904
|
await Promise.race([
|
|
23689
|
-
this.request({ version: 1, type: "disconnect", requestId:
|
|
23905
|
+
this.request({ version: 1, type: "disconnect", requestId: randomUUID24() }).catch(() => void 0),
|
|
23690
23906
|
new Promise((resolve41) => {
|
|
23691
23907
|
disconnectTimer = setTimeout(resolve41, Math.min(this.requestTimeoutMs, 250));
|
|
23692
23908
|
})
|
|
@@ -23777,7 +23993,7 @@ var PalClient = class {
|
|
|
23777
23993
|
const data2 = await this.request({
|
|
23778
23994
|
version: 1,
|
|
23779
23995
|
type: "register",
|
|
23780
|
-
requestId:
|
|
23996
|
+
requestId: randomUUID24(),
|
|
23781
23997
|
authToken: this.authToken,
|
|
23782
23998
|
presence: { version: 1, ...this.registration, connectedAt: now, lastSeenAt: now }
|
|
23783
23999
|
});
|
|
@@ -23889,7 +24105,7 @@ var PalClient = class {
|
|
|
23889
24105
|
if (!this.registered) return;
|
|
23890
24106
|
if (this.heartbeatPending) return;
|
|
23891
24107
|
this.heartbeatPending = true;
|
|
23892
|
-
void this.request({ version: 1, type: "heartbeat", requestId:
|
|
24108
|
+
void this.request({ version: 1, type: "heartbeat", requestId: randomUUID24() }).catch(() => void 0).finally(() => {
|
|
23893
24109
|
this.heartbeatPending = false;
|
|
23894
24110
|
});
|
|
23895
24111
|
}, this.heartbeatIntervalMs);
|
|
@@ -24176,7 +24392,7 @@ var ApprovalBridge = class {
|
|
|
24176
24392
|
request(request, signal = new AbortController().signal) {
|
|
24177
24393
|
if (this.#settle !== void 0) return Promise.resolve("deny");
|
|
24178
24394
|
if (signal.aborted) return Promise.resolve("deny");
|
|
24179
|
-
this.#pending = { id:
|
|
24395
|
+
this.#pending = { id: randomUUID25(), ...request };
|
|
24180
24396
|
this.#onChange?.();
|
|
24181
24397
|
return new Promise((resolvePromise) => {
|
|
24182
24398
|
this.#settle = resolvePromise;
|
|
@@ -24295,7 +24511,20 @@ async function createProductionRuntime(options) {
|
|
|
24295
24511
|
environment.OPENAI_API_KEY,
|
|
24296
24512
|
environment.ANTHROPIC_API_KEY
|
|
24297
24513
|
].filter((value) => typeof value === "string" && value.length > 0);
|
|
24298
|
-
|
|
24514
|
+
let hookSessionId;
|
|
24515
|
+
let islandControlMetadata;
|
|
24516
|
+
const hooks = new HookBus({
|
|
24517
|
+
eventContext: () => ({
|
|
24518
|
+
protocolVersion: 2,
|
|
24519
|
+
workspace,
|
|
24520
|
+
...hookSessionId === void 0 ? {} : { sessionId: hookSessionId },
|
|
24521
|
+
...islandControlMetadata === void 0 ? {} : {
|
|
24522
|
+
islandControlEndpoint: islandControlMetadata.endpoint,
|
|
24523
|
+
islandControlToken: islandControlMetadata.token,
|
|
24524
|
+
islandControlCapabilities: islandControlMetadata.capabilities
|
|
24525
|
+
}
|
|
24526
|
+
})
|
|
24527
|
+
});
|
|
24299
24528
|
hooks.on(
|
|
24300
24529
|
"PostToolUseFailure",
|
|
24301
24530
|
createIncidentReporter({
|
|
@@ -24315,8 +24544,7 @@ async function createProductionRuntime(options) {
|
|
|
24315
24544
|
diagnostics.push(...permissionPolicy.diagnostics);
|
|
24316
24545
|
const approvals = new ApprovalBridge(options.onApprovalChange);
|
|
24317
24546
|
const resolveToolApproval = options.approvalPolicy === "deny" && options.rpcToolApprovals !== true ? () => "deny" : (request, signal) => approvals.request(request, signal);
|
|
24318
|
-
const
|
|
24319
|
-
const askUserQuestionHandler = async (qs, signal) => {
|
|
24547
|
+
const relayQuestions = async (qs, signal) => {
|
|
24320
24548
|
if (options.approvalPolicy === "deny") throw new Error("AskUserQuestion is not available in non-interactive mode");
|
|
24321
24549
|
if (hooks.hasListeners("PermissionRequest")) {
|
|
24322
24550
|
let decision;
|
|
@@ -24334,8 +24562,10 @@ async function createProductionRuntime(options) {
|
|
|
24334
24562
|
const hookAnswers = hookAnswersFromUpdatedInput(decision.updatedInput, qs);
|
|
24335
24563
|
if (hookAnswers !== void 0) return hookAnswers;
|
|
24336
24564
|
}
|
|
24337
|
-
return
|
|
24565
|
+
return void 0;
|
|
24338
24566
|
};
|
|
24567
|
+
const questions = new QuestionBridge(options.onApprovalChange, relayQuestions);
|
|
24568
|
+
const askUserQuestionHandler = (qs, signal) => questions.ask(qs, signal);
|
|
24339
24569
|
const executionEnvironment = createExecutionEnvironment(workspace, config.execution);
|
|
24340
24570
|
const jobs = new JobRegistry();
|
|
24341
24571
|
const terminals = new TerminalService(workspace, { jobs });
|
|
@@ -24407,8 +24637,8 @@ async function createProductionRuntime(options) {
|
|
|
24407
24637
|
const registeredProviders = registration.registered;
|
|
24408
24638
|
let effectiveLlm = registration.effectiveLlm;
|
|
24409
24639
|
const pluginHost = new PluginHost({
|
|
24410
|
-
globalPluginDirs: [
|
|
24411
|
-
projectPluginDirs: [
|
|
24640
|
+
globalPluginDirs: [join32(home, ".flavor-code", "plugins")],
|
|
24641
|
+
projectPluginDirs: [join32(workspace, ".flavor", "plugins")],
|
|
24412
24642
|
config,
|
|
24413
24643
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
24414
24644
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -24482,6 +24712,7 @@ async function createProductionRuntime(options) {
|
|
|
24482
24712
|
const pendingCollaborationEvents = [];
|
|
24483
24713
|
let collaborationEventPump;
|
|
24484
24714
|
let collaborationEventsOpen = true;
|
|
24715
|
+
let islandControlServer;
|
|
24485
24716
|
const pumpCollaborationEvents = () => {
|
|
24486
24717
|
if (collaborationEventPump !== void 0 || collaborationSession === void 0 || deliverCollaborationEvent === void 0) return;
|
|
24487
24718
|
collaborationEventPump = (async () => {
|
|
@@ -24515,7 +24746,7 @@ async function createProductionRuntime(options) {
|
|
|
24515
24746
|
const address = palSocketAddress({
|
|
24516
24747
|
platform: process.platform,
|
|
24517
24748
|
userScope: home,
|
|
24518
|
-
runtimeDir:
|
|
24749
|
+
runtimeDir: join32(tmpdir4(), `flavor-code-pals-${createHash21("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
24519
24750
|
});
|
|
24520
24751
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
24521
24752
|
instanceId: collaboration.instanceId,
|
|
@@ -24626,8 +24857,8 @@ async function createProductionRuntime(options) {
|
|
|
24626
24857
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
24627
24858
|
});
|
|
24628
24859
|
const skills = new SkillRegistry({
|
|
24629
|
-
globalRoots: [
|
|
24630
|
-
projectRoots: [
|
|
24860
|
+
globalRoots: [join32(home, ".flavor-code", "skills")],
|
|
24861
|
+
projectRoots: [join32(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
24631
24862
|
authorizeResource: async () => true,
|
|
24632
24863
|
disabledNames: config.skills.disabled
|
|
24633
24864
|
});
|
|
@@ -24635,7 +24866,7 @@ async function createProductionRuntime(options) {
|
|
|
24635
24866
|
skillsReady.catch(() => void 0);
|
|
24636
24867
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
24637
24868
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
24638
|
-
const flavor = await optionalText2(
|
|
24869
|
+
const flavor = await optionalText2(join32(workspace, "FLAVOR.md"));
|
|
24639
24870
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
24640
24871
|
let memoryContext;
|
|
24641
24872
|
if (memoryStore !== void 0) {
|
|
@@ -24659,7 +24890,8 @@ async function createProductionRuntime(options) {
|
|
|
24659
24890
|
let taskResults = { ...recovered?.tasks.results ?? {} };
|
|
24660
24891
|
const subagentStartedAt = {};
|
|
24661
24892
|
const subagentElapsedMs = {};
|
|
24662
|
-
let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
24893
|
+
let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID25().slice(0, 8)}`;
|
|
24894
|
+
hookSessionId = sessionId;
|
|
24663
24895
|
let harnessJournal = new HarnessJournal({ workspace, sessionId });
|
|
24664
24896
|
const harnessRecovery = harnessJournal.recover();
|
|
24665
24897
|
if (harnessRecovery.queue.length > 0 || harnessRecovery.incompleteTools.length > 0 || harnessRecovery.incompleteModelIds.length > 0 || harnessRecovery.interruptedTurnIds.length > 0) {
|
|
@@ -24743,7 +24975,13 @@ async function createProductionRuntime(options) {
|
|
|
24743
24975
|
const publishTaskState = async () => {
|
|
24744
24976
|
harness.main.context.updateTaskState(serializedTaskState());
|
|
24745
24977
|
await persist();
|
|
24746
|
-
|
|
24978
|
+
const snapshot4 = taskSnapshot();
|
|
24979
|
+
emitOutput({ type: "tasks", snapshot: snapshot4 });
|
|
24980
|
+
await hooks.emit({
|
|
24981
|
+
version: 1,
|
|
24982
|
+
type: "Notification",
|
|
24983
|
+
payload: { kind: "task_snapshot", taskSnapshot: snapshot4 }
|
|
24984
|
+
}).catch(() => void 0);
|
|
24747
24985
|
};
|
|
24748
24986
|
for (const tool of createTaskPlanTools({
|
|
24749
24987
|
getPlan: () => taskPlan,
|
|
@@ -25162,7 +25400,7 @@ async function createProductionRuntime(options) {
|
|
|
25162
25400
|
evolveService.toolDefinition(),
|
|
25163
25401
|
...mcpTools
|
|
25164
25402
|
];
|
|
25165
|
-
const loopFlavor = await optionalText2(
|
|
25403
|
+
const loopFlavor = await optionalText2(join32(input.workspace, "FLAVOR.md"));
|
|
25166
25404
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
25167
25405
|
const loopEnvironment = createPromptEnvironment({
|
|
25168
25406
|
now: /* @__PURE__ */ new Date(),
|
|
@@ -25301,7 +25539,7 @@ async function createProductionRuntime(options) {
|
|
|
25301
25539
|
return answers[0] === "Continue" ? "approved" : "rejected";
|
|
25302
25540
|
},
|
|
25303
25541
|
fingerprint: workspaceFingerprint,
|
|
25304
|
-
idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
25542
|
+
idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID25().slice(0, 8)}`
|
|
25305
25543
|
});
|
|
25306
25544
|
const goalStore = new GoalStore({ workspace });
|
|
25307
25545
|
const goalOrchestrator = new GoalOrchestrator({
|
|
@@ -25320,7 +25558,7 @@ async function createProductionRuntime(options) {
|
|
|
25320
25558
|
executionEnvironment
|
|
25321
25559
|
),
|
|
25322
25560
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
25323
|
-
idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
25561
|
+
idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID25().slice(0, 8)}`,
|
|
25324
25562
|
runWorker: ({ workspace: goalWorkspace, prompt, signal }) => runLoopWorker({ workspace: goalWorkspace, prompt, signal })
|
|
25325
25563
|
});
|
|
25326
25564
|
let sessionHistory = await SessionHistory.open({
|
|
@@ -25433,7 +25671,7 @@ async function createProductionRuntime(options) {
|
|
|
25433
25671
|
runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
|
|
25434
25672
|
persist
|
|
25435
25673
|
),
|
|
25436
|
-
runLoop: (goal, signal) => runLoopSession(loopOrchestrator, goal, signal),
|
|
25674
|
+
runLoop: (goal, signal) => runLoopSession(loopOrchestrator, hooks, goal, signal),
|
|
25437
25675
|
runGoal: (goal, signal) => persistEach(runGoalSession(goalOrchestrator, goal, signal), persist),
|
|
25438
25676
|
mcp: async (command, signal) => {
|
|
25439
25677
|
signal.throwIfAborted();
|
|
@@ -25591,7 +25829,8 @@ ${summary2}`;
|
|
|
25591
25829
|
for (const key of Object.keys(subagentStartedAt)) delete subagentStartedAt[key];
|
|
25592
25830
|
for (const key of Object.keys(subagentElapsedMs)) delete subagentElapsedMs[key];
|
|
25593
25831
|
const previousIdeSessionId = ideSessionId;
|
|
25594
|
-
sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
25832
|
+
sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID25().slice(0, 8)}`;
|
|
25833
|
+
hookSessionId = sessionId;
|
|
25595
25834
|
harnessJournal = new HarnessJournal({ workspace, sessionId });
|
|
25596
25835
|
setUsageSession(sessionId);
|
|
25597
25836
|
ideSessionId = sessionId;
|
|
@@ -25691,7 +25930,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
25691
25930
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
25692
25931
|
}
|
|
25693
25932
|
try {
|
|
25694
|
-
const tokenStore = createFileTokenStore(
|
|
25933
|
+
const tokenStore = createFileTokenStore(join32(home, ".flavor-code", "auth.json"));
|
|
25695
25934
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
25696
25935
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
25697
25936
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -25734,7 +25973,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
25734
25973
|
}
|
|
25735
25974
|
},
|
|
25736
25975
|
async logout() {
|
|
25737
|
-
const tokenStore = createFileTokenStore(
|
|
25976
|
+
const tokenStore = createFileTokenStore(join32(home, ".flavor-code", "auth.json"));
|
|
25738
25977
|
const tokens2 = await tokenStore.load();
|
|
25739
25978
|
const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
|
|
25740
25979
|
if (!hadCredentials) {
|
|
@@ -25791,6 +26030,22 @@ ${renderMemoryDocument(entries)}`;
|
|
|
25791
26030
|
sleepScheduler.start();
|
|
25792
26031
|
}
|
|
25793
26032
|
const session = new FlavorSession(services);
|
|
26033
|
+
if (pluginHost.loadedPlugins.some((plugin) => plugin.name === "flavor-island")) {
|
|
26034
|
+
try {
|
|
26035
|
+
islandControlServer = await createIslandControlServer({
|
|
26036
|
+
sessionId,
|
|
26037
|
+
session,
|
|
26038
|
+
...options.islandControl?.focus === void 0 ? {} : { focus: options.islandControl.focus }
|
|
26039
|
+
});
|
|
26040
|
+
islandControlMetadata = {
|
|
26041
|
+
endpoint: islandControlServer.endpoint,
|
|
26042
|
+
token: islandControlServer.token,
|
|
26043
|
+
capabilities: islandControlServer.capabilities
|
|
26044
|
+
};
|
|
26045
|
+
} catch (error) {
|
|
26046
|
+
diagnostics.push(`Flavor Island control channel unavailable: ${message(error)}`);
|
|
26047
|
+
}
|
|
26048
|
+
}
|
|
25794
26049
|
collaborationSession = session;
|
|
25795
26050
|
pumpCollaborationEvents();
|
|
25796
26051
|
const authorization = {
|
|
@@ -25826,6 +26081,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
25826
26081
|
if (persistTail !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "persist-tail", persistTail);
|
|
25827
26082
|
if (ideSessionId !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "ide-end-session", ide.endSession(ideSessionId));
|
|
25828
26083
|
if (executionEnvironment !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "execution-environment", executionEnvironment.dispose());
|
|
26084
|
+
if (islandControlServer !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "island-control", islandControlServer.close());
|
|
25829
26085
|
terminals.dispose();
|
|
25830
26086
|
await boundedStep(stepTimeoutMs, diagnostics, "jobs", jobs.dispose());
|
|
25831
26087
|
auditLogger.close();
|
|
@@ -25839,6 +26095,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
25839
26095
|
collaborationEventsOpen = false;
|
|
25840
26096
|
mcpDiscarded = true;
|
|
25841
26097
|
memoryReviews.dispose();
|
|
26098
|
+
await islandControlServer?.close().catch(() => void 0);
|
|
25842
26099
|
try {
|
|
25843
26100
|
const stepTimeoutMs = options.shutdownStepTimeoutMs ?? DEFAULT_SHUTDOWN_STEP_TIMEOUT_MS;
|
|
25844
26101
|
if (ideSessionId !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "ide-end-session", ide.endSession(ideSessionId));
|
|
@@ -25890,9 +26147,10 @@ function formatMcpReconnect(server) {
|
|
|
25890
26147
|
if (server.status === "disabled") return `MCP server "${server.name}" is disabled. Enable it before reconnecting.`;
|
|
25891
26148
|
return `Failed to reconnect MCP server "${server.name}": ${server.error ?? "unknown error"}`;
|
|
25892
26149
|
}
|
|
25893
|
-
async function* runLoopSession(orchestrator, goal, signal) {
|
|
26150
|
+
async function* runLoopSession(orchestrator, hooks, goal, signal) {
|
|
25894
26151
|
let totalInputTokens = 0;
|
|
25895
26152
|
let totalOutputTokens = 0;
|
|
26153
|
+
let latestVerification;
|
|
25896
26154
|
for await (const event of orchestrator.run({ goal, signal })) {
|
|
25897
26155
|
if (event.type === "worker-event") {
|
|
25898
26156
|
if (event.event.type === "usage") {
|
|
@@ -25906,8 +26164,19 @@ async function* runLoopSession(orchestrator, goal, signal) {
|
|
|
25906
26164
|
} else if (event.event.type !== "done") yield event.event;
|
|
25907
26165
|
continue;
|
|
25908
26166
|
}
|
|
26167
|
+
if (event.type === "loop-verification") latestVerification = event.evidence;
|
|
25909
26168
|
yield loopProgressEvent(event);
|
|
25910
26169
|
if (event.type === "loop-terminal") {
|
|
26170
|
+
await hooks.emit({
|
|
26171
|
+
version: 1,
|
|
26172
|
+
type: "LoopEnd",
|
|
26173
|
+
payload: {
|
|
26174
|
+
loopId: event.loopId,
|
|
26175
|
+
outcome: event.status,
|
|
26176
|
+
reason: event.reason,
|
|
26177
|
+
verification: latestVerification ?? null
|
|
26178
|
+
}
|
|
26179
|
+
}).catch(() => void 0);
|
|
25911
26180
|
yield { type: "done", usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } };
|
|
25912
26181
|
}
|
|
25913
26182
|
}
|
|
@@ -25964,7 +26233,7 @@ function terminalProgressState(status) {
|
|
|
25964
26233
|
return "failed";
|
|
25965
26234
|
}
|
|
25966
26235
|
async function workspaceFingerprint(workspace) {
|
|
25967
|
-
const hash =
|
|
26236
|
+
const hash = createHash21("sha256");
|
|
25968
26237
|
const diff = await execFileNoThrow(
|
|
25969
26238
|
"git",
|
|
25970
26239
|
["-C", workspace, "diff", "--no-ext-diff", "--binary", "HEAD"],
|
|
@@ -26199,7 +26468,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
26199
26468
|
const configured = { ...providers };
|
|
26200
26469
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
26201
26470
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
26202
|
-
const oauthTokenStore = createFileTokenStore(
|
|
26471
|
+
const oauthTokenStore = createFileTokenStore(join32(home, ".flavor-code", "auth.json"));
|
|
26203
26472
|
const registered = [];
|
|
26204
26473
|
let effectiveLlm;
|
|
26205
26474
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -26618,21 +26887,21 @@ async function* runGoalSession(orchestrator, goal, signal) {
|
|
|
26618
26887
|
}
|
|
26619
26888
|
function memoryTranscriptHash(messages) {
|
|
26620
26889
|
const visible = messages.filter((item) => item.role === "user" || item.role === "assistant").map((item) => `${item.role}\0${modelContentText(item.content).trim()}`).join("\n");
|
|
26621
|
-
return
|
|
26890
|
+
return createHash21("sha256").update(visible, "utf8").digest("hex");
|
|
26622
26891
|
}
|
|
26623
26892
|
function createMemoryTaskId() {
|
|
26624
|
-
return `memory-${
|
|
26893
|
+
return `memory-${randomUUID25()}`;
|
|
26625
26894
|
}
|
|
26626
26895
|
|
|
26627
26896
|
// src/desktop/runtime-controller.ts
|
|
26628
|
-
import { createHash as
|
|
26897
|
+
import { createHash as createHash27 } from "crypto";
|
|
26629
26898
|
import { mkdir as mkdir27, readFile as readFile35, readdir as readdir12, rm as rm15, stat as stat14, writeFile as writeFile17 } from "fs/promises";
|
|
26630
26899
|
import { homedir as homedir4 } from "os";
|
|
26631
|
-
import { basename as basename9, join as
|
|
26900
|
+
import { basename as basename9, join as join42, resolve as resolve35 } from "path";
|
|
26632
26901
|
|
|
26633
26902
|
// src/d2c/workflow.ts
|
|
26634
|
-
import { createHash as
|
|
26635
|
-
import { join as
|
|
26903
|
+
import { createHash as createHash22 } from "crypto";
|
|
26904
|
+
import { join as join33 } from "path";
|
|
26636
26905
|
import { z as z42 } from "zod";
|
|
26637
26906
|
|
|
26638
26907
|
// src/d2c/workflow-shared.ts
|
|
@@ -26746,7 +27015,7 @@ function pageId(report) {
|
|
|
26746
27015
|
return report.page?.id ?? "index";
|
|
26747
27016
|
}
|
|
26748
27017
|
function issueSignature(issue, kind) {
|
|
26749
|
-
return
|
|
27018
|
+
return createHash22("sha256").update(JSON.stringify({ kind, ...issue })).digest("hex");
|
|
26750
27019
|
}
|
|
26751
27020
|
function reviewsFor(report, previous) {
|
|
26752
27021
|
const prior = new Map(previous?.reviews.map((item) => [`${item.pageId}:${item.fingerprint}`, item]) ?? []);
|
|
@@ -26778,7 +27047,7 @@ function reviewsFor(report, previous) {
|
|
|
26778
27047
|
function implementationDirectory(report) {
|
|
26779
27048
|
const source = report.implementation.source;
|
|
26780
27049
|
if (!/^https?:\/\//i.test(source)) return source;
|
|
26781
|
-
return
|
|
27050
|
+
return join33("src", "d2c-output", report.task);
|
|
26782
27051
|
}
|
|
26783
27052
|
function createWorkflow(report, framework) {
|
|
26784
27053
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -26902,7 +27171,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
|
|
|
26902
27171
|
};
|
|
26903
27172
|
}
|
|
26904
27173
|
function workflowPath(workspace, task) {
|
|
26905
|
-
return
|
|
27174
|
+
return join33(taskDir(workspace, task), "workflow.json");
|
|
26906
27175
|
}
|
|
26907
27176
|
async function readWorkflow(workspace, task) {
|
|
26908
27177
|
return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
|
|
@@ -27156,7 +27425,7 @@ function confirmApiMapping(mapping, operationKey, operations) {
|
|
|
27156
27425
|
|
|
27157
27426
|
// src/d2c/modules.ts
|
|
27158
27427
|
import { readFile as readFile28 } from "fs/promises";
|
|
27159
|
-
import { isAbsolute as isAbsolute15, join as
|
|
27428
|
+
import { isAbsolute as isAbsolute15, join as join34, normalize as normalize2, posix, sep as sep24, win32 as win322 } from "path";
|
|
27160
27429
|
import { z as z43 } from "zod";
|
|
27161
27430
|
var ModuleSchema = z43.object({
|
|
27162
27431
|
id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
@@ -27175,13 +27444,13 @@ function safeSource(path) {
|
|
|
27175
27444
|
}
|
|
27176
27445
|
function d2cOutputDirectory(workspace, task) {
|
|
27177
27446
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
|
|
27178
|
-
return
|
|
27447
|
+
return join34(workspace, "src", "d2c-output", task);
|
|
27179
27448
|
}
|
|
27180
27449
|
async function readD2cModules(workspace, task) {
|
|
27181
27450
|
const project = d2cOutputDirectory(workspace, task);
|
|
27182
27451
|
let raw;
|
|
27183
27452
|
try {
|
|
27184
|
-
raw = await readFile28(
|
|
27453
|
+
raw = await readFile28(join34(project, "d2c.modules.json"), "utf8");
|
|
27185
27454
|
} catch {
|
|
27186
27455
|
return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
|
|
27187
27456
|
}
|
|
@@ -27202,7 +27471,7 @@ async function readD2cModules(workspace, task) {
|
|
|
27202
27471
|
|
|
27203
27472
|
// src/d2c/integration.ts
|
|
27204
27473
|
import { mkdir as mkdir24, readFile as readFile29, writeFile as writeFile13 } from "fs/promises";
|
|
27205
|
-
import { join as
|
|
27474
|
+
import { join as join35 } from "path";
|
|
27206
27475
|
function record2(value) {
|
|
27207
27476
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
27208
27477
|
}
|
|
@@ -27338,7 +27607,7 @@ async def reset_acceptance(request: Request):
|
|
|
27338
27607
|
}
|
|
27339
27608
|
async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
|
|
27340
27609
|
if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
|
|
27341
|
-
const packagePath =
|
|
27610
|
+
const packagePath = join35(projectDir, "package.json");
|
|
27342
27611
|
let pkg;
|
|
27343
27612
|
try {
|
|
27344
27613
|
pkg = JSON.parse(await readFile29(packagePath, "utf8"));
|
|
@@ -27348,9 +27617,9 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
27348
27617
|
const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
|
|
27349
27618
|
const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
|
|
27350
27619
|
const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
|
|
27351
|
-
const apiDir =
|
|
27352
|
-
const mockDir =
|
|
27353
|
-
const serverDir =
|
|
27620
|
+
const apiDir = join35(projectDir, "src", "api");
|
|
27621
|
+
const mockDir = join35(projectDir, "mock");
|
|
27622
|
+
const serverDir = join35(projectDir, "server");
|
|
27354
27623
|
await Promise.all([
|
|
27355
27624
|
mkdir24(apiDir, { recursive: true }),
|
|
27356
27625
|
...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
|
|
@@ -27360,23 +27629,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
27360
27629
|
if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
|
|
27361
27630
|
else files.push("mock/server.mjs");
|
|
27362
27631
|
await Promise.all([
|
|
27363
|
-
writeFile13(
|
|
27632
|
+
writeFile13(join35(apiDir, "http.js"), `import axios from "axios";
|
|
27364
27633
|
|
|
27365
27634
|
export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
|
|
27366
27635
|
|
|
27367
27636
|
http.interceptors.response.use((response) => response, (error) => Promise.reject({ name: "ApiError", status: error.response?.status, message: error.response?.data?.message || error.message, cause: error }));
|
|
27368
27637
|
`),
|
|
27369
|
-
writeFile13(
|
|
27370
|
-
writeFile13(
|
|
27638
|
+
writeFile13(join35(apiDir, "d2c-api.js"), apiModule(selected)),
|
|
27639
|
+
writeFile13(join35(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
|
|
27371
27640
|
`),
|
|
27372
|
-
...options.pythonServer ? [] : [writeFile13(
|
|
27641
|
+
...options.pythonServer ? [] : [writeFile13(join35(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
|
|
27373
27642
|
writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
|
|
27374
27643
|
`),
|
|
27375
27644
|
...options.pythonServer ? [
|
|
27376
|
-
writeFile13(
|
|
27377
|
-
writeFile13(
|
|
27378
|
-
writeFile13(
|
|
27379
|
-
writeFile13(
|
|
27645
|
+
writeFile13(join35(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
|
|
27646
|
+
writeFile13(join35(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
|
|
27647
|
+
writeFile13(join35(serverDir, "README.md"), "# Python API service\n\nThis is the real integration service, not a mock. Development defaults to SQLite through `DATABASE_URL=sqlite:///./data/app.db`. Set the same environment variable to a SQLAlchemy MySQL or PostgreSQL URL for deployment; keep schema changes in migrations instead of database-specific SQL. Flavor Code starts the service automatically for acceptance.\n"),
|
|
27648
|
+
writeFile13(join35(serverDir, "flavor-runtime.json"), `${JSON.stringify({
|
|
27380
27649
|
schema: 1,
|
|
27381
27650
|
kind: "python-fastapi",
|
|
27382
27651
|
cwd: "server",
|
|
@@ -27396,8 +27665,8 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
|
|
|
27396
27665
|
// src/d2c/mock-runner.ts
|
|
27397
27666
|
import { spawn as spawn8 } from "child_process";
|
|
27398
27667
|
import { access as access3, readFile as readFile30 } from "fs/promises";
|
|
27399
|
-
import { createServer as
|
|
27400
|
-
import { join as
|
|
27668
|
+
import { createServer as createServer5 } from "net";
|
|
27669
|
+
import { join as join36 } from "path";
|
|
27401
27670
|
async function exists3(path) {
|
|
27402
27671
|
try {
|
|
27403
27672
|
await access3(path);
|
|
@@ -27408,7 +27677,7 @@ async function exists3(path) {
|
|
|
27408
27677
|
}
|
|
27409
27678
|
async function port() {
|
|
27410
27679
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
27411
|
-
const server =
|
|
27680
|
+
const server = createServer5();
|
|
27412
27681
|
server.once("error", rejectPromise);
|
|
27413
27682
|
server.listen(0, "127.0.0.1", () => {
|
|
27414
27683
|
const address = server.address();
|
|
@@ -27505,7 +27774,7 @@ ${output.trim()}` : ""}`);
|
|
|
27505
27774
|
}
|
|
27506
27775
|
}
|
|
27507
27776
|
async function runPythonBackend(projectDir, options) {
|
|
27508
|
-
const serverDir =
|
|
27777
|
+
const serverDir = join36(projectDir, "server");
|
|
27509
27778
|
if (options.installDependencies !== false) await installPython(serverDir, options.signal);
|
|
27510
27779
|
const selectedPort = await port();
|
|
27511
27780
|
const url = `http://127.0.0.1:${selectedPort}`;
|
|
@@ -27551,7 +27820,7 @@ ${managed.output().slice(-4e3)}`);
|
|
|
27551
27820
|
};
|
|
27552
27821
|
}
|
|
27553
27822
|
async function runD2cMockServer(projectDir, options = {}) {
|
|
27554
|
-
const runtimePath =
|
|
27823
|
+
const runtimePath = join36(projectDir, "server", "flavor-runtime.json");
|
|
27555
27824
|
if (await exists3(runtimePath)) {
|
|
27556
27825
|
let runtime;
|
|
27557
27826
|
try {
|
|
@@ -27562,9 +27831,9 @@ async function runD2cMockServer(projectDir, options = {}) {
|
|
|
27562
27831
|
if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
|
|
27563
27832
|
return runPythonBackend(projectDir, options);
|
|
27564
27833
|
}
|
|
27565
|
-
const serverPath =
|
|
27834
|
+
const serverPath = join36(projectDir, "mock", "server.mjs");
|
|
27566
27835
|
if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
|
|
27567
|
-
if (options.installDependencies === false && !await exists3(
|
|
27836
|
+
if (options.installDependencies === false && !await exists3(join36(projectDir, "node_modules", "express", "package.json"))) {
|
|
27568
27837
|
throw new Error("D2C mock server dependencies are not installed");
|
|
27569
27838
|
}
|
|
27570
27839
|
if (options.installDependencies !== false) await install(projectDir, options.signal);
|
|
@@ -27605,16 +27874,16 @@ ${managed.output().slice(-4e3)}`);
|
|
|
27605
27874
|
}
|
|
27606
27875
|
|
|
27607
27876
|
// src/d2c/product.ts
|
|
27608
|
-
import { createHash as
|
|
27609
|
-
import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile32, rename as rename13, stat as stat12, unlink as
|
|
27610
|
-
import { join as
|
|
27877
|
+
import { createHash as createHash24, randomUUID as randomUUID27 } from "crypto";
|
|
27878
|
+
import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile32, rename as rename13, stat as stat12, unlink as unlink7, writeFile as writeFile15 } from "fs/promises";
|
|
27879
|
+
import { join as join37 } from "path";
|
|
27611
27880
|
import { z as z44 } from "zod";
|
|
27612
27881
|
|
|
27613
27882
|
// src/e2e/prd-governance.ts
|
|
27614
|
-
import { createHash as
|
|
27883
|
+
import { createHash as createHash23, randomUUID as randomUUID26 } from "crypto";
|
|
27615
27884
|
import { readFile as readFile31, rename as rename12, rm as rm13, writeFile as writeFile14 } from "fs/promises";
|
|
27616
27885
|
function hashPrd(markdown) {
|
|
27617
|
-
return
|
|
27886
|
+
return createHash23("sha256").update(markdown, "utf8").digest("hex");
|
|
27618
27887
|
}
|
|
27619
27888
|
function parsePrdSections(markdown) {
|
|
27620
27889
|
const headings = [...markdown.matchAll(/^(#{2,6})[ \t]+(.+?)[ \t]*$/gm)];
|
|
@@ -27711,7 +27980,7 @@ ${body}
|
|
|
27711
27980
|
|
|
27712
27981
|
`;
|
|
27713
27982
|
const markdown = `${current.slice(0, section.contentStart)}${replacement}${current.slice(section.contentEnd)}`;
|
|
27714
|
-
const temporary = `${path}.${process.pid}.${
|
|
27983
|
+
const temporary = `${path}.${process.pid}.${randomUUID26()}.tmp`;
|
|
27715
27984
|
try {
|
|
27716
27985
|
await writeFile14(temporary, markdown, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
27717
27986
|
await rename12(temporary, path);
|
|
@@ -27725,7 +27994,7 @@ ${body}
|
|
|
27725
27994
|
var RelativePrdPath = "product/prd.md";
|
|
27726
27995
|
var RelativePrototypePath = "product/prototype/index.html";
|
|
27727
27996
|
var RelativeInteractionPath = "product/prototype/interaction-manifest.json";
|
|
27728
|
-
var defaultPlanFileOperations = { rename: rename13, copyFile: copyFile2, unlink:
|
|
27997
|
+
var defaultPlanFileOperations = { rename: rename13, copyFile: copyFile2, unlink: unlink7 };
|
|
27729
27998
|
var planWriteQueues = /* @__PURE__ */ new Map();
|
|
27730
27999
|
var PlanSchema2 = z44.object({
|
|
27731
28000
|
schema: z44.literal(1),
|
|
@@ -27762,13 +28031,13 @@ var PlanSchema2 = z44.object({
|
|
|
27762
28031
|
}).strict();
|
|
27763
28032
|
function d2cProductDirectory(workspace, task) {
|
|
27764
28033
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
|
|
27765
|
-
return
|
|
28034
|
+
return join37(taskDir(workspace, task), "product");
|
|
27766
28035
|
}
|
|
27767
28036
|
function d2cProductPrototypeDirectory(workspace, task) {
|
|
27768
|
-
return
|
|
28037
|
+
return join37(d2cProductDirectory(workspace, task), "prototype");
|
|
27769
28038
|
}
|
|
27770
28039
|
function planPath(workspace, task) {
|
|
27771
|
-
return
|
|
28040
|
+
return join37(taskDir(workspace, task), "product-plan.json");
|
|
27772
28041
|
}
|
|
27773
28042
|
function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
|
|
27774
28043
|
const frontendCandidates = [
|
|
@@ -27864,7 +28133,7 @@ async function fileHash(path) {
|
|
|
27864
28133
|
if (error.code === "ENOENT") return void 0;
|
|
27865
28134
|
throw error;
|
|
27866
28135
|
});
|
|
27867
|
-
return buffer === void 0 ? void 0 :
|
|
28136
|
+
return buffer === void 0 ? void 0 : createHash24("sha256").update(buffer).digest("hex");
|
|
27868
28137
|
}
|
|
27869
28138
|
async function validateAndNormalizePrototypeManifest(path) {
|
|
27870
28139
|
const raw = await readFile32(path, "utf8").catch((error) => {
|
|
@@ -27911,7 +28180,7 @@ async function writeD2cProductPlan(workspace, plan, operations = defaultPlanFile
|
|
|
27911
28180
|
const target = planPath(workspace, parsed.task);
|
|
27912
28181
|
await serializePlanWrite(target, async () => {
|
|
27913
28182
|
await mkdir25(taskDir(workspace, parsed.task), { recursive: true });
|
|
27914
|
-
const temporary = `${target}.${process.pid}.${
|
|
28183
|
+
const temporary = `${target}.${process.pid}.${randomUUID27()}.tmp`;
|
|
27915
28184
|
await writeFile15(temporary, `${JSON.stringify(parsed, null, 2)}
|
|
27916
28185
|
`, { encoding: "utf8", mode: 384 });
|
|
27917
28186
|
await replaceD2cProductPlanFile(temporary, target, operations);
|
|
@@ -27951,16 +28220,16 @@ async function readD2cProductPlan(workspace, task) {
|
|
|
27951
28220
|
}
|
|
27952
28221
|
async function discoverD2cProductArtifacts(workspace, plan) {
|
|
27953
28222
|
const product = d2cProductDirectory(workspace, plan.task);
|
|
27954
|
-
const prdUpdatedAt = await fileTimestamp(
|
|
27955
|
-
const prdHash = await fileHash(
|
|
27956
|
-
const prototypeUpdatedAt = await fileTimestamp(
|
|
27957
|
-
const prototypeHash = await fileHash(
|
|
27958
|
-
const expectedManifest =
|
|
28223
|
+
const prdUpdatedAt = await fileTimestamp(join37(product, "prd.md"));
|
|
28224
|
+
const prdHash = await fileHash(join37(product, "prd.md"));
|
|
28225
|
+
const prototypeUpdatedAt = await fileTimestamp(join37(product, "prototype", "index.html"));
|
|
28226
|
+
const prototypeHash = await fileHash(join37(product, "prototype", "index.html"));
|
|
28227
|
+
const expectedManifest = join37(product, "prototype", "interaction-manifest.json");
|
|
27959
28228
|
let manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
27960
28229
|
if (manifestUpdatedAt === void 0) {
|
|
27961
|
-
const misplacedManifest =
|
|
28230
|
+
const misplacedManifest = join37(product, "interaction-manifest.json");
|
|
27962
28231
|
if (await fileTimestamp(misplacedManifest) !== void 0) {
|
|
27963
|
-
await mkdir25(
|
|
28232
|
+
await mkdir25(join37(product, "prototype"), { recursive: true });
|
|
27964
28233
|
await copyFile2(misplacedManifest, expectedManifest);
|
|
27965
28234
|
manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
27966
28235
|
}
|
|
@@ -27993,8 +28262,8 @@ async function readD2cProductPlanView(workspace, task) {
|
|
|
27993
28262
|
if (stored === void 0) return void 0;
|
|
27994
28263
|
const plan = await discoverD2cProductArtifacts(workspace, stored);
|
|
27995
28264
|
if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
|
|
27996
|
-
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(
|
|
27997
|
-
const manifestPath =
|
|
28265
|
+
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(join37(taskDir(workspace, task), plan.prd.path), "utf8");
|
|
28266
|
+
const manifestPath = join37(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
|
|
27998
28267
|
const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
|
|
27999
28268
|
return {
|
|
28000
28269
|
plan,
|
|
@@ -28083,11 +28352,11 @@ ${prdMarkdown.slice(0, 2e4)}`
|
|
|
28083
28352
|
}
|
|
28084
28353
|
|
|
28085
28354
|
// src/e2e/acceptance-baseline.ts
|
|
28086
|
-
import { createHash as
|
|
28355
|
+
import { createHash as createHash25 } from "crypto";
|
|
28087
28356
|
import { readFile as readFile33 } from "fs/promises";
|
|
28088
28357
|
async function fileArtifact(path) {
|
|
28089
28358
|
const content = await readFile33(path);
|
|
28090
|
-
return { path, hash:
|
|
28359
|
+
return { path, hash: createHash25("sha256").update(content).digest("hex"), bytes: content.byteLength };
|
|
28091
28360
|
}
|
|
28092
28361
|
async function verifyAcceptanceArtifact(path, expected, label) {
|
|
28093
28362
|
const current = await fileArtifact(path);
|
|
@@ -28152,8 +28421,8 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
|
|
|
28152
28421
|
}
|
|
28153
28422
|
|
|
28154
28423
|
// src/e2e/delivery-run.ts
|
|
28155
|
-
import { createHash as
|
|
28156
|
-
import { join as
|
|
28424
|
+
import { createHash as createHash26 } from "crypto";
|
|
28425
|
+
import { join as join38 } from "path";
|
|
28157
28426
|
import { z as z45 } from "zod";
|
|
28158
28427
|
var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
|
|
28159
28428
|
var ArtifactSchema = z45.object({
|
|
@@ -28191,7 +28460,7 @@ var dependencies = {
|
|
|
28191
28460
|
};
|
|
28192
28461
|
function artifactRef(path, content, now = /* @__PURE__ */ new Date()) {
|
|
28193
28462
|
const bytes = typeof content === "string" ? Buffer.from(content, "utf8") : Buffer.from(content);
|
|
28194
|
-
return { path, hash:
|
|
28463
|
+
return { path, hash: createHash26("sha256").update(bytes).digest("hex"), bytes: bytes.byteLength, createdAt: now.toISOString() };
|
|
28195
28464
|
}
|
|
28196
28465
|
function emptyNode(id) {
|
|
28197
28466
|
return { id, status: "pending", attempt: 0, inputs: [], outputs: [] };
|
|
@@ -28270,7 +28539,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
|
|
|
28270
28539
|
return { ...run, nodes, updatedAt: timestamp2 };
|
|
28271
28540
|
}
|
|
28272
28541
|
function deliveryRunPath(workspace, task) {
|
|
28273
|
-
return
|
|
28542
|
+
return join38(taskDir(workspace, task), "delivery-run.json");
|
|
28274
28543
|
}
|
|
28275
28544
|
function decodeDeliveryRun(raw) {
|
|
28276
28545
|
return DeliveryRunSchema.parse(JSON.parse(raw));
|
|
@@ -28313,7 +28582,7 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
|
|
|
28313
28582
|
// src/skills/manager.ts
|
|
28314
28583
|
import { lstat as lstat13, mkdir as mkdir26, readFile as readFile34, readdir as readdir11, realpath as realpath13, rm as rm14, writeFile as writeFile16 } from "fs/promises";
|
|
28315
28584
|
import { homedir as homedir3 } from "os";
|
|
28316
|
-
import { isAbsolute as isAbsolute16, join as
|
|
28585
|
+
import { isAbsolute as isAbsolute16, join as join39, relative as relative24, resolve as resolve33, sep as sep25 } from "path";
|
|
28317
28586
|
import { stringify } from "yaml";
|
|
28318
28587
|
var SkillManager = class {
|
|
28319
28588
|
#workspace;
|
|
@@ -28336,7 +28605,7 @@ var SkillManager = class {
|
|
|
28336
28605
|
async create(draft) {
|
|
28337
28606
|
const value = parseDraft(draft);
|
|
28338
28607
|
const projectRoot = this.#projectRoot();
|
|
28339
|
-
const directory =
|
|
28608
|
+
const directory = join39(projectRoot, value.name);
|
|
28340
28609
|
await mkdir26(projectRoot, { recursive: true, mode: 448 });
|
|
28341
28610
|
try {
|
|
28342
28611
|
await mkdir26(directory, { mode: 448 });
|
|
@@ -28345,7 +28614,7 @@ var SkillManager = class {
|
|
|
28345
28614
|
throw error;
|
|
28346
28615
|
}
|
|
28347
28616
|
try {
|
|
28348
|
-
await writeFile16(
|
|
28617
|
+
await writeFile16(join39(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
28349
28618
|
await this.#persistEnabled(value.name, true);
|
|
28350
28619
|
return this.get(value.name);
|
|
28351
28620
|
} catch (error) {
|
|
@@ -28363,7 +28632,7 @@ var SkillManager = class {
|
|
|
28363
28632
|
throw error;
|
|
28364
28633
|
});
|
|
28365
28634
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
|
|
28366
|
-
const skillFile =
|
|
28635
|
+
const skillFile = join39(directory, "SKILL.md");
|
|
28367
28636
|
const fileInfo = await lstat13(skillFile);
|
|
28368
28637
|
if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
|
|
28369
28638
|
await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
|
|
@@ -28395,12 +28664,12 @@ var SkillManager = class {
|
|
|
28395
28664
|
async #registry() {
|
|
28396
28665
|
const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
|
|
28397
28666
|
const [globalPluginRoots, projectPluginRoots] = await Promise.all([
|
|
28398
|
-
discoverPluginSkillRoots(
|
|
28399
|
-
discoverPluginSkillRoots(
|
|
28667
|
+
discoverPluginSkillRoots(join39(this.#home, ".flavor-code", "plugins")),
|
|
28668
|
+
discoverPluginSkillRoots(join39(this.#workspace, ".flavor", "plugins"))
|
|
28400
28669
|
]);
|
|
28401
28670
|
return {
|
|
28402
28671
|
registry: new SkillRegistry({
|
|
28403
|
-
globalRoots: [
|
|
28672
|
+
globalRoots: [join39(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
|
|
28404
28673
|
projectRoots: [this.#projectRoot(), ...projectPluginRoots]
|
|
28405
28674
|
}),
|
|
28406
28675
|
disabled: new Set(loaded.config.skills.disabled)
|
|
@@ -28416,10 +28685,10 @@ var SkillManager = class {
|
|
|
28416
28685
|
};
|
|
28417
28686
|
}
|
|
28418
28687
|
#projectRoot() {
|
|
28419
|
-
return
|
|
28688
|
+
return join39(this.#workspace, ".flavor", "skills");
|
|
28420
28689
|
}
|
|
28421
28690
|
#projectSkillDirectory(name) {
|
|
28422
|
-
return
|
|
28691
|
+
return join39(this.#projectRoot(), name);
|
|
28423
28692
|
}
|
|
28424
28693
|
};
|
|
28425
28694
|
function parseName(value) {
|
|
@@ -28460,9 +28729,9 @@ async function discoverPluginSkillRoots(directory) {
|
|
|
28460
28729
|
const roots = [];
|
|
28461
28730
|
for (const entry2 of entries) {
|
|
28462
28731
|
if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
|
|
28463
|
-
const pluginRoot = await realpath13(
|
|
28732
|
+
const pluginRoot = await realpath13(join39(directory, entry2.name));
|
|
28464
28733
|
try {
|
|
28465
|
-
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(
|
|
28734
|
+
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(join39(pluginRoot, "flavor-plugin.json"), "utf8")));
|
|
28466
28735
|
for (const contribution of manifest.contributes.skillRoots) {
|
|
28467
28736
|
const candidate = resolve33(pluginRoot, contribution.path);
|
|
28468
28737
|
const info = await lstat13(candidate);
|
|
@@ -28537,11 +28806,11 @@ async function createProjectMemoryManager(options) {
|
|
|
28537
28806
|
}
|
|
28538
28807
|
|
|
28539
28808
|
// src/mcp/config-manager.ts
|
|
28540
|
-
import { join as
|
|
28809
|
+
import { join as join40 } from "path";
|
|
28541
28810
|
var ProjectMcpConfigManager = class {
|
|
28542
28811
|
path;
|
|
28543
28812
|
constructor(workspace) {
|
|
28544
|
-
this.path =
|
|
28813
|
+
this.path = join40(workspace, ".flavor", "flavor.json");
|
|
28545
28814
|
}
|
|
28546
28815
|
async list() {
|
|
28547
28816
|
const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
|
|
@@ -28638,7 +28907,7 @@ function isRecord7(value) {
|
|
|
28638
28907
|
}
|
|
28639
28908
|
|
|
28640
28909
|
// src/desktop/model-config.ts
|
|
28641
|
-
import { join as
|
|
28910
|
+
import { join as join41 } from "path";
|
|
28642
28911
|
var DEFAULT_DESKTOP_MODELS = [
|
|
28643
28912
|
{
|
|
28644
28913
|
id: "anthropic:deepseek-v4-pro",
|
|
@@ -28659,7 +28928,7 @@ var DEFAULT_DESKTOP_MODELS = [
|
|
|
28659
28928
|
];
|
|
28660
28929
|
async function loadDesktopModels(workspace, home) {
|
|
28661
28930
|
try {
|
|
28662
|
-
const tokens2 = await createFileTokenStore(
|
|
28931
|
+
const tokens2 = await createFileTokenStore(join41(home, ".flavor-code", "auth.json")).load();
|
|
28663
28932
|
const pkceModels = [];
|
|
28664
28933
|
const knownPkce = /* @__PURE__ */ new Set();
|
|
28665
28934
|
for (const token of Object.values(tokens2)) {
|
|
@@ -28728,7 +28997,7 @@ function protocolLabel(type) {
|
|
|
28728
28997
|
// src/desktop/d2c-product-preview.ts
|
|
28729
28998
|
import { createReadStream as createReadStream2 } from "fs";
|
|
28730
28999
|
import { realpath as realpath14, stat as stat13 } from "fs/promises";
|
|
28731
|
-
import { createServer as
|
|
29000
|
+
import { createServer as createServer6 } from "http";
|
|
28732
29001
|
import { extname as extname3, isAbsolute as isAbsolute17, relative as relative25, resolve as resolve34, sep as sep26 } from "path";
|
|
28733
29002
|
var MIME_TYPES = {
|
|
28734
29003
|
".css": "text/css; charset=utf-8",
|
|
@@ -28754,7 +29023,7 @@ async function startD2cProductPreview(rootDirectory) {
|
|
|
28754
29023
|
const root = await realpath14(resolve34(rootDirectory));
|
|
28755
29024
|
const rootInfo = await stat13(root);
|
|
28756
29025
|
if (!rootInfo.isDirectory()) throw new Error("D2C product prototype directory is unavailable");
|
|
28757
|
-
const server =
|
|
29026
|
+
const server = createServer6(async (request, response2) => {
|
|
28758
29027
|
response2.setHeader("cache-control", "no-store");
|
|
28759
29028
|
response2.setHeader("x-content-type-options", "nosniff");
|
|
28760
29029
|
response2.setHeader("content-security-policy", "default-src 'self' data: blob:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'");
|
|
@@ -28804,37 +29073,37 @@ function pngDataUrl(png) {
|
|
|
28804
29073
|
}
|
|
28805
29074
|
async function detectD2cFramework(workspace, task) {
|
|
28806
29075
|
try {
|
|
28807
|
-
const pkg = JSON.parse(await readFile35(
|
|
29076
|
+
const pkg = JSON.parse(await readFile35(join42(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
|
|
28808
29077
|
return pkg.dependencies?.react === void 0 ? "vue" : "react";
|
|
28809
29078
|
} catch {
|
|
28810
29079
|
return "vue";
|
|
28811
29080
|
}
|
|
28812
29081
|
}
|
|
28813
29082
|
function integrationDirectory(workspace, task) {
|
|
28814
|
-
return
|
|
29083
|
+
return join42(workspace, ".flavor", "d2c", task, "integration");
|
|
28815
29084
|
}
|
|
28816
29085
|
var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
|
|
28817
29086
|
var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
|
|
28818
29087
|
async function d2cBackendSourceFingerprint(project) {
|
|
28819
|
-
const runtimeManifest =
|
|
29088
|
+
const runtimeManifest = join42(project, "server", "flavor-runtime.json");
|
|
28820
29089
|
const realBackend = (await stat14(runtimeManifest).catch(() => void 0))?.isFile() === true;
|
|
28821
|
-
const root = realBackend ?
|
|
29090
|
+
const root = realBackend ? join42(project, "server") : join42(project, "mock");
|
|
28822
29091
|
if ((await stat14(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
28823
29092
|
const files = [];
|
|
28824
29093
|
const visit = async (directory) => {
|
|
28825
29094
|
const entries = await readdir12(directory, { withFileTypes: true });
|
|
28826
29095
|
for (const entry2 of entries) {
|
|
28827
29096
|
if (entry2.isDirectory()) {
|
|
28828
|
-
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(
|
|
29097
|
+
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join42(directory, entry2.name));
|
|
28829
29098
|
continue;
|
|
28830
29099
|
}
|
|
28831
29100
|
if (!entry2.isFile()) continue;
|
|
28832
29101
|
const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
|
|
28833
|
-
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(
|
|
29102
|
+
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join42(directory, entry2.name));
|
|
28834
29103
|
}
|
|
28835
29104
|
};
|
|
28836
29105
|
await visit(root);
|
|
28837
|
-
const hash =
|
|
29106
|
+
const hash = createHash27("sha256");
|
|
28838
29107
|
for (const path of files.sort()) {
|
|
28839
29108
|
hash.update(path.slice(root.length));
|
|
28840
29109
|
hash.update("\0");
|
|
@@ -29380,7 +29649,7 @@ var DesktopRuntimeController = class {
|
|
|
29380
29649
|
if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
|
|
29381
29650
|
throw new Error("Complete visual review before importing OpenAPI");
|
|
29382
29651
|
}
|
|
29383
|
-
const moduleManifest =
|
|
29652
|
+
const moduleManifest = join42(d2cOutputDirectory(workspace, task), "d2c.modules.json");
|
|
29384
29653
|
const moduleManifestInfo = await stat14(moduleManifest).catch(() => void 0);
|
|
29385
29654
|
if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
|
|
29386
29655
|
throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
|
|
@@ -29391,10 +29660,10 @@ var DesktopRuntimeController = class {
|
|
|
29391
29660
|
const mappings = generated ? matched.map((mapping) => ({ ...mapping, status: "confirmed" })) : matched;
|
|
29392
29661
|
const dir = integrationDirectory(workspace, task);
|
|
29393
29662
|
await mkdir27(dir, { recursive: true });
|
|
29394
|
-
const hash =
|
|
29663
|
+
const hash = createHash27("sha256").update(raw).digest("hex");
|
|
29395
29664
|
await Promise.all([
|
|
29396
|
-
writeFile17(
|
|
29397
|
-
writeFile17(
|
|
29665
|
+
writeFile17(join42(dir, "swagger.json"), raw),
|
|
29666
|
+
writeFile17(join42(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
|
|
29398
29667
|
`)
|
|
29399
29668
|
]);
|
|
29400
29669
|
const next = {
|
|
@@ -29429,7 +29698,7 @@ var DesktopRuntimeController = class {
|
|
|
29429
29698
|
let workflow = await readWorkflow(workspace, task);
|
|
29430
29699
|
if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
|
|
29431
29700
|
const modules = await readD2cModules(workspace, task);
|
|
29432
|
-
const productOpenApiPath =
|
|
29701
|
+
const productOpenApiPath = join42(taskDir(workspace, task), "product", "openapi.json");
|
|
29433
29702
|
const authored = await readFile35(productOpenApiPath, "utf8").catch(() => void 0);
|
|
29434
29703
|
const generated = authored === void 0;
|
|
29435
29704
|
const prepared = await this.#storeD2cOpenApi(
|
|
@@ -29442,7 +29711,7 @@ var DesktopRuntimeController = class {
|
|
|
29442
29711
|
return prepared;
|
|
29443
29712
|
}
|
|
29444
29713
|
if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
|
|
29445
|
-
const document = JSON.parse(await readFile35(
|
|
29714
|
+
const document = JSON.parse(await readFile35(join42(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
|
|
29446
29715
|
return { workflow, document, mappings: workflow.mappings };
|
|
29447
29716
|
}
|
|
29448
29717
|
async confirmD2cMapping(task, moduleId, operationKey) {
|
|
@@ -29509,7 +29778,7 @@ var DesktopRuntimeController = class {
|
|
|
29509
29778
|
].join("\n");
|
|
29510
29779
|
const delivery = await readDeliveryRun(workspace, task);
|
|
29511
29780
|
if (delivery?.nodes.api.status === "running") {
|
|
29512
|
-
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(
|
|
29781
|
+
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(join42(project, file)))));
|
|
29513
29782
|
const completed = await updateDeliveryRun(
|
|
29514
29783
|
workspace,
|
|
29515
29784
|
task,
|
|
@@ -29536,7 +29805,7 @@ var DesktopRuntimeController = class {
|
|
|
29536
29805
|
try {
|
|
29537
29806
|
const fingerprint2 = await d2cBackendSourceFingerprint(project);
|
|
29538
29807
|
if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
|
|
29539
|
-
const environmentPath =
|
|
29808
|
+
const environmentPath = join42(project, ".env.local");
|
|
29540
29809
|
const existingEnvironment = await readFile35(environmentPath, "utf8").catch(() => "");
|
|
29541
29810
|
const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
|
|
29542
29811
|
VITE_API_BASE_URL=${running.url}
|
|
@@ -29617,7 +29886,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29617
29886
|
const readyPreview = this.#d2cPreviews.get(task);
|
|
29618
29887
|
const readyMock = this.#d2cMocks.get(task);
|
|
29619
29888
|
if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
|
|
29620
|
-
const manifestPath =
|
|
29889
|
+
const manifestPath = join42(taskDir(workspace, task), "design", "interaction-manifest.json");
|
|
29621
29890
|
const manifest = parseInteractionManifest(await readFile35(manifestPath, "utf8"));
|
|
29622
29891
|
const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
|
|
29623
29892
|
const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
|
|
@@ -29629,7 +29898,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29629
29898
|
await mkdir27(planDir, { recursive: true });
|
|
29630
29899
|
try {
|
|
29631
29900
|
const observations = await this.#observeD2cPages(manifest, readyPreview.url);
|
|
29632
|
-
const prd = await readFile35(
|
|
29901
|
+
const prd = await readFile35(join42(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
|
|
29633
29902
|
const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
|
|
29634
29903
|
const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
|
|
29635
29904
|
executableManifest = mergeInteractionManifests(manifest, plan.manifest);
|
|
@@ -29640,9 +29909,9 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29640
29909
|
plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
29641
29910
|
pageAnalyses: plan.pageAnalyses
|
|
29642
29911
|
};
|
|
29643
|
-
await writeFile17(
|
|
29912
|
+
await writeFile17(join42(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
|
|
29644
29913
|
`, { encoding: "utf8", mode: 384 });
|
|
29645
|
-
await rm15(
|
|
29914
|
+
await rm15(join42(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
|
|
29646
29915
|
} catch (cause) {
|
|
29647
29916
|
const detail = message(cause);
|
|
29648
29917
|
review = {
|
|
@@ -29650,8 +29919,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29650
29919
|
plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
29651
29920
|
warning: `\u591A\u6A21\u6001\u81EA\u4E3B\u89C4\u5212\u5931\u8D25\uFF0C\u5DF2\u7EE7\u7EED\u6267\u884C\u5DF2\u786E\u8BA4\u7684\u8BBE\u8BA1\u5951\u7EA6\uFF1A${detail}`
|
|
29652
29921
|
};
|
|
29653
|
-
await rm15(
|
|
29654
|
-
await writeFile17(
|
|
29922
|
+
await rm15(join42(planDir, "autonomous-interaction-plan.json"), { force: true });
|
|
29923
|
+
await writeFile17(join42(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
|
|
29655
29924
|
schema: 1,
|
|
29656
29925
|
stage: "planning",
|
|
29657
29926
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -29665,13 +29934,13 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29665
29934
|
let result = { ...rawResult, evidenceMode: review.mode };
|
|
29666
29935
|
if (coverage !== void 0) {
|
|
29667
29936
|
const evidence = createAcceptanceEvidence(coverage, result);
|
|
29668
|
-
await writeFile17(
|
|
29937
|
+
await writeFile17(join42(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
|
|
29669
29938
|
`, { encoding: "utf8", mode: 384 });
|
|
29670
29939
|
if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
|
|
29671
29940
|
}
|
|
29672
29941
|
const dir = integrationDirectory(workspace, task);
|
|
29673
29942
|
await mkdir27(dir, { recursive: true });
|
|
29674
|
-
await writeFile17(
|
|
29943
|
+
await writeFile17(join42(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
|
|
29675
29944
|
`);
|
|
29676
29945
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
|
|
29677
29946
|
return { workflow: next, result, review };
|
|
@@ -29684,7 +29953,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29684
29953
|
if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
|
|
29685
29954
|
const output = mock.output().trim();
|
|
29686
29955
|
try {
|
|
29687
|
-
const dir =
|
|
29956
|
+
const dir = join42(d2cOutputDirectory(workspace, task), "mock-server.log");
|
|
29688
29957
|
await writeFile17(dir, `${output}
|
|
29689
29958
|
`);
|
|
29690
29959
|
} catch {
|
|
@@ -29779,7 +30048,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29779
30048
|
if (current === void 0) throw new Error("D2C product plan is unavailable");
|
|
29780
30049
|
if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
|
|
29781
30050
|
if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
|
|
29782
|
-
const path =
|
|
30051
|
+
const path = join42(taskDir(workspace, task), "product", "prd.md");
|
|
29783
30052
|
const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
|
|
29784
30053
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
29785
30054
|
await writeD2cProductPlan(workspace, {
|
|
@@ -29799,15 +30068,15 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29799
30068
|
async #assertProductPrdLocked(workspace, task) {
|
|
29800
30069
|
const current = await readD2cProductPlanView(workspace, task);
|
|
29801
30070
|
if (current?.plan.approvedPrd === void 0) return;
|
|
29802
|
-
await assertApprovedPrd(
|
|
30071
|
+
await assertApprovedPrd(join42(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
|
|
29803
30072
|
}
|
|
29804
30073
|
async #acceptancePaths(workspace, task) {
|
|
29805
|
-
const product =
|
|
29806
|
-
const openapi =
|
|
30074
|
+
const product = join42(taskDir(workspace, task), "product");
|
|
30075
|
+
const openapi = join42(product, "openapi.json");
|
|
29807
30076
|
return {
|
|
29808
|
-
prd:
|
|
29809
|
-
prototype:
|
|
29810
|
-
interaction:
|
|
30077
|
+
prd: join42(product, "prd.md"),
|
|
30078
|
+
prototype: join42(product, "prototype", "index.html"),
|
|
30079
|
+
interaction: join42(product, "prototype", "interaction-manifest.json"),
|
|
29811
30080
|
...await stat14(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
|
|
29812
30081
|
};
|
|
29813
30082
|
}
|
|
@@ -29815,17 +30084,17 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29815
30084
|
const product = await readD2cProductPlanView(workspace, task);
|
|
29816
30085
|
if (product?.plan.approvedPrd === void 0) return void 0;
|
|
29817
30086
|
await this.#assertProductPrdLocked(workspace, task);
|
|
29818
|
-
const baseline = JSON.parse(await readFile35(
|
|
30087
|
+
const baseline = JSON.parse(await readFile35(join42(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
|
|
29819
30088
|
await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
|
|
29820
|
-
const design =
|
|
30089
|
+
const design = join42(taskDir(workspace, task), "design");
|
|
29821
30090
|
await Promise.all([
|
|
29822
|
-
verifyAcceptanceArtifact(
|
|
29823
|
-
verifyAcceptanceArtifact(
|
|
30091
|
+
verifyAcceptanceArtifact(join42(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
|
|
30092
|
+
verifyAcceptanceArtifact(join42(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
|
|
29824
30093
|
]);
|
|
29825
30094
|
const workflow = await readWorkflow(workspace, task);
|
|
29826
30095
|
if (workflow?.openapi !== void 0) {
|
|
29827
30096
|
await verifyAcceptanceArtifact(
|
|
29828
|
-
|
|
30097
|
+
join42(taskDir(workspace, task), "integration", "swagger.json"),
|
|
29829
30098
|
{ hash: workflow.openapi.hash },
|
|
29830
30099
|
"runtime OpenAPI contract"
|
|
29831
30100
|
);
|
|
@@ -29846,14 +30115,14 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29846
30115
|
throw new Error("D2C PRD is empty and cannot be accepted");
|
|
29847
30116
|
}
|
|
29848
30117
|
if (stage === "design" && accepted) {
|
|
29849
|
-
const manifest =
|
|
30118
|
+
const manifest = join42(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
|
|
29850
30119
|
const normalizedManifest = parseInteractionManifest(await readFile35(manifest, "utf8"));
|
|
29851
30120
|
if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
|
|
29852
30121
|
verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
|
|
29853
30122
|
await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
|
|
29854
30123
|
`, { encoding: "utf8", mode: 384 });
|
|
29855
30124
|
const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
|
|
29856
|
-
await writeFile17(
|
|
30125
|
+
await writeFile17(join42(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
|
|
29857
30126
|
`, { encoding: "utf8", mode: 384 });
|
|
29858
30127
|
}
|
|
29859
30128
|
const plan = await writeD2cProductPlan(
|
|
@@ -29887,8 +30156,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29887
30156
|
const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
|
|
29888
30157
|
const delivery = await readDeliveryRun(workspace, task);
|
|
29889
30158
|
if (delivery !== void 0) {
|
|
29890
|
-
const prototype = await readFile35(
|
|
29891
|
-
const interaction = await readFile35(
|
|
30159
|
+
const prototype = await readFile35(join42(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
|
|
30160
|
+
const interaction = await readFile35(join42(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
|
|
29892
30161
|
const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
|
|
29893
30162
|
artifactRef("product/prototype/index.html", prototype),
|
|
29894
30163
|
artifactRef("product/prototype/interaction-manifest.json", interaction)
|
|
@@ -29972,10 +30241,10 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29972
30241
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
|
|
29973
30242
|
const qualityContent = `${JSON.stringify(judgment, null, 2)}
|
|
29974
30243
|
`;
|
|
29975
|
-
await writeFile17(
|
|
30244
|
+
await writeFile17(join42(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
|
|
29976
30245
|
const delivery = await readDeliveryRun(workspace, task);
|
|
29977
30246
|
if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
|
|
29978
|
-
const interactionContent = await readFile35(
|
|
30247
|
+
const interactionContent = await readFile35(join42(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
|
|
29979
30248
|
const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
|
|
29980
30249
|
artifactRef("integration/interaction-results.json", interactionContent),
|
|
29981
30250
|
artifactRef("quality-judge.json", qualityContent)
|
|
@@ -30157,14 +30426,14 @@ function desktopWindowChrome(platform = process.platform) {
|
|
|
30157
30426
|
|
|
30158
30427
|
// src/utils/crash-guard.ts
|
|
30159
30428
|
import { mkdirSync as mkdirSync3, writeFileSync } from "fs";
|
|
30160
|
-
import { join as
|
|
30429
|
+
import { join as join43 } from "path";
|
|
30161
30430
|
var installed = false;
|
|
30162
30431
|
function installCrashGuard(options = {}) {
|
|
30163
30432
|
if (installed) return;
|
|
30164
30433
|
installed = true;
|
|
30165
30434
|
const crashLogDir = () => {
|
|
30166
30435
|
try {
|
|
30167
|
-
return
|
|
30436
|
+
return join43((options.workspace ?? process.cwd)(), ".flavor");
|
|
30168
30437
|
} catch {
|
|
30169
30438
|
return process.cwd();
|
|
30170
30439
|
}
|
|
@@ -30173,7 +30442,7 @@ function installCrashGuard(options = {}) {
|
|
|
30173
30442
|
try {
|
|
30174
30443
|
const dir = crashLogDir();
|
|
30175
30444
|
mkdirSync3(dir, { recursive: true });
|
|
30176
|
-
const path =
|
|
30445
|
+
const path = join43(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
|
|
30177
30446
|
writeFileSync(path, [
|
|
30178
30447
|
`kind: ${kind}`,
|
|
30179
30448
|
`version: ${process.version}`,
|
|
@@ -30364,9 +30633,9 @@ function safeGitTarget(value) {
|
|
|
30364
30633
|
}
|
|
30365
30634
|
|
|
30366
30635
|
// src/desktop/worktree-manager.ts
|
|
30367
|
-
import { randomUUID as
|
|
30636
|
+
import { randomUUID as randomUUID28 } from "crypto";
|
|
30368
30637
|
import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat15, writeFile as writeFile18 } from "fs/promises";
|
|
30369
|
-
import { basename as basename10, isAbsolute as isAbsolute19, join as
|
|
30638
|
+
import { basename as basename10, isAbsolute as isAbsolute19, join as join44, relative as relative27, resolve as resolve37, sep as sep27 } from "path";
|
|
30370
30639
|
var DesktopWorktreeManager = class {
|
|
30371
30640
|
#repository;
|
|
30372
30641
|
#storage;
|
|
@@ -30377,7 +30646,7 @@ var DesktopWorktreeManager = class {
|
|
|
30377
30646
|
async create(label) {
|
|
30378
30647
|
if (!await isGitRepository(this.#repository)) throw new Error("Task worktrees require a Git repository");
|
|
30379
30648
|
const safe = label.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 36) || "task";
|
|
30380
|
-
const suffix =
|
|
30649
|
+
const suffix = randomUUID28().slice(0, 8);
|
|
30381
30650
|
const id = `worktree-${safe}-${suffix}`;
|
|
30382
30651
|
const branch = `flavor/desktop-${safe}-${suffix}`;
|
|
30383
30652
|
const path = this.#safeStoragePath(id);
|
|
@@ -30412,7 +30681,7 @@ var DesktopWorktreeManager = class {
|
|
|
30412
30681
|
}
|
|
30413
30682
|
const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
|
|
30414
30683
|
try {
|
|
30415
|
-
const stored = validateStored(JSON.parse(await readFile37(
|
|
30684
|
+
const stored = validateStored(JSON.parse(await readFile37(join44(this.#storage, name), "utf8")), this.#repository, this.#storage);
|
|
30416
30685
|
if ((await stat15(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
30417
30686
|
const status = await git2(stored.path, ["status", "--porcelain"]);
|
|
30418
30687
|
const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
|
|
@@ -30476,7 +30745,7 @@ function validateStored(value, repository, storage) {
|
|
|
30476
30745
|
// src/desktop/workbench-service.ts
|
|
30477
30746
|
import { readFile as readFile38, readdir as readdir14, stat as stat16 } from "fs/promises";
|
|
30478
30747
|
import { homedir as homedir5 } from "os";
|
|
30479
|
-
import { join as
|
|
30748
|
+
import { join as join45, resolve as resolve38 } from "path";
|
|
30480
30749
|
var MAX_TEXT = 128e3;
|
|
30481
30750
|
var MAX_ITEMS = 200;
|
|
30482
30751
|
var DesktopWorkbenchService = class {
|
|
@@ -30509,11 +30778,11 @@ var DesktopWorkbenchService = class {
|
|
|
30509
30778
|
return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
|
|
30510
30779
|
}
|
|
30511
30780
|
async #goals() {
|
|
30512
|
-
const root =
|
|
30781
|
+
const root = join45(this.#workspace, ".flavor", "goals");
|
|
30513
30782
|
const names = await readdir14(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
|
|
30514
30783
|
const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
|
|
30515
30784
|
try {
|
|
30516
|
-
return GoalStateSchema.parse(JSON.parse(await boundedRead2(
|
|
30785
|
+
return GoalStateSchema.parse(JSON.parse(await boundedRead2(join45(root, name))));
|
|
30517
30786
|
} catch {
|
|
30518
30787
|
return void 0;
|
|
30519
30788
|
}
|
|
@@ -30523,7 +30792,7 @@ var DesktopWorkbenchService = class {
|
|
|
30523
30792
|
async #instructions() {
|
|
30524
30793
|
const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
30525
30794
|
const items = await Promise.all(names.map(async (name) => {
|
|
30526
|
-
const path =
|
|
30795
|
+
const path = join45(this.#workspace, name);
|
|
30527
30796
|
try {
|
|
30528
30797
|
return { name, path, content: redactErrorText(await boundedRead2(path)) };
|
|
30529
30798
|
} catch (error) {
|
|
@@ -30536,9 +30805,9 @@ var DesktopWorkbenchService = class {
|
|
|
30536
30805
|
async #permissionFiles() {
|
|
30537
30806
|
const candidates = [
|
|
30538
30807
|
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve38(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
|
|
30539
|
-
{ tier: "user", path:
|
|
30540
|
-
{ tier: "project", path:
|
|
30541
|
-
{ tier: "local", path:
|
|
30808
|
+
{ tier: "user", path: join45(this.#home, ".flavor-code", "permissions.json"), content: "" },
|
|
30809
|
+
{ tier: "project", path: join45(this.#workspace, ".flavor", "permissions.json"), content: "" },
|
|
30810
|
+
{ tier: "local", path: join45(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
|
|
30542
30811
|
];
|
|
30543
30812
|
const values = await Promise.all(candidates.map(async (item) => {
|
|
30544
30813
|
try {
|
|
@@ -30553,7 +30822,7 @@ var DesktopWorkbenchService = class {
|
|
|
30553
30822
|
async #audit() {
|
|
30554
30823
|
let raw;
|
|
30555
30824
|
try {
|
|
30556
|
-
raw = await boundedRead2(
|
|
30825
|
+
raw = await boundedRead2(join45(this.#workspace, ".flavor", "audit.jsonl"));
|
|
30557
30826
|
} catch (error) {
|
|
30558
30827
|
if (error.code === "ENOENT") return [];
|
|
30559
30828
|
throw error;
|
|
@@ -30568,7 +30837,7 @@ var DesktopWorkbenchService = class {
|
|
|
30568
30837
|
}).reverse();
|
|
30569
30838
|
}
|
|
30570
30839
|
async #context(sessionId) {
|
|
30571
|
-
const usage = await readJsonLines2(
|
|
30840
|
+
const usage = await readJsonLines2(join45(this.#workspace, ".flavor", "usage.jsonl"));
|
|
30572
30841
|
if (sessionId === void 0) return { visibility: [], usage };
|
|
30573
30842
|
try {
|
|
30574
30843
|
const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
|
|
@@ -30609,12 +30878,12 @@ function redact2(value, key = "") {
|
|
|
30609
30878
|
// src/desktop/astgraph-service.ts
|
|
30610
30879
|
import { stat as stat17 } from "fs/promises";
|
|
30611
30880
|
import { createRequire as nodeCreateRequire2 } from "module";
|
|
30612
|
-
import { join as
|
|
30881
|
+
import { join as join46, resolve as resolve39 } from "path";
|
|
30613
30882
|
var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
|
|
30614
30883
|
var DesktopAstGraphService = class {
|
|
30615
30884
|
#path;
|
|
30616
30885
|
constructor(workspace) {
|
|
30617
|
-
this.#path =
|
|
30886
|
+
this.#path = join46(resolve39(workspace), ".flavor", "astgraph", "index.db");
|
|
30618
30887
|
}
|
|
30619
30888
|
async status() {
|
|
30620
30889
|
if ((await stat17(this.#path).catch(() => void 0))?.isFile() !== true) {
|
|
@@ -30691,7 +30960,7 @@ var appMenu;
|
|
|
30691
30960
|
var quitting = false;
|
|
30692
30961
|
function logStartup(step, detail) {
|
|
30693
30962
|
try {
|
|
30694
|
-
const logPath =
|
|
30963
|
+
const logPath = join47(dirname24(process.execPath), "flavor-code-startup.log");
|
|
30695
30964
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
30696
30965
|
appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
|
|
30697
30966
|
`);
|
|
@@ -30716,7 +30985,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
|
|
|
30716
30985
|
};
|
|
30717
30986
|
});
|
|
30718
30987
|
var d2cJudgeClient = createD2cJudgeClient();
|
|
30719
|
-
var judgeStore = () => createD2cJudgeConfigStore(
|
|
30988
|
+
var judgeStore = () => createD2cJudgeConfigStore(join47(app.getPath("userData"), "d2c-judge.json"));
|
|
30720
30989
|
function judgeImage(png) {
|
|
30721
30990
|
const image = nativeImage.createFromBuffer(png);
|
|
30722
30991
|
const size = image.getSize();
|
|
@@ -30741,8 +31010,8 @@ function sessionMetaKey(workspace, sessionId) {
|
|
|
30741
31010
|
return `${workspace}\0${sessionId}`;
|
|
30742
31011
|
}
|
|
30743
31012
|
function worktreeManager(workspace) {
|
|
30744
|
-
const key =
|
|
30745
|
-
return new DesktopWorktreeManager({ repository: workspace, storage:
|
|
31013
|
+
const key = createHash28("sha256").update(resolve40(workspace)).digest("hex").slice(0, 20);
|
|
31014
|
+
return new DesktopWorktreeManager({ repository: workspace, storage: join47(app.getPath("userData"), "task-worktrees", key) });
|
|
30746
31015
|
}
|
|
30747
31016
|
function activeWorkingDirectory() {
|
|
30748
31017
|
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
@@ -30841,7 +31110,7 @@ function emitManagedDesktopEvent(workspace, owner2, event) {
|
|
|
30841
31110
|
}
|
|
30842
31111
|
}
|
|
30843
31112
|
function createDesktopController(workspace, owner2) {
|
|
30844
|
-
const collaborationId =
|
|
31113
|
+
const collaborationId = randomUUID29();
|
|
30845
31114
|
return new DesktopRuntimeController({
|
|
30846
31115
|
emit: (event) => emitManagedDesktopEvent(workspace, owner2, event),
|
|
30847
31116
|
runD2cInteractionTests: (manifest, baseUrl, mockUrl) => d2cEmbedded.run(manifest, baseUrl, mockUrl),
|
|
@@ -30873,12 +31142,21 @@ function createDesktopController(workspace, owner2) {
|
|
|
30873
31142
|
},
|
|
30874
31143
|
createRuntime: async (runtimeOptions) => createProductionRuntime({
|
|
30875
31144
|
...runtimeOptions,
|
|
31145
|
+
islandControl: {
|
|
31146
|
+
focus: () => {
|
|
31147
|
+
const target = mainWindow;
|
|
31148
|
+
if (target === void 0 || target.isDestroyed()) return;
|
|
31149
|
+
if (target.isMinimized()) target.restore();
|
|
31150
|
+
target.show();
|
|
31151
|
+
target.focus();
|
|
31152
|
+
}
|
|
31153
|
+
},
|
|
30876
31154
|
collaboration: {
|
|
30877
31155
|
instanceId: collaborationId,
|
|
30878
31156
|
alias: `desktop-${basename11(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
|
|
30879
31157
|
optional: true,
|
|
30880
31158
|
startBroker: (address) => {
|
|
30881
|
-
const entry2 = app.isPackaged ?
|
|
31159
|
+
const entry2 = app.isPackaged ? join47(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join47(moduleDirectory, "pals-broker-entry.js");
|
|
30882
31160
|
return spawnPalBrokerProcess({
|
|
30883
31161
|
address,
|
|
30884
31162
|
executable: process.execPath,
|
|
@@ -30900,7 +31178,7 @@ function createDesktopController(workspace, owner2) {
|
|
|
30900
31178
|
var detachedController = createDesktopController();
|
|
30901
31179
|
var controller = detachedController;
|
|
30902
31180
|
function statePath() {
|
|
30903
|
-
return
|
|
31181
|
+
return join47(app.getPath("userData"), "desktop-state.json");
|
|
30904
31182
|
}
|
|
30905
31183
|
async function loadPersistedProjects() {
|
|
30906
31184
|
try {
|
|
@@ -31073,7 +31351,7 @@ async function chooseAndOpenWorkspace() {
|
|
|
31073
31351
|
var cachedAppIconDataUrl;
|
|
31074
31352
|
function appIconDataUrl() {
|
|
31075
31353
|
if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
|
|
31076
|
-
const icon = nativeImage.createFromPath(
|
|
31354
|
+
const icon = nativeImage.createFromPath(join47(app.getAppPath(), "assets", "icon.png"));
|
|
31077
31355
|
if (icon.isEmpty()) return void 0;
|
|
31078
31356
|
cachedAppIconDataUrl = icon.toDataURL();
|
|
31079
31357
|
return cachedAppIconDataUrl;
|
|
@@ -31491,7 +31769,7 @@ function applicationMenu() {
|
|
|
31491
31769
|
]);
|
|
31492
31770
|
}
|
|
31493
31771
|
async function createWindow() {
|
|
31494
|
-
const rendererPath =
|
|
31772
|
+
const rendererPath = join47(app.getAppPath(), "dist", "desktop-renderer", "index.html");
|
|
31495
31773
|
mainWindow = new BrowserWindow2({
|
|
31496
31774
|
title: "Flavor Code",
|
|
31497
31775
|
width: 1280,
|
|
@@ -31502,14 +31780,14 @@ async function createWindow() {
|
|
|
31502
31780
|
show: false,
|
|
31503
31781
|
...desktopWindowChrome(),
|
|
31504
31782
|
webPreferences: {
|
|
31505
|
-
preload:
|
|
31783
|
+
preload: join47(moduleDirectory, "preload.cjs"),
|
|
31506
31784
|
contextIsolation: true,
|
|
31507
31785
|
nodeIntegration: false,
|
|
31508
31786
|
sandbox: false,
|
|
31509
31787
|
webSecurity: true
|
|
31510
31788
|
}
|
|
31511
31789
|
});
|
|
31512
|
-
const iconPath =
|
|
31790
|
+
const iconPath = join47(app.getAppPath(), "assets", "icon.png");
|
|
31513
31791
|
const icon = nativeImage.createFromPath(iconPath);
|
|
31514
31792
|
if (!icon.isEmpty()) {
|
|
31515
31793
|
mainWindow.setIcon(icon);
|