flavor-code 1.3.15 → 1.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/loop.d.ts +10 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/{app-DDYOKGCX.js → app-6SEGTMMC.js} +3 -2
- package/dist/chunk-CIVPKHF4.js +53 -0
- package/dist/{chunk-JFKRX2KG.js → chunk-HJOCNKPN.js} +1 -1
- package/dist/{chunk-TQXNF337.js → chunk-LMFN2QEA.js} +1 -1
- package/dist/{chunk-WB4IJXFT.js → chunk-S525EOFQ.js} +138 -12
- package/dist/cli-main.js +6 -5
- package/dist/cli.js +52 -9
- package/dist/context/manager.d.ts +7 -0
- package/dist/desktop/main.js +316 -172
- package/dist/desktop-renderer/assets/{index-BAHTmEUI.js → index-C3JpD2_B.js} +2 -2
- package/dist/desktop-renderer/assets/{interactive-terminal-Bn8G5Pig.js → interactive-terminal-f1PNniPO.js} +1 -1
- package/dist/desktop-renderer/index.html +1 -1
- package/dist/{load-LTTKVPJZ.js → load-DELQW42K.js} +1 -1
- package/dist/sdk/index.js +4 -3
- package/dist/utils/memory-restart.d.ts +33 -0
- package/package.json +3 -1
package/dist/desktop/main.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { appendFileSync } from "fs";
|
|
3
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
|
|
5
|
+
import { basename as basename11, dirname as dirname25, join as join48, 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
|
|
|
@@ -305,7 +305,7 @@ var FlavorConfigSchema = z2.object({
|
|
|
305
305
|
context: z2.object({
|
|
306
306
|
windowTokens: z2.number().int().positive().default(2e5),
|
|
307
307
|
reservedOutputTokens: z2.number().int().nonnegative().default(2e4),
|
|
308
|
-
autoCompactBufferTokens: z2.number().int().nonnegative().default(
|
|
308
|
+
autoCompactBufferTokens: z2.number().int().nonnegative().default(27e3),
|
|
309
309
|
warningBufferTokens: z2.number().int().nonnegative().default(2e4),
|
|
310
310
|
blockingBufferTokens: z2.number().int().nonnegative().default(3e3),
|
|
311
311
|
microcompactKeepRecentToolResults: z2.number().int().nonnegative().default(5),
|
|
@@ -4225,9 +4225,10 @@ ${summarizeReport(report2)}`).join("\n\n")
|
|
|
4225
4225
|
|
|
4226
4226
|
// src/production.ts
|
|
4227
4227
|
import { readFile as readFile27 } from "fs/promises";
|
|
4228
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
4228
4229
|
import { createHash as createHash21, randomUUID as randomUUID25 } from "crypto";
|
|
4229
4230
|
import { homedir as homedir2, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
4230
|
-
import { basename as basename8, join as
|
|
4231
|
+
import { basename as basename8, dirname as dirname24, join as join34, relative as relative23, resolve as resolve33, sep as sep23 } from "path";
|
|
4231
4232
|
|
|
4232
4233
|
// src/agent/subagents.ts
|
|
4233
4234
|
import { z as z10 } from "zod";
|
|
@@ -5161,7 +5162,9 @@ import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
|
|
|
5161
5162
|
var DEFAULT_COMPACTION_POLICY = {
|
|
5162
5163
|
windowTokens: 2e5,
|
|
5163
5164
|
reservedOutputTokens: 2e4,
|
|
5164
|
-
|
|
5165
|
+
// 85% of the effective window (180_000 - 27_000 = 153_000): compaction used
|
|
5166
|
+
// to trigger at ~92.8%, which let sessions ride the threshold into heap OOM.
|
|
5167
|
+
autoCompactBufferTokens: 27e3,
|
|
5165
5168
|
warningBufferTokens: 2e4,
|
|
5166
5169
|
blockingBufferTokens: 3e3,
|
|
5167
5170
|
microcompactKeepRecentToolResults: 5,
|
|
@@ -5371,6 +5374,13 @@ var ContextManager = class _ContextManager {
|
|
|
5371
5374
|
#epoch;
|
|
5372
5375
|
#visibilityLog = [];
|
|
5373
5376
|
#activeTransientSystem = /* @__PURE__ */ new Map();
|
|
5377
|
+
/**
|
|
5378
|
+
* The visibility audit log persists every transient system admission and is
|
|
5379
|
+
* serialized in full on every session save. Bound both dimensions or a long
|
|
5380
|
+
* subagent-driven run grows it into hundreds of megabytes (heap OOM).
|
|
5381
|
+
*/
|
|
5382
|
+
static VISIBILITY_LOG_MAX_ENTRIES = 1e3;
|
|
5383
|
+
static VISIBILITY_RECORD_CONTENT_LIMIT = 2048;
|
|
5374
5384
|
constructor(options) {
|
|
5375
5385
|
if (options.compactAtChars !== void 0 && options.compactAtChars <= 0) throw new Error("compactAtChars must be positive");
|
|
5376
5386
|
if (options.toolOutputChars <= 0) throw new Error("toolOutputChars must be positive");
|
|
@@ -5466,11 +5476,14 @@ var ContextManager = class _ContextManager {
|
|
|
5466
5476
|
const record3 = {
|
|
5467
5477
|
id,
|
|
5468
5478
|
role: "system",
|
|
5469
|
-
content,
|
|
5479
|
+
content: limitVisibilityContent(content),
|
|
5470
5480
|
admittedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5471
5481
|
scope: "run"
|
|
5472
5482
|
};
|
|
5473
5483
|
this.#visibilityLog.push(record3);
|
|
5484
|
+
if (this.#visibilityLog.length > _ContextManager.VISIBILITY_LOG_MAX_ENTRIES) {
|
|
5485
|
+
this.#visibilityLog = this.#visibilityLog.slice(-_ContextManager.VISIBILITY_LOG_MAX_ENTRIES);
|
|
5486
|
+
}
|
|
5474
5487
|
this.#activeTransientSystem.set(id, { role: "system", content });
|
|
5475
5488
|
return id;
|
|
5476
5489
|
}
|
|
@@ -5494,8 +5507,9 @@ var ContextManager = class _ContextManager {
|
|
|
5494
5507
|
const currentStableHash = currentStable === void 0 ? this.#epoch.stableSourceHash : hashMessages(currentStable);
|
|
5495
5508
|
if (currentStable !== void 0 && currentStableHash !== this.#epoch.stableSourceHash) {
|
|
5496
5509
|
const value = currentStable.map((message2) => message2.content).join("\n\n");
|
|
5510
|
+
const previous = this.#epoch.sources["system-baseline"] ?? this.#epoch.stableMessages.map((message2) => message2.content).join("\n\n");
|
|
5497
5511
|
if (this.#epoch.sources["system-baseline"] !== value) {
|
|
5498
|
-
updates.push(contextUpdateMessage("system-baseline", value));
|
|
5512
|
+
updates.push(contextUpdateMessage("system-baseline", deltaSourceValue(previous, value)));
|
|
5499
5513
|
this.#epoch.sources["system-baseline"] = value;
|
|
5500
5514
|
}
|
|
5501
5515
|
} else if (currentStable !== void 0 && this.#epoch.sources["system-baseline"] !== void 0) {
|
|
@@ -5506,7 +5520,7 @@ var ContextManager = class _ContextManager {
|
|
|
5506
5520
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
5507
5521
|
const value = next[key];
|
|
5508
5522
|
if (value === void 0 || value === this.#epoch.sources[key]) continue;
|
|
5509
|
-
updates.push(contextUpdateMessage(key, displaySourceValue(key, value)));
|
|
5523
|
+
updates.push(contextUpdateMessage(key, deltaSourceValue(this.#epoch.sources[key], displaySourceValue(key, value))));
|
|
5510
5524
|
this.#epoch.sources[key] = value;
|
|
5511
5525
|
}
|
|
5512
5526
|
for (const key of DYNAMIC_SOURCE_ORDER) {
|
|
@@ -5533,7 +5547,7 @@ var ContextManager = class _ContextManager {
|
|
|
5533
5547
|
const legacySummary = snapshot4.summary?.role === "system" && snapshot4.summary.content.startsWith("Conversation summary\n") ? snapshot4.summary.content.slice("Conversation summary\n".length) : void 0;
|
|
5534
5548
|
this.#compact = snapshot4.compact === void 0 ? legacySummary === void 0 ? void 0 : { summary: legacySummary, compactedAt: (/* @__PURE__ */ new Date(0)).toISOString() } : { ...snapshot4.compact };
|
|
5535
5549
|
if (snapshot4.epoch !== void 0) this.#epoch = cloneEpoch(snapshot4.epoch);
|
|
5536
|
-
this.#visibilityLog = snapshot4.visibilityLog
|
|
5550
|
+
this.#visibilityLog = boundVisibilityLog(snapshot4.visibilityLog);
|
|
5537
5551
|
this.#activeTransientSystem.clear();
|
|
5538
5552
|
this.#messages = messages.map((message2) => message2.role === "tool" ? { ...message2, content: truncateToolOutput(message2.content, this.#toolOutputChars) } : cloneMessage2(message2));
|
|
5539
5553
|
this.#lastRecordedInputTokens = void 0;
|
|
@@ -5577,6 +5591,10 @@ var ContextManager = class _ContextManager {
|
|
|
5577
5591
|
async prepareForModelCall(signal = new AbortController().signal) {
|
|
5578
5592
|
signal.throwIfAborted();
|
|
5579
5593
|
this.refreshContextSources();
|
|
5594
|
+
if (this.#dropStaleContextUpdates()) {
|
|
5595
|
+
this.#lastRecordedInputTokens = void 0;
|
|
5596
|
+
this.#estimatedTokensAtLastRecordedUsage = void 0;
|
|
5597
|
+
}
|
|
5580
5598
|
if (!this.needsCompaction()) return false;
|
|
5581
5599
|
const originalMessages = this.#messages;
|
|
5582
5600
|
const originalRecordedUsage = this.#lastRecordedInputTokens;
|
|
@@ -5676,6 +5694,26 @@ var ContextManager = class _ContextManager {
|
|
|
5676
5694
|
this.#reportCompactProgress(100);
|
|
5677
5695
|
return true;
|
|
5678
5696
|
}
|
|
5697
|
+
#dropStaleContextUpdates() {
|
|
5698
|
+
const latestBySource = /* @__PURE__ */ new Map();
|
|
5699
|
+
for (let index = this.#messages.length - 1; index >= 0; index -= 1) {
|
|
5700
|
+
const source = contextUpdateSource(this.#messages[index]);
|
|
5701
|
+
if (source === void 0 || latestBySource.has(source)) continue;
|
|
5702
|
+
latestBySource.set(source, index);
|
|
5703
|
+
}
|
|
5704
|
+
const next = [];
|
|
5705
|
+
let changed = false;
|
|
5706
|
+
this.#messages.forEach((message2, index) => {
|
|
5707
|
+
const source = contextUpdateSource(message2);
|
|
5708
|
+
if (source !== void 0 && latestBySource.get(source) !== index) {
|
|
5709
|
+
changed = true;
|
|
5710
|
+
return;
|
|
5711
|
+
}
|
|
5712
|
+
next.push(message2);
|
|
5713
|
+
});
|
|
5714
|
+
if (changed) this.#messages = next;
|
|
5715
|
+
return changed;
|
|
5716
|
+
}
|
|
5679
5717
|
#reportCompactProgress(percentage) {
|
|
5680
5718
|
const normalized = Math.max(0, Math.min(100, Math.floor(percentage / 10) * 10));
|
|
5681
5719
|
if (normalized === this.#lastCompactProgress) return;
|
|
@@ -5787,6 +5825,33 @@ function contextUpdateMessage(source, content) {
|
|
|
5787
5825
|
return { role: "system", content: `Context update [${source}]
|
|
5788
5826
|
${content}` };
|
|
5789
5827
|
}
|
|
5828
|
+
var CONTEXT_UPDATE_PREFIX = "Context update [";
|
|
5829
|
+
function contextUpdateSource(message2) {
|
|
5830
|
+
if (message2.role !== "system" || typeof message2.content !== "string") return void 0;
|
|
5831
|
+
if (!message2.content.startsWith(CONTEXT_UPDATE_PREFIX)) return void 0;
|
|
5832
|
+
const end = message2.content.indexOf("]\n", CONTEXT_UPDATE_PREFIX.length);
|
|
5833
|
+
if (end < 0) return void 0;
|
|
5834
|
+
return message2.content.slice(CONTEXT_UPDATE_PREFIX.length, end);
|
|
5835
|
+
}
|
|
5836
|
+
function deltaSourceValue(previous, display) {
|
|
5837
|
+
if (previous === void 0 || !display.startsWith(previous)) return display;
|
|
5838
|
+
return display.slice(previous.length).replace(/^\s+/, "") || display;
|
|
5839
|
+
}
|
|
5840
|
+
function limitVisibilityContent(content) {
|
|
5841
|
+
const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
|
|
5842
|
+
if (content.length <= limit) return content;
|
|
5843
|
+
return `${content.slice(0, limit)}
|
|
5844
|
+
...[truncated; original length: ${content.length} characters]...`;
|
|
5845
|
+
}
|
|
5846
|
+
function boundVisibilityLog(records) {
|
|
5847
|
+
if (records === void 0) return [];
|
|
5848
|
+
const limit = ContextManager.VISIBILITY_RECORD_CONTENT_LIMIT;
|
|
5849
|
+
const bounded2 = records.map((item) => ({
|
|
5850
|
+
...item,
|
|
5851
|
+
...item.content.length <= limit ? {} : { content: limitVisibilityContent(item.content) }
|
|
5852
|
+
}));
|
|
5853
|
+
return bounded2.length > ContextManager.VISIBILITY_LOG_MAX_ENTRIES ? bounded2.slice(-ContextManager.VISIBILITY_LOG_MAX_ENTRIES) : bounded2;
|
|
5854
|
+
}
|
|
5790
5855
|
function cloneEpoch(epoch) {
|
|
5791
5856
|
return {
|
|
5792
5857
|
...epoch,
|
|
@@ -6081,6 +6146,9 @@ function reportProgress(callback, percentage) {
|
|
|
6081
6146
|
}
|
|
6082
6147
|
}
|
|
6083
6148
|
|
|
6149
|
+
// src/agent/loop.ts
|
|
6150
|
+
import { getHeapStatistics } from "v8";
|
|
6151
|
+
|
|
6084
6152
|
// src/models/structured.ts
|
|
6085
6153
|
import { z as z12 } from "zod";
|
|
6086
6154
|
var DEFAULT_RETRY_DELAYS = [1e3, 2e3, 4e3];
|
|
@@ -6498,6 +6566,18 @@ function envMaxIterations() {
|
|
|
6498
6566
|
if (!Number.isInteger(parsed) || parsed <= 0) return void 0;
|
|
6499
6567
|
return parsed;
|
|
6500
6568
|
}
|
|
6569
|
+
var MEMORY_PRESSURE_HEAP_RATIO = 0.8;
|
|
6570
|
+
function memoryPressureError(heapUsed, heapLimit) {
|
|
6571
|
+
if (heapLimit <= 0 || heapUsed < heapLimit * MEMORY_PRESSURE_HEAP_RATIO) return void 0;
|
|
6572
|
+
const mb = (bytes) => Math.round(bytes / 1048576);
|
|
6573
|
+
return {
|
|
6574
|
+
code: "memory_pressure",
|
|
6575
|
+
message: `Heap usage reached ${Math.round(heapUsed / heapLimit * 100)}% of the V8 limit (${mb(heapUsed)}MB of ${mb(heapLimit)}MB); stopping to avoid a crash. The session is saved and will resume after restart.`
|
|
6576
|
+
};
|
|
6577
|
+
}
|
|
6578
|
+
function currentMemoryPressure() {
|
|
6579
|
+
return memoryPressureError(process.memoryUsage().heapUsed, getHeapStatistics().heap_size_limit);
|
|
6580
|
+
}
|
|
6501
6581
|
var AgentLoop = class {
|
|
6502
6582
|
#options;
|
|
6503
6583
|
#iterationLimitMode = "standard";
|
|
@@ -6568,6 +6648,11 @@ var AgentLoop = class {
|
|
|
6568
6648
|
yield { type: "error", error: { code: "cancelled", message: abortMessage(request.signal) } };
|
|
6569
6649
|
return;
|
|
6570
6650
|
}
|
|
6651
|
+
const pressure = currentMemoryPressure();
|
|
6652
|
+
if (pressure !== void 0) {
|
|
6653
|
+
yield { type: "error", error: pressure };
|
|
6654
|
+
return;
|
|
6655
|
+
}
|
|
6571
6656
|
const softLimit = Math.floor(maxIterations * this.#options.softLimitFactor);
|
|
6572
6657
|
if (!warned && iteration >= softLimit) {
|
|
6573
6658
|
warned = true;
|
|
@@ -12927,7 +13012,8 @@ var BoundedOutput = class {
|
|
|
12927
13012
|
#headLimit;
|
|
12928
13013
|
#tailLimit;
|
|
12929
13014
|
#head = Buffer.alloc(0);
|
|
12930
|
-
#
|
|
13015
|
+
#tailChunks = [];
|
|
13016
|
+
#tailBytes = 0;
|
|
12931
13017
|
#complete = Buffer.alloc(0);
|
|
12932
13018
|
#bytes = 0;
|
|
12933
13019
|
constructor(limit) {
|
|
@@ -12947,15 +13033,24 @@ var BoundedOutput = class {
|
|
|
12947
13033
|
const count = Math.min(this.#headLimit - this.#head.length, chunk.length);
|
|
12948
13034
|
this.#head = Buffer.concat([this.#head, chunk.subarray(0, count)]);
|
|
12949
13035
|
}
|
|
12950
|
-
if (this.#tailLimit > 0)
|
|
13036
|
+
if (this.#tailLimit > 0) {
|
|
13037
|
+
this.#tailChunks.push(chunk);
|
|
13038
|
+
this.#tailBytes += chunk.length;
|
|
13039
|
+
if (this.#tailBytes >= this.#tailLimit * 2) {
|
|
13040
|
+
const merged = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
13041
|
+
this.#tailChunks = [merged];
|
|
13042
|
+
this.#tailBytes = merged.length;
|
|
13043
|
+
}
|
|
13044
|
+
}
|
|
12951
13045
|
}
|
|
12952
13046
|
metadata() {
|
|
12953
13047
|
return { truncated: this.truncated, originalBytes: this.#bytes, limitBytes: this.#limit };
|
|
12954
13048
|
}
|
|
12955
13049
|
text() {
|
|
12956
13050
|
if (!this.truncated) return decodeOutput(this.#complete);
|
|
13051
|
+
const tail = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
12957
13052
|
const encoding = classifyUtf8(this.#complete) === "invalid" && process.platform === "win32" ? "gb18030" : "utf-8";
|
|
12958
|
-
return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(
|
|
13053
|
+
return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(tail, encoding)}`;
|
|
12959
13054
|
}
|
|
12960
13055
|
};
|
|
12961
13056
|
var AdaptiveOutputDecoder = class {
|
|
@@ -16774,11 +16869,35 @@ function isCode10(error, code) {
|
|
|
16774
16869
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16775
16870
|
}
|
|
16776
16871
|
|
|
16872
|
+
// src/utils/memory-restart.ts
|
|
16873
|
+
import { join as join26 } from "path";
|
|
16874
|
+
var MEMORY_RESTART_EXIT_CODE = 75;
|
|
16875
|
+
var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
|
|
16876
|
+
var MEMORY_RESTART_WINDOW_MS = 30 * 60 * 1e3;
|
|
16877
|
+
function memoryRestartMarkerPath(workspace) {
|
|
16878
|
+
return join26(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
|
|
16879
|
+
}
|
|
16880
|
+
function nextMemoryRestartMarker(previous, sessionId, now) {
|
|
16881
|
+
const reusable = previous !== void 0 && previous.sessionId === sessionId && now.getTime() - Date.parse(previous.requestedAt) < MEMORY_RESTART_WINDOW_MS && Number.isInteger(previous.attempts) && previous.attempts > 0;
|
|
16882
|
+
return { sessionId, requestedAt: now.toISOString(), attempts: reusable ? previous.attempts + 1 : 1 };
|
|
16883
|
+
}
|
|
16884
|
+
function parseMemoryRestartMarker(json) {
|
|
16885
|
+
try {
|
|
16886
|
+
const value = JSON.parse(json);
|
|
16887
|
+
if (typeof value.sessionId !== "string" || typeof value.requestedAt !== "string" || typeof value.attempts !== "number") {
|
|
16888
|
+
return void 0;
|
|
16889
|
+
}
|
|
16890
|
+
return { sessionId: value.sessionId, requestedAt: value.requestedAt, attempts: value.attempts };
|
|
16891
|
+
} catch {
|
|
16892
|
+
return void 0;
|
|
16893
|
+
}
|
|
16894
|
+
}
|
|
16895
|
+
|
|
16777
16896
|
// src/sleep/organizer.ts
|
|
16778
16897
|
import { constants as constants7 } from "fs";
|
|
16779
16898
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
16780
16899
|
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";
|
|
16781
|
-
import { isAbsolute as isAbsolute10, join as
|
|
16900
|
+
import { isAbsolute as isAbsolute10, join as join27, relative as relative15, resolve as resolve22, sep as sep15 } from "path";
|
|
16782
16901
|
import { z as z28 } from "zod";
|
|
16783
16902
|
var DateKeySchema = z28.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
16784
16903
|
var ReviewSchema = z28.object({
|
|
@@ -16833,7 +16952,7 @@ var ProjectSleepOrganizer = class {
|
|
|
16833
16952
|
#generate;
|
|
16834
16953
|
constructor(options) {
|
|
16835
16954
|
this.#workspace = resolve22(options.workspace);
|
|
16836
|
-
this.#sleepDirectory =
|
|
16955
|
+
this.#sleepDirectory = join27(this.#workspace, ".flavor", "sleep");
|
|
16837
16956
|
this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
|
|
16838
16957
|
this.#generate = options.generate;
|
|
16839
16958
|
}
|
|
@@ -16842,7 +16961,7 @@ var ProjectSleepOrganizer = class {
|
|
|
16842
16961
|
signal.throwIfAborted();
|
|
16843
16962
|
await this.#prepareDirectory();
|
|
16844
16963
|
if (await this.#hasReport(date)) return { status: "exists", date };
|
|
16845
|
-
const lockPath =
|
|
16964
|
+
const lockPath = join27(this.#sleepDirectory, `.${date}.lock`);
|
|
16846
16965
|
let lock;
|
|
16847
16966
|
try {
|
|
16848
16967
|
lock = await open9(lockPath, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
@@ -16877,7 +16996,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16877
16996
|
}
|
|
16878
16997
|
if (review === void 0) throw lastError;
|
|
16879
16998
|
const filename2 = `${date}-${filenameSummary(review.title)}.md`;
|
|
16880
|
-
const path =
|
|
16999
|
+
const path = join27(this.#sleepDirectory, filename2);
|
|
16881
17000
|
const markdown = renderSleepReport(date, review, documents);
|
|
16882
17001
|
await this.#atomicWrite(path, markdown);
|
|
16883
17002
|
return { status: "written", date, path, sessionCount: documents.length };
|
|
@@ -16919,7 +17038,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16919
17038
|
});
|
|
16920
17039
|
}
|
|
16921
17040
|
async #prepareDirectory() {
|
|
16922
|
-
const flavorDirectory =
|
|
17041
|
+
const flavorDirectory = join27(this.#workspace, ".flavor");
|
|
16923
17042
|
await assertNotSymlink(flavorDirectory);
|
|
16924
17043
|
await mkdir18(flavorDirectory, { recursive: true, mode: 448 });
|
|
16925
17044
|
await assertNotSymlink(this.#sleepDirectory);
|
|
@@ -16933,7 +17052,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
16933
17052
|
return (await readdir8(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
|
|
16934
17053
|
}
|
|
16935
17054
|
async #atomicWrite(path, content) {
|
|
16936
|
-
const temporary =
|
|
17055
|
+
const temporary = join27(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
|
|
16937
17056
|
let handle;
|
|
16938
17057
|
try {
|
|
16939
17058
|
handle = await open9(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
@@ -19837,12 +19956,12 @@ ${explanation}` : "";
|
|
|
19837
19956
|
// src/desktop/astgraph-service.ts
|
|
19838
19957
|
import { stat as stat10 } from "fs/promises";
|
|
19839
19958
|
import { createRequire as nodeCreateRequire } from "module";
|
|
19840
|
-
import { join as
|
|
19959
|
+
import { join as join28, resolve as resolve26 } from "path";
|
|
19841
19960
|
var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
|
|
19842
19961
|
var DesktopAstGraphService = class {
|
|
19843
19962
|
#path;
|
|
19844
19963
|
constructor(workspace) {
|
|
19845
|
-
this.#path =
|
|
19964
|
+
this.#path = join28(resolve26(workspace), ".flavor", "astgraph", "index.db");
|
|
19846
19965
|
}
|
|
19847
19966
|
async status() {
|
|
19848
19967
|
if ((await stat10(this.#path).catch(() => void 0))?.isFile() !== true) {
|
|
@@ -20111,7 +20230,7 @@ function createTodoWriteTool() {
|
|
|
20111
20230
|
// src/tools/managed.ts
|
|
20112
20231
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
20113
20232
|
import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile22, readdir as readdir9, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
|
|
20114
|
-
import { dirname as dirname21, join as
|
|
20233
|
+
import { dirname as dirname21, join as join29, resolve as resolve27 } from "path";
|
|
20115
20234
|
import { z as z37 } from "zod";
|
|
20116
20235
|
var MANAGED_TOOL_VERSION = 1;
|
|
20117
20236
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -20154,8 +20273,8 @@ var ManagedToolStore = class {
|
|
|
20154
20273
|
#diagnostics = [];
|
|
20155
20274
|
constructor(options) {
|
|
20156
20275
|
this.#workspace = resolve27(options.workspace);
|
|
20157
|
-
this.#projectRoot =
|
|
20158
|
-
this.#globalRoot =
|
|
20276
|
+
this.#projectRoot = join29(this.#workspace, ".flavor", "tools");
|
|
20277
|
+
this.#globalRoot = join29(resolve27(options.home), ".flavor-code", "tools");
|
|
20159
20278
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
20160
20279
|
}
|
|
20161
20280
|
get diagnostics() {
|
|
@@ -20202,7 +20321,7 @@ var ManagedToolStore = class {
|
|
|
20202
20321
|
version: MANAGED_TOOL_VERSION,
|
|
20203
20322
|
createdAt: this.#now()
|
|
20204
20323
|
});
|
|
20205
|
-
const path =
|
|
20324
|
+
const path = join29(this.directory(input.scope), filename(input.name));
|
|
20206
20325
|
const definition = compileDefinition(record3, input.scope, this.#workspace);
|
|
20207
20326
|
await writeExclusive(path, `${JSON.stringify(record3, void 0, 2)}
|
|
20208
20327
|
`);
|
|
@@ -20227,7 +20346,7 @@ var ManagedToolStore = class {
|
|
|
20227
20346
|
if (!parsed.success) return [];
|
|
20228
20347
|
const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
|
|
20229
20348
|
if (matches.length > 0) return matches.map((entry2) => entry2.path);
|
|
20230
|
-
return [
|
|
20349
|
+
return [join29(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
|
|
20231
20350
|
}
|
|
20232
20351
|
async #loadDirectory(scope2) {
|
|
20233
20352
|
const root = this.directory(scope2);
|
|
@@ -20240,7 +20359,7 @@ var ManagedToolStore = class {
|
|
|
20240
20359
|
return;
|
|
20241
20360
|
}
|
|
20242
20361
|
for (const name of names) {
|
|
20243
|
-
const path =
|
|
20362
|
+
const path = join29(root, name);
|
|
20244
20363
|
try {
|
|
20245
20364
|
const info = await lstat11(path);
|
|
20246
20365
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
|
|
@@ -22558,7 +22677,7 @@ ${total}`;
|
|
|
22558
22677
|
// src/memory/store.ts
|
|
22559
22678
|
import { createHash as createHash17 } from "crypto";
|
|
22560
22679
|
import { readFile as readFile23, rm as rm12 } from "fs/promises";
|
|
22561
|
-
import { resolve as resolve28, join as
|
|
22680
|
+
import { resolve as resolve28, join as join30, relative as relative19, sep as sep19 } from "path";
|
|
22562
22681
|
|
|
22563
22682
|
// src/memory/similarity.ts
|
|
22564
22683
|
function normalizeForSimilarity(value) {
|
|
@@ -22661,9 +22780,9 @@ var MemoryStore = class {
|
|
|
22661
22780
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
22662
22781
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
22663
22782
|
this.workspace = resolve28(options.workspace);
|
|
22664
|
-
this.#memoryRoot =
|
|
22665
|
-
this.path =
|
|
22666
|
-
this.#behaviorPath =
|
|
22783
|
+
this.#memoryRoot = join30(this.workspace, ".flavor", "memory");
|
|
22784
|
+
this.path = join30(this.#memoryRoot, "MEMORY.md");
|
|
22785
|
+
this.#behaviorPath = join30(this.#memoryRoot, "behavior.json");
|
|
22667
22786
|
this.#maxEntries = options.maxEntries;
|
|
22668
22787
|
this.#maxEntryChars = options.maxEntryChars;
|
|
22669
22788
|
}
|
|
@@ -23452,7 +23571,7 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
23452
23571
|
|
|
23453
23572
|
// src/ide/client.ts
|
|
23454
23573
|
import { readFile as readFile24, readdir as readdir10, stat as stat11 } from "fs/promises";
|
|
23455
|
-
import { basename as basename7, join as
|
|
23574
|
+
import { basename as basename7, join as join31, relative as relative21, resolve as resolve30, sep as sep21 } from "path";
|
|
23456
23575
|
var FlavorIdeClient = class {
|
|
23457
23576
|
#workspace;
|
|
23458
23577
|
#home;
|
|
@@ -23548,7 +23667,7 @@ var FlavorIdeClient = class {
|
|
|
23548
23667
|
};
|
|
23549
23668
|
if (await this.#healthy(configured)) return configured;
|
|
23550
23669
|
}
|
|
23551
|
-
const directory =
|
|
23670
|
+
const directory = join31(this.#home, ".flavor-code", "ide");
|
|
23552
23671
|
let entries;
|
|
23553
23672
|
try {
|
|
23554
23673
|
entries = (await readdir10(directory)).filter((name) => name.endsWith(".lock"));
|
|
@@ -23556,7 +23675,7 @@ var FlavorIdeClient = class {
|
|
|
23556
23675
|
return void 0;
|
|
23557
23676
|
}
|
|
23558
23677
|
const candidates = (await Promise.all(entries.map(async (name) => {
|
|
23559
|
-
const path =
|
|
23678
|
+
const path = join31(directory, name);
|
|
23560
23679
|
try {
|
|
23561
23680
|
const [contents, info] = await Promise.all([readFile24(path, "utf8"), stat11(path)]);
|
|
23562
23681
|
const lock = parseLockfile(contents);
|
|
@@ -24063,7 +24182,7 @@ function palSocketAddress(options) {
|
|
|
24063
24182
|
import { createHash as createHash20 } from "crypto";
|
|
24064
24183
|
import { Socket as Socket2 } from "net";
|
|
24065
24184
|
import { tmpdir as tmpdir3 } from "os";
|
|
24066
|
-
import { join as
|
|
24185
|
+
import { join as join33 } from "path";
|
|
24067
24186
|
import { spawn as spawn7 } from "child_process";
|
|
24068
24187
|
|
|
24069
24188
|
// src/pals/broker.ts
|
|
@@ -24076,16 +24195,16 @@ import { dirname as dirname23 } from "path";
|
|
|
24076
24195
|
import { randomBytes as randomBytes3, randomUUID as randomUUID21 } from "crypto";
|
|
24077
24196
|
import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as readFile25, unlink as unlink4 } from "fs/promises";
|
|
24078
24197
|
import { homedir } from "os";
|
|
24079
|
-
import { join as
|
|
24198
|
+
import { join as join32 } from "path";
|
|
24080
24199
|
var PAL_AUTH_TOKEN_BYTES = 32;
|
|
24081
24200
|
var TOKEN_PATTERN = /^[a-f0-9]{64}$/;
|
|
24082
24201
|
async function loadOrCreatePalAuthToken(options = {}) {
|
|
24083
|
-
const directory =
|
|
24084
|
-
const tokenPath =
|
|
24202
|
+
const directory = join32(options.home ?? homedir(), ".flavor-code", "pals");
|
|
24203
|
+
const tokenPath = join32(directory, "auth-token");
|
|
24085
24204
|
await mkdir21(directory, { recursive: true, mode: 448 });
|
|
24086
24205
|
await chmod2(directory, 448);
|
|
24087
24206
|
const token = (options.random ?? randomBytes3)(PAL_AUTH_TOKEN_BYTES).toString("hex");
|
|
24088
|
-
const temporaryPath =
|
|
24207
|
+
const temporaryPath = join32(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
|
|
24089
24208
|
let handle;
|
|
24090
24209
|
try {
|
|
24091
24210
|
handle = await open12(temporaryPath, "wx", 384);
|
|
@@ -24215,7 +24334,7 @@ function canConnect(address) {
|
|
|
24215
24334
|
}
|
|
24216
24335
|
function startupLockPath(address) {
|
|
24217
24336
|
const digest = createHash20("sha256").update(address, "utf8").digest("hex").slice(0, 24);
|
|
24218
|
-
return
|
|
24337
|
+
return join33(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
|
|
24219
24338
|
}
|
|
24220
24339
|
async function spawnProductionBroker(address) {
|
|
24221
24340
|
const entry2 = process.argv[1];
|
|
@@ -25151,8 +25270,8 @@ async function createProductionRuntime(options) {
|
|
|
25151
25270
|
const registeredProviders = registration.registered;
|
|
25152
25271
|
let effectiveLlm = registration.effectiveLlm;
|
|
25153
25272
|
const pluginHost = new PluginHost({
|
|
25154
|
-
globalPluginDirs: [
|
|
25155
|
-
projectPluginDirs: [
|
|
25273
|
+
globalPluginDirs: [join34(home, ".flavor-code", "plugins")],
|
|
25274
|
+
projectPluginDirs: [join34(workspace, ".flavor", "plugins")],
|
|
25156
25275
|
config,
|
|
25157
25276
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
25158
25277
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -25260,7 +25379,7 @@ async function createProductionRuntime(options) {
|
|
|
25260
25379
|
const address = palSocketAddress({
|
|
25261
25380
|
platform: process.platform,
|
|
25262
25381
|
userScope: home,
|
|
25263
|
-
runtimeDir:
|
|
25382
|
+
runtimeDir: join34(tmpdir4(), `flavor-code-pals-${createHash21("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
25264
25383
|
});
|
|
25265
25384
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
25266
25385
|
instanceId: collaboration.instanceId,
|
|
@@ -25371,8 +25490,8 @@ async function createProductionRuntime(options) {
|
|
|
25371
25490
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
25372
25491
|
});
|
|
25373
25492
|
const skills = new SkillRegistry({
|
|
25374
|
-
globalRoots: [
|
|
25375
|
-
projectRoots: [
|
|
25493
|
+
globalRoots: [join34(home, ".flavor-code", "skills")],
|
|
25494
|
+
projectRoots: [join34(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
25376
25495
|
authorizeResource: async () => true,
|
|
25377
25496
|
disabledNames: config.skills.disabled
|
|
25378
25497
|
});
|
|
@@ -25380,7 +25499,7 @@ async function createProductionRuntime(options) {
|
|
|
25380
25499
|
skillsReady.catch(() => void 0);
|
|
25381
25500
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
25382
25501
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
25383
|
-
const flavor = await optionalText2(
|
|
25502
|
+
const flavor = await optionalText2(join34(workspace, "FLAVOR.md"));
|
|
25384
25503
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
25385
25504
|
let memoryContext;
|
|
25386
25505
|
if (memoryStore !== void 0) {
|
|
@@ -25961,7 +26080,7 @@ async function createProductionRuntime(options) {
|
|
|
25961
26080
|
evolveService.toolDefinition(),
|
|
25962
26081
|
...mcpTools
|
|
25963
26082
|
];
|
|
25964
|
-
const loopFlavor = await optionalText2(
|
|
26083
|
+
const loopFlavor = await optionalText2(join34(input.workspace, "FLAVOR.md"));
|
|
25965
26084
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
25966
26085
|
const loopEnvironment = createPromptEnvironment({
|
|
25967
26086
|
now: /* @__PURE__ */ new Date(),
|
|
@@ -26206,7 +26325,7 @@ async function createProductionRuntime(options) {
|
|
|
26206
26325
|
contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
|
|
26207
26326
|
});
|
|
26208
26327
|
const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
|
|
26209
|
-
return durableTurn(persistAndCheckpointAfter(runMain(
|
|
26328
|
+
return monitorMemoryRestart(durableTurn(persistAndCheckpointAfter(runMain(
|
|
26210
26329
|
harness,
|
|
26211
26330
|
skills,
|
|
26212
26331
|
prompt,
|
|
@@ -26226,7 +26345,7 @@ async function createProductionRuntime(options) {
|
|
|
26226
26345
|
prompt,
|
|
26227
26346
|
context: harness.main.context.snapshot(),
|
|
26228
26347
|
label: `turn: ${prompt.slice(0, 80)}`
|
|
26229
|
-
})), harnessJournal, turnId, turnConfig);
|
|
26348
|
+
})), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
|
|
26230
26349
|
},
|
|
26231
26350
|
runSkill: (skill, prompt, signal) => persistAfter(
|
|
26232
26351
|
runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
|
|
@@ -26526,7 +26645,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
26526
26645
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
26527
26646
|
}
|
|
26528
26647
|
try {
|
|
26529
|
-
const tokenStore = createFileTokenStore(
|
|
26648
|
+
const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
26530
26649
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
26531
26650
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
26532
26651
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -26573,7 +26692,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
26573
26692
|
}
|
|
26574
26693
|
},
|
|
26575
26694
|
async logout() {
|
|
26576
|
-
const tokenStore = createFileTokenStore(
|
|
26695
|
+
const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
26577
26696
|
const tokens2 = await tokenStore.load();
|
|
26578
26697
|
const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
|
|
26579
26698
|
if (!hadCredentials) {
|
|
@@ -26975,6 +27094,31 @@ async function* durableTurn(source, journal, turnId, config) {
|
|
|
26975
27094
|
}
|
|
26976
27095
|
}
|
|
26977
27096
|
}
|
|
27097
|
+
async function* monitorMemoryRestart(source, onRestartNeeded) {
|
|
27098
|
+
let pressure = false;
|
|
27099
|
+
for await (const event of source) {
|
|
27100
|
+
if (event.type === "error" && event.error.code === "memory_pressure") pressure = true;
|
|
27101
|
+
yield event;
|
|
27102
|
+
}
|
|
27103
|
+
if (pressure) onRestartNeeded();
|
|
27104
|
+
}
|
|
27105
|
+
function requestMemoryRestart(workspace, sessionId, emitOutput) {
|
|
27106
|
+
try {
|
|
27107
|
+
const markerPath = memoryRestartMarkerPath(workspace);
|
|
27108
|
+
let previous;
|
|
27109
|
+
try {
|
|
27110
|
+
previous = parseMemoryRestartMarker(readFileSync3(markerPath, "utf8"));
|
|
27111
|
+
} catch {
|
|
27112
|
+
}
|
|
27113
|
+
mkdirSync3(dirname24(markerPath), { recursive: true });
|
|
27114
|
+
writeFileSync(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
|
|
27115
|
+
`, "utf8");
|
|
27116
|
+
} catch {
|
|
27117
|
+
}
|
|
27118
|
+
process.exitCode = MEMORY_RESTART_EXIT_CODE;
|
|
27119
|
+
emitOutput({ type: "notice", message: "Memory watermark reached; the session is saved and Flavor is restarting with a fresh heap." });
|
|
27120
|
+
emitOutput({ type: "exit" });
|
|
27121
|
+
}
|
|
26978
27122
|
async function runGitCommit(deps, hint, signal) {
|
|
26979
27123
|
if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
|
|
26980
27124
|
let summary2 = await changeSummary(deps.workspace);
|
|
@@ -27068,7 +27212,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
27068
27212
|
const configured = { ...providers };
|
|
27069
27213
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
27070
27214
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
27071
|
-
const oauthTokenStore = createFileTokenStore(
|
|
27215
|
+
const oauthTokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
27072
27216
|
const registered = [];
|
|
27073
27217
|
let effectiveLlm;
|
|
27074
27218
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -27501,11 +27645,11 @@ function createMemoryTaskId() {
|
|
|
27501
27645
|
import { createHash as createHash27 } from "crypto";
|
|
27502
27646
|
import { mkdir as mkdir27, readFile as readFile35, readdir as readdir12, rm as rm15, stat as stat15, writeFile as writeFile17 } from "fs/promises";
|
|
27503
27647
|
import { homedir as homedir4 } from "os";
|
|
27504
|
-
import { basename as basename9, join as
|
|
27648
|
+
import { basename as basename9, join as join44, resolve as resolve36 } from "path";
|
|
27505
27649
|
|
|
27506
27650
|
// src/d2c/workflow.ts
|
|
27507
27651
|
import { createHash as createHash22 } from "crypto";
|
|
27508
|
-
import { join as
|
|
27652
|
+
import { join as join35 } from "path";
|
|
27509
27653
|
import { z as z42 } from "zod";
|
|
27510
27654
|
|
|
27511
27655
|
// src/d2c/workflow-shared.ts
|
|
@@ -27651,7 +27795,7 @@ function reviewsFor(report, previous) {
|
|
|
27651
27795
|
function implementationDirectory(report) {
|
|
27652
27796
|
const source = report.implementation.source;
|
|
27653
27797
|
if (!/^https?:\/\//i.test(source)) return source;
|
|
27654
|
-
return
|
|
27798
|
+
return join35("src", "d2c-output", report.task);
|
|
27655
27799
|
}
|
|
27656
27800
|
function createWorkflow(report, framework) {
|
|
27657
27801
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -27775,7 +27919,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
|
|
|
27775
27919
|
};
|
|
27776
27920
|
}
|
|
27777
27921
|
function workflowPath(workspace, task) {
|
|
27778
|
-
return
|
|
27922
|
+
return join35(taskDir(workspace, task), "workflow.json");
|
|
27779
27923
|
}
|
|
27780
27924
|
async function readWorkflow(workspace, task) {
|
|
27781
27925
|
return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
|
|
@@ -28029,7 +28173,7 @@ function confirmApiMapping(mapping, operationKey, operations) {
|
|
|
28029
28173
|
|
|
28030
28174
|
// src/d2c/modules.ts
|
|
28031
28175
|
import { readFile as readFile28 } from "fs/promises";
|
|
28032
|
-
import { isAbsolute as isAbsolute15, join as
|
|
28176
|
+
import { isAbsolute as isAbsolute15, join as join36, normalize as normalize2, posix, sep as sep24, win32 as win322 } from "path";
|
|
28033
28177
|
import { z as z43 } from "zod";
|
|
28034
28178
|
var ModuleSchema = z43.object({
|
|
28035
28179
|
id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
@@ -28048,13 +28192,13 @@ function safeSource(path) {
|
|
|
28048
28192
|
}
|
|
28049
28193
|
function d2cOutputDirectory(workspace, task) {
|
|
28050
28194
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
|
|
28051
|
-
return
|
|
28195
|
+
return join36(workspace, "src", "d2c-output", task);
|
|
28052
28196
|
}
|
|
28053
28197
|
async function readD2cModules(workspace, task) {
|
|
28054
28198
|
const project = d2cOutputDirectory(workspace, task);
|
|
28055
28199
|
let raw;
|
|
28056
28200
|
try {
|
|
28057
|
-
raw = await readFile28(
|
|
28201
|
+
raw = await readFile28(join36(project, "d2c.modules.json"), "utf8");
|
|
28058
28202
|
} catch {
|
|
28059
28203
|
return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
|
|
28060
28204
|
}
|
|
@@ -28075,7 +28219,7 @@ async function readD2cModules(workspace, task) {
|
|
|
28075
28219
|
|
|
28076
28220
|
// src/d2c/integration.ts
|
|
28077
28221
|
import { mkdir as mkdir24, readFile as readFile29, writeFile as writeFile13 } from "fs/promises";
|
|
28078
|
-
import { join as
|
|
28222
|
+
import { join as join37 } from "path";
|
|
28079
28223
|
function record2(value) {
|
|
28080
28224
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
28081
28225
|
}
|
|
@@ -28211,7 +28355,7 @@ async def reset_acceptance(request: Request):
|
|
|
28211
28355
|
}
|
|
28212
28356
|
async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
|
|
28213
28357
|
if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
|
|
28214
|
-
const packagePath =
|
|
28358
|
+
const packagePath = join37(projectDir, "package.json");
|
|
28215
28359
|
let pkg;
|
|
28216
28360
|
try {
|
|
28217
28361
|
pkg = JSON.parse(await readFile29(packagePath, "utf8"));
|
|
@@ -28221,9 +28365,9 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
28221
28365
|
const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
|
|
28222
28366
|
const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
|
|
28223
28367
|
const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
|
|
28224
|
-
const apiDir =
|
|
28225
|
-
const mockDir =
|
|
28226
|
-
const serverDir =
|
|
28368
|
+
const apiDir = join37(projectDir, "src", "api");
|
|
28369
|
+
const mockDir = join37(projectDir, "mock");
|
|
28370
|
+
const serverDir = join37(projectDir, "server");
|
|
28227
28371
|
await Promise.all([
|
|
28228
28372
|
mkdir24(apiDir, { recursive: true }),
|
|
28229
28373
|
...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
|
|
@@ -28233,23 +28377,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
28233
28377
|
if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
|
|
28234
28378
|
else files.push("mock/server.mjs");
|
|
28235
28379
|
await Promise.all([
|
|
28236
|
-
writeFile13(
|
|
28380
|
+
writeFile13(join37(apiDir, "http.js"), `import axios from "axios";
|
|
28237
28381
|
|
|
28238
28382
|
export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
|
|
28239
28383
|
|
|
28240
28384
|
http.interceptors.response.use((response) => response, (error) => Promise.reject({ name: "ApiError", status: error.response?.status, message: error.response?.data?.message || error.message, cause: error }));
|
|
28241
28385
|
`),
|
|
28242
|
-
writeFile13(
|
|
28243
|
-
writeFile13(
|
|
28386
|
+
writeFile13(join37(apiDir, "d2c-api.js"), apiModule(selected)),
|
|
28387
|
+
writeFile13(join37(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
|
|
28244
28388
|
`),
|
|
28245
|
-
...options.pythonServer ? [] : [writeFile13(
|
|
28389
|
+
...options.pythonServer ? [] : [writeFile13(join37(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
|
|
28246
28390
|
writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
|
|
28247
28391
|
`),
|
|
28248
28392
|
...options.pythonServer ? [
|
|
28249
|
-
writeFile13(
|
|
28250
|
-
writeFile13(
|
|
28251
|
-
writeFile13(
|
|
28252
|
-
writeFile13(
|
|
28393
|
+
writeFile13(join37(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
|
|
28394
|
+
writeFile13(join37(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
|
|
28395
|
+
writeFile13(join37(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"),
|
|
28396
|
+
writeFile13(join37(serverDir, "flavor-runtime.json"), `${JSON.stringify({
|
|
28253
28397
|
schema: 1,
|
|
28254
28398
|
kind: "python-fastapi",
|
|
28255
28399
|
cwd: "server",
|
|
@@ -28270,7 +28414,7 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
|
|
|
28270
28414
|
import { spawn as spawn8 } from "child_process";
|
|
28271
28415
|
import { access as access3, readFile as readFile30 } from "fs/promises";
|
|
28272
28416
|
import { createServer as createServer5 } from "net";
|
|
28273
|
-
import { join as
|
|
28417
|
+
import { join as join38 } from "path";
|
|
28274
28418
|
async function exists3(path) {
|
|
28275
28419
|
try {
|
|
28276
28420
|
await access3(path);
|
|
@@ -28378,7 +28522,7 @@ ${output.trim()}` : ""}`);
|
|
|
28378
28522
|
}
|
|
28379
28523
|
}
|
|
28380
28524
|
async function runPythonBackend(projectDir, options) {
|
|
28381
|
-
const serverDir =
|
|
28525
|
+
const serverDir = join38(projectDir, "server");
|
|
28382
28526
|
if (options.installDependencies !== false) await installPython(serverDir, options.signal);
|
|
28383
28527
|
const selectedPort = await port();
|
|
28384
28528
|
const url = `http://127.0.0.1:${selectedPort}`;
|
|
@@ -28424,7 +28568,7 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28424
28568
|
};
|
|
28425
28569
|
}
|
|
28426
28570
|
async function runD2cMockServer(projectDir, options = {}) {
|
|
28427
|
-
const runtimePath =
|
|
28571
|
+
const runtimePath = join38(projectDir, "server", "flavor-runtime.json");
|
|
28428
28572
|
if (await exists3(runtimePath)) {
|
|
28429
28573
|
let runtime;
|
|
28430
28574
|
try {
|
|
@@ -28435,9 +28579,9 @@ async function runD2cMockServer(projectDir, options = {}) {
|
|
|
28435
28579
|
if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
|
|
28436
28580
|
return runPythonBackend(projectDir, options);
|
|
28437
28581
|
}
|
|
28438
|
-
const serverPath =
|
|
28582
|
+
const serverPath = join38(projectDir, "mock", "server.mjs");
|
|
28439
28583
|
if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
|
|
28440
|
-
if (options.installDependencies === false && !await exists3(
|
|
28584
|
+
if (options.installDependencies === false && !await exists3(join38(projectDir, "node_modules", "express", "package.json"))) {
|
|
28441
28585
|
throw new Error("D2C mock server dependencies are not installed");
|
|
28442
28586
|
}
|
|
28443
28587
|
if (options.installDependencies !== false) await install(projectDir, options.signal);
|
|
@@ -28480,7 +28624,7 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28480
28624
|
// src/d2c/product.ts
|
|
28481
28625
|
import { createHash as createHash24, randomUUID as randomUUID27 } from "crypto";
|
|
28482
28626
|
import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile32, rename as rename13, stat as stat13, unlink as unlink7, writeFile as writeFile15 } from "fs/promises";
|
|
28483
|
-
import { join as
|
|
28627
|
+
import { join as join39 } from "path";
|
|
28484
28628
|
import { z as z44 } from "zod";
|
|
28485
28629
|
|
|
28486
28630
|
// src/e2e/prd-governance.ts
|
|
@@ -28635,13 +28779,13 @@ var PlanSchema2 = z44.object({
|
|
|
28635
28779
|
}).strict();
|
|
28636
28780
|
function d2cProductDirectory(workspace, task) {
|
|
28637
28781
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
|
|
28638
|
-
return
|
|
28782
|
+
return join39(taskDir(workspace, task), "product");
|
|
28639
28783
|
}
|
|
28640
28784
|
function d2cProductPrototypeDirectory(workspace, task) {
|
|
28641
|
-
return
|
|
28785
|
+
return join39(d2cProductDirectory(workspace, task), "prototype");
|
|
28642
28786
|
}
|
|
28643
28787
|
function planPath(workspace, task) {
|
|
28644
|
-
return
|
|
28788
|
+
return join39(taskDir(workspace, task), "product-plan.json");
|
|
28645
28789
|
}
|
|
28646
28790
|
function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
|
|
28647
28791
|
const frontendCandidates = [
|
|
@@ -28824,16 +28968,16 @@ async function readD2cProductPlan(workspace, task) {
|
|
|
28824
28968
|
}
|
|
28825
28969
|
async function discoverD2cProductArtifacts(workspace, plan) {
|
|
28826
28970
|
const product = d2cProductDirectory(workspace, plan.task);
|
|
28827
|
-
const prdUpdatedAt = await fileTimestamp(
|
|
28828
|
-
const prdHash = await fileHash(
|
|
28829
|
-
const prototypeUpdatedAt = await fileTimestamp(
|
|
28830
|
-
const prototypeHash = await fileHash(
|
|
28831
|
-
const expectedManifest =
|
|
28971
|
+
const prdUpdatedAt = await fileTimestamp(join39(product, "prd.md"));
|
|
28972
|
+
const prdHash = await fileHash(join39(product, "prd.md"));
|
|
28973
|
+
const prototypeUpdatedAt = await fileTimestamp(join39(product, "prototype", "index.html"));
|
|
28974
|
+
const prototypeHash = await fileHash(join39(product, "prototype", "index.html"));
|
|
28975
|
+
const expectedManifest = join39(product, "prototype", "interaction-manifest.json");
|
|
28832
28976
|
let manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
28833
28977
|
if (manifestUpdatedAt === void 0) {
|
|
28834
|
-
const misplacedManifest =
|
|
28978
|
+
const misplacedManifest = join39(product, "interaction-manifest.json");
|
|
28835
28979
|
if (await fileTimestamp(misplacedManifest) !== void 0) {
|
|
28836
|
-
await mkdir25(
|
|
28980
|
+
await mkdir25(join39(product, "prototype"), { recursive: true });
|
|
28837
28981
|
await copyFile2(misplacedManifest, expectedManifest);
|
|
28838
28982
|
manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
28839
28983
|
}
|
|
@@ -28866,8 +29010,8 @@ async function readD2cProductPlanView(workspace, task) {
|
|
|
28866
29010
|
if (stored === void 0) return void 0;
|
|
28867
29011
|
const plan = await discoverD2cProductArtifacts(workspace, stored);
|
|
28868
29012
|
if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
|
|
28869
|
-
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(
|
|
28870
|
-
const manifestPath =
|
|
29013
|
+
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(join39(taskDir(workspace, task), plan.prd.path), "utf8");
|
|
29014
|
+
const manifestPath = join39(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
|
|
28871
29015
|
const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
|
|
28872
29016
|
return {
|
|
28873
29017
|
plan,
|
|
@@ -29026,7 +29170,7 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
|
|
|
29026
29170
|
|
|
29027
29171
|
// src/e2e/delivery-run.ts
|
|
29028
29172
|
import { createHash as createHash26 } from "crypto";
|
|
29029
|
-
import { join as
|
|
29173
|
+
import { join as join40 } from "path";
|
|
29030
29174
|
import { z as z45 } from "zod";
|
|
29031
29175
|
var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
|
|
29032
29176
|
var ArtifactSchema = z45.object({
|
|
@@ -29143,7 +29287,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
|
|
|
29143
29287
|
return { ...run, nodes, updatedAt: timestamp2 };
|
|
29144
29288
|
}
|
|
29145
29289
|
function deliveryRunPath(workspace, task) {
|
|
29146
|
-
return
|
|
29290
|
+
return join40(taskDir(workspace, task), "delivery-run.json");
|
|
29147
29291
|
}
|
|
29148
29292
|
function decodeDeliveryRun(raw) {
|
|
29149
29293
|
return DeliveryRunSchema.parse(JSON.parse(raw));
|
|
@@ -29186,7 +29330,7 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
|
|
|
29186
29330
|
// src/skills/manager.ts
|
|
29187
29331
|
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";
|
|
29188
29332
|
import { homedir as homedir3 } from "os";
|
|
29189
|
-
import { isAbsolute as isAbsolute16, join as
|
|
29333
|
+
import { isAbsolute as isAbsolute16, join as join41, relative as relative24, resolve as resolve34, sep as sep25 } from "path";
|
|
29190
29334
|
import { stringify } from "yaml";
|
|
29191
29335
|
var SkillManager = class {
|
|
29192
29336
|
#workspace;
|
|
@@ -29209,7 +29353,7 @@ var SkillManager = class {
|
|
|
29209
29353
|
async create(draft) {
|
|
29210
29354
|
const value = parseDraft(draft);
|
|
29211
29355
|
const projectRoot = this.#projectRoot();
|
|
29212
|
-
const directory =
|
|
29356
|
+
const directory = join41(projectRoot, value.name);
|
|
29213
29357
|
await mkdir26(projectRoot, { recursive: true, mode: 448 });
|
|
29214
29358
|
try {
|
|
29215
29359
|
await mkdir26(directory, { mode: 448 });
|
|
@@ -29218,7 +29362,7 @@ var SkillManager = class {
|
|
|
29218
29362
|
throw error;
|
|
29219
29363
|
}
|
|
29220
29364
|
try {
|
|
29221
|
-
await writeFile16(
|
|
29365
|
+
await writeFile16(join41(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
29222
29366
|
await this.#persistEnabled(value.name, true);
|
|
29223
29367
|
return this.get(value.name);
|
|
29224
29368
|
} catch (error) {
|
|
@@ -29236,7 +29380,7 @@ var SkillManager = class {
|
|
|
29236
29380
|
throw error;
|
|
29237
29381
|
});
|
|
29238
29382
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
|
|
29239
|
-
const skillFile =
|
|
29383
|
+
const skillFile = join41(directory, "SKILL.md");
|
|
29240
29384
|
const fileInfo = await lstat13(skillFile);
|
|
29241
29385
|
if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
|
|
29242
29386
|
await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
|
|
@@ -29268,12 +29412,12 @@ var SkillManager = class {
|
|
|
29268
29412
|
async #registry() {
|
|
29269
29413
|
const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
|
|
29270
29414
|
const [globalPluginRoots, projectPluginRoots] = await Promise.all([
|
|
29271
|
-
discoverPluginSkillRoots(
|
|
29272
|
-
discoverPluginSkillRoots(
|
|
29415
|
+
discoverPluginSkillRoots(join41(this.#home, ".flavor-code", "plugins")),
|
|
29416
|
+
discoverPluginSkillRoots(join41(this.#workspace, ".flavor", "plugins"))
|
|
29273
29417
|
]);
|
|
29274
29418
|
return {
|
|
29275
29419
|
registry: new SkillRegistry({
|
|
29276
|
-
globalRoots: [
|
|
29420
|
+
globalRoots: [join41(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
|
|
29277
29421
|
projectRoots: [this.#projectRoot(), ...projectPluginRoots]
|
|
29278
29422
|
}),
|
|
29279
29423
|
disabled: new Set(loaded.config.skills.disabled)
|
|
@@ -29289,10 +29433,10 @@ var SkillManager = class {
|
|
|
29289
29433
|
};
|
|
29290
29434
|
}
|
|
29291
29435
|
#projectRoot() {
|
|
29292
|
-
return
|
|
29436
|
+
return join41(this.#workspace, ".flavor", "skills");
|
|
29293
29437
|
}
|
|
29294
29438
|
#projectSkillDirectory(name) {
|
|
29295
|
-
return
|
|
29439
|
+
return join41(this.#projectRoot(), name);
|
|
29296
29440
|
}
|
|
29297
29441
|
};
|
|
29298
29442
|
function parseName(value) {
|
|
@@ -29333,9 +29477,9 @@ async function discoverPluginSkillRoots(directory) {
|
|
|
29333
29477
|
const roots = [];
|
|
29334
29478
|
for (const entry2 of entries) {
|
|
29335
29479
|
if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
|
|
29336
|
-
const pluginRoot = await realpath13(
|
|
29480
|
+
const pluginRoot = await realpath13(join41(directory, entry2.name));
|
|
29337
29481
|
try {
|
|
29338
|
-
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(
|
|
29482
|
+
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(join41(pluginRoot, "flavor-plugin.json"), "utf8")));
|
|
29339
29483
|
for (const contribution of manifest.contributes.skillRoots) {
|
|
29340
29484
|
const candidate = resolve34(pluginRoot, contribution.path);
|
|
29341
29485
|
const info = await lstat13(candidate);
|
|
@@ -29410,11 +29554,11 @@ async function createProjectMemoryManager(options) {
|
|
|
29410
29554
|
}
|
|
29411
29555
|
|
|
29412
29556
|
// src/mcp/config-manager.ts
|
|
29413
|
-
import { join as
|
|
29557
|
+
import { join as join42 } from "path";
|
|
29414
29558
|
var ProjectMcpConfigManager = class {
|
|
29415
29559
|
path;
|
|
29416
29560
|
constructor(workspace) {
|
|
29417
|
-
this.path =
|
|
29561
|
+
this.path = join42(workspace, ".flavor", "flavor.json");
|
|
29418
29562
|
}
|
|
29419
29563
|
async list() {
|
|
29420
29564
|
const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
|
|
@@ -29511,7 +29655,7 @@ function isRecord7(value) {
|
|
|
29511
29655
|
}
|
|
29512
29656
|
|
|
29513
29657
|
// src/desktop/model-config.ts
|
|
29514
|
-
import { join as
|
|
29658
|
+
import { join as join43 } from "path";
|
|
29515
29659
|
var DEFAULT_DESKTOP_MODELS = [
|
|
29516
29660
|
{
|
|
29517
29661
|
id: "anthropic:deepseek-v4-pro",
|
|
@@ -29532,7 +29676,7 @@ var DEFAULT_DESKTOP_MODELS = [
|
|
|
29532
29676
|
];
|
|
29533
29677
|
async function loadDesktopModels(workspace, home) {
|
|
29534
29678
|
try {
|
|
29535
|
-
const tokens2 = await createFileTokenStore(
|
|
29679
|
+
const tokens2 = await createFileTokenStore(join43(home, ".flavor-code", "auth.json")).load();
|
|
29536
29680
|
const pkceModels = [];
|
|
29537
29681
|
const knownPkce = /* @__PURE__ */ new Set();
|
|
29538
29682
|
for (const token of Object.values(tokens2)) {
|
|
@@ -29677,33 +29821,33 @@ function pngDataUrl(png) {
|
|
|
29677
29821
|
}
|
|
29678
29822
|
async function detectD2cFramework(workspace, task) {
|
|
29679
29823
|
try {
|
|
29680
|
-
const pkg = JSON.parse(await readFile35(
|
|
29824
|
+
const pkg = JSON.parse(await readFile35(join44(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
|
|
29681
29825
|
return pkg.dependencies?.react === void 0 ? "vue" : "react";
|
|
29682
29826
|
} catch {
|
|
29683
29827
|
return "vue";
|
|
29684
29828
|
}
|
|
29685
29829
|
}
|
|
29686
29830
|
function integrationDirectory(workspace, task) {
|
|
29687
|
-
return
|
|
29831
|
+
return join44(workspace, ".flavor", "d2c", task, "integration");
|
|
29688
29832
|
}
|
|
29689
29833
|
var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
|
|
29690
29834
|
var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
|
|
29691
29835
|
async function d2cBackendSourceFingerprint(project) {
|
|
29692
|
-
const runtimeManifest =
|
|
29836
|
+
const runtimeManifest = join44(project, "server", "flavor-runtime.json");
|
|
29693
29837
|
const realBackend = (await stat15(runtimeManifest).catch(() => void 0))?.isFile() === true;
|
|
29694
|
-
const root = realBackend ?
|
|
29838
|
+
const root = realBackend ? join44(project, "server") : join44(project, "mock");
|
|
29695
29839
|
if ((await stat15(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
29696
29840
|
const files = [];
|
|
29697
29841
|
const visit = async (directory) => {
|
|
29698
29842
|
const entries = await readdir12(directory, { withFileTypes: true });
|
|
29699
29843
|
for (const entry2 of entries) {
|
|
29700
29844
|
if (entry2.isDirectory()) {
|
|
29701
|
-
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(
|
|
29845
|
+
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join44(directory, entry2.name));
|
|
29702
29846
|
continue;
|
|
29703
29847
|
}
|
|
29704
29848
|
if (!entry2.isFile()) continue;
|
|
29705
29849
|
const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
|
|
29706
|
-
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(
|
|
29850
|
+
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join44(directory, entry2.name));
|
|
29707
29851
|
}
|
|
29708
29852
|
};
|
|
29709
29853
|
await visit(root);
|
|
@@ -30257,7 +30401,7 @@ var DesktopRuntimeController = class {
|
|
|
30257
30401
|
if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
|
|
30258
30402
|
throw new Error("Complete visual review before importing OpenAPI");
|
|
30259
30403
|
}
|
|
30260
|
-
const moduleManifest =
|
|
30404
|
+
const moduleManifest = join44(d2cOutputDirectory(workspace, task), "d2c.modules.json");
|
|
30261
30405
|
const moduleManifestInfo = await stat15(moduleManifest).catch(() => void 0);
|
|
30262
30406
|
if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
|
|
30263
30407
|
throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
|
|
@@ -30270,8 +30414,8 @@ var DesktopRuntimeController = class {
|
|
|
30270
30414
|
await mkdir27(dir, { recursive: true });
|
|
30271
30415
|
const hash = createHash27("sha256").update(raw).digest("hex");
|
|
30272
30416
|
await Promise.all([
|
|
30273
|
-
writeFile17(
|
|
30274
|
-
writeFile17(
|
|
30417
|
+
writeFile17(join44(dir, "swagger.json"), raw),
|
|
30418
|
+
writeFile17(join44(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
|
|
30275
30419
|
`)
|
|
30276
30420
|
]);
|
|
30277
30421
|
const next = {
|
|
@@ -30306,7 +30450,7 @@ var DesktopRuntimeController = class {
|
|
|
30306
30450
|
let workflow = await readWorkflow(workspace, task);
|
|
30307
30451
|
if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
|
|
30308
30452
|
const modules = await readD2cModules(workspace, task);
|
|
30309
|
-
const productOpenApiPath =
|
|
30453
|
+
const productOpenApiPath = join44(taskDir(workspace, task), "product", "openapi.json");
|
|
30310
30454
|
const authored = await readFile35(productOpenApiPath, "utf8").catch(() => void 0);
|
|
30311
30455
|
const generated = authored === void 0;
|
|
30312
30456
|
const prepared = await this.#storeD2cOpenApi(
|
|
@@ -30319,7 +30463,7 @@ var DesktopRuntimeController = class {
|
|
|
30319
30463
|
return prepared;
|
|
30320
30464
|
}
|
|
30321
30465
|
if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
|
|
30322
|
-
const document = JSON.parse(await readFile35(
|
|
30466
|
+
const document = JSON.parse(await readFile35(join44(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
|
|
30323
30467
|
return { workflow, document, mappings: workflow.mappings };
|
|
30324
30468
|
}
|
|
30325
30469
|
async confirmD2cMapping(task, moduleId, operationKey) {
|
|
@@ -30386,7 +30530,7 @@ var DesktopRuntimeController = class {
|
|
|
30386
30530
|
].join("\n");
|
|
30387
30531
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30388
30532
|
if (delivery?.nodes.api.status === "running") {
|
|
30389
|
-
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(
|
|
30533
|
+
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile35(join44(project, file)))));
|
|
30390
30534
|
const completed = await updateDeliveryRun(
|
|
30391
30535
|
workspace,
|
|
30392
30536
|
task,
|
|
@@ -30413,7 +30557,7 @@ var DesktopRuntimeController = class {
|
|
|
30413
30557
|
try {
|
|
30414
30558
|
const fingerprint2 = await d2cBackendSourceFingerprint(project);
|
|
30415
30559
|
if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
|
|
30416
|
-
const environmentPath =
|
|
30560
|
+
const environmentPath = join44(project, ".env.local");
|
|
30417
30561
|
const existingEnvironment = await readFile35(environmentPath, "utf8").catch(() => "");
|
|
30418
30562
|
const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
|
|
30419
30563
|
VITE_API_BASE_URL=${running.url}
|
|
@@ -30494,7 +30638,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30494
30638
|
const readyPreview = this.#d2cPreviews.get(task);
|
|
30495
30639
|
const readyMock = this.#d2cMocks.get(task);
|
|
30496
30640
|
if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
|
|
30497
|
-
const manifestPath =
|
|
30641
|
+
const manifestPath = join44(taskDir(workspace, task), "design", "interaction-manifest.json");
|
|
30498
30642
|
const manifest = parseInteractionManifest(await readFile35(manifestPath, "utf8"));
|
|
30499
30643
|
const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
|
|
30500
30644
|
const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
|
|
@@ -30506,7 +30650,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30506
30650
|
await mkdir27(planDir, { recursive: true });
|
|
30507
30651
|
try {
|
|
30508
30652
|
const observations = await this.#observeD2cPages(manifest, readyPreview.url);
|
|
30509
|
-
const prd = await readFile35(
|
|
30653
|
+
const prd = await readFile35(join44(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
|
|
30510
30654
|
const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
|
|
30511
30655
|
const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
|
|
30512
30656
|
executableManifest = mergeInteractionManifests(manifest, plan.manifest);
|
|
@@ -30517,9 +30661,9 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30517
30661
|
plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
30518
30662
|
pageAnalyses: plan.pageAnalyses
|
|
30519
30663
|
};
|
|
30520
|
-
await writeFile17(
|
|
30664
|
+
await writeFile17(join44(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
|
|
30521
30665
|
`, { encoding: "utf8", mode: 384 });
|
|
30522
|
-
await rm15(
|
|
30666
|
+
await rm15(join44(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
|
|
30523
30667
|
} catch (cause) {
|
|
30524
30668
|
const detail = message(cause);
|
|
30525
30669
|
review = {
|
|
@@ -30527,8 +30671,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30527
30671
|
plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
30528
30672
|
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}`
|
|
30529
30673
|
};
|
|
30530
|
-
await rm15(
|
|
30531
|
-
await writeFile17(
|
|
30674
|
+
await rm15(join44(planDir, "autonomous-interaction-plan.json"), { force: true });
|
|
30675
|
+
await writeFile17(join44(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
|
|
30532
30676
|
schema: 1,
|
|
30533
30677
|
stage: "planning",
|
|
30534
30678
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -30542,13 +30686,13 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30542
30686
|
let result = { ...rawResult, evidenceMode: review.mode };
|
|
30543
30687
|
if (coverage !== void 0) {
|
|
30544
30688
|
const evidence = createAcceptanceEvidence(coverage, result);
|
|
30545
|
-
await writeFile17(
|
|
30689
|
+
await writeFile17(join44(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
|
|
30546
30690
|
`, { encoding: "utf8", mode: 384 });
|
|
30547
30691
|
if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
|
|
30548
30692
|
}
|
|
30549
30693
|
const dir = integrationDirectory(workspace, task);
|
|
30550
30694
|
await mkdir27(dir, { recursive: true });
|
|
30551
|
-
await writeFile17(
|
|
30695
|
+
await writeFile17(join44(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
|
|
30552
30696
|
`);
|
|
30553
30697
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
|
|
30554
30698
|
return { workflow: next, result, review };
|
|
@@ -30561,7 +30705,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30561
30705
|
if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
|
|
30562
30706
|
const output = mock.output().trim();
|
|
30563
30707
|
try {
|
|
30564
|
-
const dir =
|
|
30708
|
+
const dir = join44(d2cOutputDirectory(workspace, task), "mock-server.log");
|
|
30565
30709
|
await writeFile17(dir, `${output}
|
|
30566
30710
|
`);
|
|
30567
30711
|
} catch {
|
|
@@ -30656,7 +30800,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30656
30800
|
if (current === void 0) throw new Error("D2C product plan is unavailable");
|
|
30657
30801
|
if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
|
|
30658
30802
|
if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
|
|
30659
|
-
const path =
|
|
30803
|
+
const path = join44(taskDir(workspace, task), "product", "prd.md");
|
|
30660
30804
|
const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
|
|
30661
30805
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30662
30806
|
await writeD2cProductPlan(workspace, {
|
|
@@ -30676,15 +30820,15 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30676
30820
|
async #assertProductPrdLocked(workspace, task) {
|
|
30677
30821
|
const current = await readD2cProductPlanView(workspace, task);
|
|
30678
30822
|
if (current?.plan.approvedPrd === void 0) return;
|
|
30679
|
-
await assertApprovedPrd(
|
|
30823
|
+
await assertApprovedPrd(join44(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
|
|
30680
30824
|
}
|
|
30681
30825
|
async #acceptancePaths(workspace, task) {
|
|
30682
|
-
const product =
|
|
30683
|
-
const openapi =
|
|
30826
|
+
const product = join44(taskDir(workspace, task), "product");
|
|
30827
|
+
const openapi = join44(product, "openapi.json");
|
|
30684
30828
|
return {
|
|
30685
|
-
prd:
|
|
30686
|
-
prototype:
|
|
30687
|
-
interaction:
|
|
30829
|
+
prd: join44(product, "prd.md"),
|
|
30830
|
+
prototype: join44(product, "prototype", "index.html"),
|
|
30831
|
+
interaction: join44(product, "prototype", "interaction-manifest.json"),
|
|
30688
30832
|
...await stat15(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
|
|
30689
30833
|
};
|
|
30690
30834
|
}
|
|
@@ -30692,17 +30836,17 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30692
30836
|
const product = await readD2cProductPlanView(workspace, task);
|
|
30693
30837
|
if (product?.plan.approvedPrd === void 0) return void 0;
|
|
30694
30838
|
await this.#assertProductPrdLocked(workspace, task);
|
|
30695
|
-
const baseline = JSON.parse(await readFile35(
|
|
30839
|
+
const baseline = JSON.parse(await readFile35(join44(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
|
|
30696
30840
|
await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
|
|
30697
|
-
const design =
|
|
30841
|
+
const design = join44(taskDir(workspace, task), "design");
|
|
30698
30842
|
await Promise.all([
|
|
30699
|
-
verifyAcceptanceArtifact(
|
|
30700
|
-
verifyAcceptanceArtifact(
|
|
30843
|
+
verifyAcceptanceArtifact(join44(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
|
|
30844
|
+
verifyAcceptanceArtifact(join44(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
|
|
30701
30845
|
]);
|
|
30702
30846
|
const workflow = await readWorkflow(workspace, task);
|
|
30703
30847
|
if (workflow?.openapi !== void 0) {
|
|
30704
30848
|
await verifyAcceptanceArtifact(
|
|
30705
|
-
|
|
30849
|
+
join44(taskDir(workspace, task), "integration", "swagger.json"),
|
|
30706
30850
|
{ hash: workflow.openapi.hash },
|
|
30707
30851
|
"runtime OpenAPI contract"
|
|
30708
30852
|
);
|
|
@@ -30723,14 +30867,14 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30723
30867
|
throw new Error("D2C PRD is empty and cannot be accepted");
|
|
30724
30868
|
}
|
|
30725
30869
|
if (stage === "design" && accepted) {
|
|
30726
|
-
const manifest =
|
|
30870
|
+
const manifest = join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
|
|
30727
30871
|
const normalizedManifest = parseInteractionManifest(await readFile35(manifest, "utf8"));
|
|
30728
30872
|
if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
|
|
30729
30873
|
verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
|
|
30730
30874
|
await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
|
|
30731
30875
|
`, { encoding: "utf8", mode: 384 });
|
|
30732
30876
|
const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
|
|
30733
|
-
await writeFile17(
|
|
30877
|
+
await writeFile17(join44(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
|
|
30734
30878
|
`, { encoding: "utf8", mode: 384 });
|
|
30735
30879
|
}
|
|
30736
30880
|
const plan = await writeD2cProductPlan(
|
|
@@ -30764,8 +30908,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30764
30908
|
const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
|
|
30765
30909
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30766
30910
|
if (delivery !== void 0) {
|
|
30767
|
-
const prototype = await readFile35(
|
|
30768
|
-
const interaction = await readFile35(
|
|
30911
|
+
const prototype = await readFile35(join44(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
|
|
30912
|
+
const interaction = await readFile35(join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
|
|
30769
30913
|
const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
|
|
30770
30914
|
artifactRef("product/prototype/index.html", prototype),
|
|
30771
30915
|
artifactRef("product/prototype/interaction-manifest.json", interaction)
|
|
@@ -30849,10 +30993,10 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30849
30993
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
|
|
30850
30994
|
const qualityContent = `${JSON.stringify(judgment, null, 2)}
|
|
30851
30995
|
`;
|
|
30852
|
-
await writeFile17(
|
|
30996
|
+
await writeFile17(join44(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
|
|
30853
30997
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30854
30998
|
if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
|
|
30855
|
-
const interactionContent = await readFile35(
|
|
30999
|
+
const interactionContent = await readFile35(join44(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
|
|
30856
31000
|
const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
|
|
30857
31001
|
artifactRef("integration/interaction-results.json", interactionContent),
|
|
30858
31002
|
artifactRef("quality-judge.json", qualityContent)
|
|
@@ -31033,15 +31177,15 @@ function desktopWindowChrome(platform = process.platform) {
|
|
|
31033
31177
|
}
|
|
31034
31178
|
|
|
31035
31179
|
// src/utils/crash-guard.ts
|
|
31036
|
-
import { mkdirSync as
|
|
31037
|
-
import { join as
|
|
31180
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
31181
|
+
import { join as join45 } from "path";
|
|
31038
31182
|
var installed = false;
|
|
31039
31183
|
function installCrashGuard(options = {}) {
|
|
31040
31184
|
if (installed) return;
|
|
31041
31185
|
installed = true;
|
|
31042
31186
|
const crashLogDir = () => {
|
|
31043
31187
|
try {
|
|
31044
|
-
return
|
|
31188
|
+
return join45((options.workspace ?? process.cwd)(), ".flavor");
|
|
31045
31189
|
} catch {
|
|
31046
31190
|
return process.cwd();
|
|
31047
31191
|
}
|
|
@@ -31049,9 +31193,9 @@ function installCrashGuard(options = {}) {
|
|
|
31049
31193
|
const writeCrashLog = (kind, detail) => {
|
|
31050
31194
|
try {
|
|
31051
31195
|
const dir = crashLogDir();
|
|
31052
|
-
|
|
31053
|
-
const path =
|
|
31054
|
-
|
|
31196
|
+
mkdirSync4(dir, { recursive: true });
|
|
31197
|
+
const path = join45(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
|
|
31198
|
+
writeFileSync2(path, [
|
|
31055
31199
|
`kind: ${kind}`,
|
|
31056
31200
|
`version: ${process.version}`,
|
|
31057
31201
|
`platform: ${process.platform}`,
|
|
@@ -31243,7 +31387,7 @@ function safeGitTarget(value) {
|
|
|
31243
31387
|
// src/desktop/worktree-manager.ts
|
|
31244
31388
|
import { randomUUID as randomUUID28 } from "crypto";
|
|
31245
31389
|
import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat16, writeFile as writeFile18 } from "fs/promises";
|
|
31246
|
-
import { basename as basename10, isAbsolute as isAbsolute19, join as
|
|
31390
|
+
import { basename as basename10, isAbsolute as isAbsolute19, join as join46, relative as relative27, resolve as resolve38, sep as sep27 } from "path";
|
|
31247
31391
|
var DesktopWorktreeManager = class {
|
|
31248
31392
|
#repository;
|
|
31249
31393
|
#storage;
|
|
@@ -31289,7 +31433,7 @@ var DesktopWorktreeManager = class {
|
|
|
31289
31433
|
}
|
|
31290
31434
|
const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
|
|
31291
31435
|
try {
|
|
31292
|
-
const stored = validateStored(JSON.parse(await readFile37(
|
|
31436
|
+
const stored = validateStored(JSON.parse(await readFile37(join46(this.#storage, name), "utf8")), this.#repository, this.#storage);
|
|
31293
31437
|
if ((await stat16(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
31294
31438
|
const status = await git2(stored.path, ["status", "--porcelain"]);
|
|
31295
31439
|
const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
|
|
@@ -31353,7 +31497,7 @@ function validateStored(value, repository, storage) {
|
|
|
31353
31497
|
// src/desktop/workbench-service.ts
|
|
31354
31498
|
import { readFile as readFile38, readdir as readdir14, stat as stat17 } from "fs/promises";
|
|
31355
31499
|
import { homedir as homedir5 } from "os";
|
|
31356
|
-
import { join as
|
|
31500
|
+
import { join as join47, resolve as resolve39 } from "path";
|
|
31357
31501
|
var MAX_TEXT = 128e3;
|
|
31358
31502
|
var MAX_ITEMS = 200;
|
|
31359
31503
|
var DesktopWorkbenchService = class {
|
|
@@ -31386,11 +31530,11 @@ var DesktopWorkbenchService = class {
|
|
|
31386
31530
|
return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
|
|
31387
31531
|
}
|
|
31388
31532
|
async #goals() {
|
|
31389
|
-
const root =
|
|
31533
|
+
const root = join47(this.#workspace, ".flavor", "goals");
|
|
31390
31534
|
const names = await readdir14(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
|
|
31391
31535
|
const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
|
|
31392
31536
|
try {
|
|
31393
|
-
return GoalStateSchema.parse(JSON.parse(await boundedRead2(
|
|
31537
|
+
return GoalStateSchema.parse(JSON.parse(await boundedRead2(join47(root, name))));
|
|
31394
31538
|
} catch {
|
|
31395
31539
|
return void 0;
|
|
31396
31540
|
}
|
|
@@ -31400,7 +31544,7 @@ var DesktopWorkbenchService = class {
|
|
|
31400
31544
|
async #instructions() {
|
|
31401
31545
|
const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
31402
31546
|
const items = await Promise.all(names.map(async (name) => {
|
|
31403
|
-
const path =
|
|
31547
|
+
const path = join47(this.#workspace, name);
|
|
31404
31548
|
try {
|
|
31405
31549
|
return { name, path, content: redactErrorText(await boundedRead2(path)) };
|
|
31406
31550
|
} catch (error) {
|
|
@@ -31413,9 +31557,9 @@ var DesktopWorkbenchService = class {
|
|
|
31413
31557
|
async #permissionFiles() {
|
|
31414
31558
|
const candidates = [
|
|
31415
31559
|
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve39(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
|
|
31416
|
-
{ tier: "user", path:
|
|
31417
|
-
{ tier: "project", path:
|
|
31418
|
-
{ tier: "local", path:
|
|
31560
|
+
{ tier: "user", path: join47(this.#home, ".flavor-code", "permissions.json"), content: "" },
|
|
31561
|
+
{ tier: "project", path: join47(this.#workspace, ".flavor", "permissions.json"), content: "" },
|
|
31562
|
+
{ tier: "local", path: join47(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
|
|
31419
31563
|
];
|
|
31420
31564
|
const values = await Promise.all(candidates.map(async (item) => {
|
|
31421
31565
|
try {
|
|
@@ -31430,7 +31574,7 @@ var DesktopWorkbenchService = class {
|
|
|
31430
31574
|
async #audit() {
|
|
31431
31575
|
let raw;
|
|
31432
31576
|
try {
|
|
31433
|
-
raw = await boundedRead2(
|
|
31577
|
+
raw = await boundedRead2(join47(this.#workspace, ".flavor", "audit.jsonl"));
|
|
31434
31578
|
} catch (error) {
|
|
31435
31579
|
if (error.code === "ENOENT") return [];
|
|
31436
31580
|
throw error;
|
|
@@ -31445,7 +31589,7 @@ var DesktopWorkbenchService = class {
|
|
|
31445
31589
|
}).reverse();
|
|
31446
31590
|
}
|
|
31447
31591
|
async #context(sessionId) {
|
|
31448
|
-
const usage = await readJsonLines2(
|
|
31592
|
+
const usage = await readJsonLines2(join47(this.#workspace, ".flavor", "usage.jsonl"));
|
|
31449
31593
|
if (sessionId === void 0) return { visibility: [], usage };
|
|
31450
31594
|
try {
|
|
31451
31595
|
const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
|
|
@@ -31485,14 +31629,14 @@ function redact2(value, key = "") {
|
|
|
31485
31629
|
|
|
31486
31630
|
// src/desktop/main.ts
|
|
31487
31631
|
installCrashGuard();
|
|
31488
|
-
var moduleDirectory =
|
|
31632
|
+
var moduleDirectory = dirname25(fileURLToPath4(import.meta.url));
|
|
31489
31633
|
var developmentUrl = process.env.FLAVOR_DESKTOP_DEV_URL;
|
|
31490
31634
|
var mainWindow;
|
|
31491
31635
|
var appMenu;
|
|
31492
31636
|
var quitting = false;
|
|
31493
31637
|
function logStartup(step, detail) {
|
|
31494
31638
|
try {
|
|
31495
|
-
const logPath =
|
|
31639
|
+
const logPath = join48(dirname25(process.execPath), "flavor-code-startup.log");
|
|
31496
31640
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
31497
31641
|
appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
|
|
31498
31642
|
`);
|
|
@@ -31517,7 +31661,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
|
|
|
31517
31661
|
};
|
|
31518
31662
|
});
|
|
31519
31663
|
var d2cJudgeClient = createD2cJudgeClient();
|
|
31520
|
-
var judgeStore = () => createD2cJudgeConfigStore(
|
|
31664
|
+
var judgeStore = () => createD2cJudgeConfigStore(join48(app.getPath("userData"), "d2c-judge.json"));
|
|
31521
31665
|
function judgeImage(png) {
|
|
31522
31666
|
const image = nativeImage.createFromBuffer(png);
|
|
31523
31667
|
const size = image.getSize();
|
|
@@ -31543,7 +31687,7 @@ function sessionMetaKey(workspace, sessionId) {
|
|
|
31543
31687
|
}
|
|
31544
31688
|
function worktreeManager(workspace) {
|
|
31545
31689
|
const key = createHash28("sha256").update(resolve40(workspace)).digest("hex").slice(0, 20);
|
|
31546
|
-
return new DesktopWorktreeManager({ repository: workspace, storage:
|
|
31690
|
+
return new DesktopWorktreeManager({ repository: workspace, storage: join48(app.getPath("userData"), "task-worktrees", key) });
|
|
31547
31691
|
}
|
|
31548
31692
|
function activeWorkingDirectory() {
|
|
31549
31693
|
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
@@ -31688,7 +31832,7 @@ function createDesktopController(workspace, owner2) {
|
|
|
31688
31832
|
alias: `desktop-${basename11(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
|
|
31689
31833
|
optional: true,
|
|
31690
31834
|
startBroker: (address) => {
|
|
31691
|
-
const entry2 = app.isPackaged ?
|
|
31835
|
+
const entry2 = app.isPackaged ? join48(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join48(moduleDirectory, "pals-broker-entry.js");
|
|
31692
31836
|
return spawnPalBrokerProcess({
|
|
31693
31837
|
address,
|
|
31694
31838
|
executable: process.execPath,
|
|
@@ -31710,7 +31854,7 @@ function createDesktopController(workspace, owner2) {
|
|
|
31710
31854
|
var detachedController = createDesktopController();
|
|
31711
31855
|
var controller = detachedController;
|
|
31712
31856
|
function statePath() {
|
|
31713
|
-
return
|
|
31857
|
+
return join48(app.getPath("userData"), "desktop-state.json");
|
|
31714
31858
|
}
|
|
31715
31859
|
async function loadPersistedProjects() {
|
|
31716
31860
|
try {
|
|
@@ -31883,7 +32027,7 @@ async function chooseAndOpenWorkspace() {
|
|
|
31883
32027
|
var cachedAppIconDataUrl;
|
|
31884
32028
|
function appIconDataUrl() {
|
|
31885
32029
|
if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
|
|
31886
|
-
const icon = nativeImage.createFromPath(
|
|
32030
|
+
const icon = nativeImage.createFromPath(join48(app.getAppPath(), "assets", "icon.png"));
|
|
31887
32031
|
if (icon.isEmpty()) return void 0;
|
|
31888
32032
|
cachedAppIconDataUrl = icon.toDataURL();
|
|
31889
32033
|
return cachedAppIconDataUrl;
|
|
@@ -32301,7 +32445,7 @@ function applicationMenu() {
|
|
|
32301
32445
|
]);
|
|
32302
32446
|
}
|
|
32303
32447
|
async function createWindow() {
|
|
32304
|
-
const rendererPath =
|
|
32448
|
+
const rendererPath = join48(app.getAppPath(), "dist", "desktop-renderer", "index.html");
|
|
32305
32449
|
mainWindow = new BrowserWindow2({
|
|
32306
32450
|
title: "Flavor Code",
|
|
32307
32451
|
width: 1280,
|
|
@@ -32312,14 +32456,14 @@ async function createWindow() {
|
|
|
32312
32456
|
show: false,
|
|
32313
32457
|
...desktopWindowChrome(),
|
|
32314
32458
|
webPreferences: {
|
|
32315
|
-
preload:
|
|
32459
|
+
preload: join48(moduleDirectory, "preload.cjs"),
|
|
32316
32460
|
contextIsolation: true,
|
|
32317
32461
|
nodeIntegration: false,
|
|
32318
32462
|
sandbox: false,
|
|
32319
32463
|
webSecurity: true
|
|
32320
32464
|
}
|
|
32321
32465
|
});
|
|
32322
|
-
const iconPath =
|
|
32466
|
+
const iconPath = join48(app.getAppPath(), "assets", "icon.png");
|
|
32323
32467
|
const icon = nativeImage.createFromPath(iconPath);
|
|
32324
32468
|
if (!icon.isEmpty()) {
|
|
32325
32469
|
mainWindow.setIcon(icon);
|