flavor-code 1.3.13 → 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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/agent/loop.d.ts +10 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/{app-2SBDQ6NS.js → app-6SEGTMMC.js} +136 -31
- package/dist/chunk-CIVPKHF4.js +53 -0
- package/dist/{chunk-JFKRX2KG.js → chunk-HJOCNKPN.js} +1 -1
- package/dist/{chunk-EYCTP5JB.js → chunk-LMFN2QEA.js} +1 -1
- package/dist/{chunk-W2D2MLY2.js → chunk-S525EOFQ.js} +182 -34
- package/dist/cli-main.js +1037 -0
- package/dist/cli.js +84 -1000
- package/dist/context/manager.d.ts +7 -0
- package/dist/desktop/main.js +360 -194
- package/dist/desktop-renderer/assets/{index-BiDVkW9t.js → index-C3JpD2_B.js} +6 -6
- package/dist/desktop-renderer/assets/index-ZZsUx916.css +1 -0
- package/dist/desktop-renderer/assets/{interactive-terminal-CKHHiGNG.js → interactive-terminal-f1PNniPO.js} +1 -1
- package/dist/desktop-renderer/index.html +2 -2
- 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-renderer/assets/index-DHTr_Acb.css +0 -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];
|
|
@@ -24993,9 +25112,28 @@ async function createProductionRuntime(options) {
|
|
|
24993
25112
|
const recovered = options.resumeSession === void 0 ? void 0 : await sessionStore.load(options.resumeSession === true ? void 0 : options.resumeSession);
|
|
24994
25113
|
let timelineState = recovered === void 0 ? createTranscriptState() : restoreTranscriptState(recovered.timeline.state);
|
|
24995
25114
|
const restoredTranscript = restoreTranscriptState(timelineState);
|
|
25115
|
+
const pendingTimelineStreams = [];
|
|
25116
|
+
const flushTimelineStreams = () => {
|
|
25117
|
+
for (const pending2 of pendingTimelineStreams.splice(0)) {
|
|
25118
|
+
timelineState = transcriptReducer(timelineState, {
|
|
25119
|
+
type: "session",
|
|
25120
|
+
event: { type: pending2.type, text: pending2.chunks.join("") }
|
|
25121
|
+
});
|
|
25122
|
+
}
|
|
25123
|
+
};
|
|
25124
|
+
const recordTimelineEvent = (event) => {
|
|
25125
|
+
if (event.type === "text" || event.type === "thinking") {
|
|
25126
|
+
const last = pendingTimelineStreams.at(-1);
|
|
25127
|
+
if (last?.type === event.type) last.chunks.push(event.text);
|
|
25128
|
+
else pendingTimelineStreams.push({ type: event.type, chunks: [event.text] });
|
|
25129
|
+
return;
|
|
25130
|
+
}
|
|
25131
|
+
flushTimelineStreams();
|
|
25132
|
+
timelineState = transcriptReducer(timelineState, { type: "session", event });
|
|
25133
|
+
};
|
|
24996
25134
|
let ideSessionId;
|
|
24997
25135
|
const emitOutput = (event) => {
|
|
24998
|
-
|
|
25136
|
+
recordTimelineEvent(event);
|
|
24999
25137
|
options.output(event);
|
|
25000
25138
|
if (ideSessionId !== void 0) ide.publishEvent(ideSessionId, event);
|
|
25001
25139
|
};
|
|
@@ -25132,8 +25270,8 @@ async function createProductionRuntime(options) {
|
|
|
25132
25270
|
const registeredProviders = registration.registered;
|
|
25133
25271
|
let effectiveLlm = registration.effectiveLlm;
|
|
25134
25272
|
const pluginHost = new PluginHost({
|
|
25135
|
-
globalPluginDirs: [
|
|
25136
|
-
projectPluginDirs: [
|
|
25273
|
+
globalPluginDirs: [join34(home, ".flavor-code", "plugins")],
|
|
25274
|
+
projectPluginDirs: [join34(workspace, ".flavor", "plugins")],
|
|
25137
25275
|
config,
|
|
25138
25276
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
25139
25277
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -25241,7 +25379,7 @@ async function createProductionRuntime(options) {
|
|
|
25241
25379
|
const address = palSocketAddress({
|
|
25242
25380
|
platform: process.platform,
|
|
25243
25381
|
userScope: home,
|
|
25244
|
-
runtimeDir:
|
|
25382
|
+
runtimeDir: join34(tmpdir4(), `flavor-code-pals-${createHash21("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
25245
25383
|
});
|
|
25246
25384
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
25247
25385
|
instanceId: collaboration.instanceId,
|
|
@@ -25352,8 +25490,8 @@ async function createProductionRuntime(options) {
|
|
|
25352
25490
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
25353
25491
|
});
|
|
25354
25492
|
const skills = new SkillRegistry({
|
|
25355
|
-
globalRoots: [
|
|
25356
|
-
projectRoots: [
|
|
25493
|
+
globalRoots: [join34(home, ".flavor-code", "skills")],
|
|
25494
|
+
projectRoots: [join34(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
25357
25495
|
authorizeResource: async () => true,
|
|
25358
25496
|
disabledNames: config.skills.disabled
|
|
25359
25497
|
});
|
|
@@ -25361,7 +25499,7 @@ async function createProductionRuntime(options) {
|
|
|
25361
25499
|
skillsReady.catch(() => void 0);
|
|
25362
25500
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
25363
25501
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
25364
|
-
const flavor = await optionalText2(
|
|
25502
|
+
const flavor = await optionalText2(join34(workspace, "FLAVOR.md"));
|
|
25365
25503
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
25366
25504
|
let memoryContext;
|
|
25367
25505
|
if (memoryStore !== void 0) {
|
|
@@ -25412,27 +25550,30 @@ async function createProductionRuntime(options) {
|
|
|
25412
25550
|
};
|
|
25413
25551
|
}
|
|
25414
25552
|
let persistTail = Promise.resolve();
|
|
25415
|
-
const sessionDocument = () =>
|
|
25416
|
-
|
|
25417
|
-
|
|
25418
|
-
|
|
25419
|
-
|
|
25420
|
-
|
|
25421
|
-
|
|
25422
|
-
|
|
25423
|
-
|
|
25424
|
-
|
|
25425
|
-
|
|
25426
|
-
|
|
25427
|
-
|
|
25428
|
-
|
|
25429
|
-
|
|
25430
|
-
|
|
25431
|
-
|
|
25432
|
-
|
|
25433
|
-
|
|
25434
|
-
|
|
25435
|
-
|
|
25553
|
+
const sessionDocument = () => {
|
|
25554
|
+
flushTimelineStreams();
|
|
25555
|
+
return {
|
|
25556
|
+
version: SESSION_VERSION,
|
|
25557
|
+
sessionId,
|
|
25558
|
+
createdAt,
|
|
25559
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25560
|
+
workspace: { path: workspace },
|
|
25561
|
+
conversation: storedConversation(harness.main.context.snapshot()),
|
|
25562
|
+
tasks: {
|
|
25563
|
+
...taskPlan === void 0 ? {} : { plan: taskPlan },
|
|
25564
|
+
...taskGraph === void 0 ? {} : { graph: taskGraph },
|
|
25565
|
+
states: { ...taskStates },
|
|
25566
|
+
results: { ...taskResults }
|
|
25567
|
+
},
|
|
25568
|
+
// Persist the resolved decision, not the harness snapshot: after logout the
|
|
25569
|
+
// harness may still hold the old login model, and resuming from that stale
|
|
25570
|
+
// value is what made the welcome card show a previous service.
|
|
25571
|
+
models: { main: mainModel, subagent: childModel },
|
|
25572
|
+
permissionMode: harness.permissionMode,
|
|
25573
|
+
memory: memoryLifecycle,
|
|
25574
|
+
timeline: { version: 1, state: timelineState }
|
|
25575
|
+
};
|
|
25576
|
+
};
|
|
25436
25577
|
let persistFailed = false;
|
|
25437
25578
|
const persist = () => {
|
|
25438
25579
|
persistTail = persistTail.catch(() => void 0).then(
|
|
@@ -25939,7 +26080,7 @@ async function createProductionRuntime(options) {
|
|
|
25939
26080
|
evolveService.toolDefinition(),
|
|
25940
26081
|
...mcpTools
|
|
25941
26082
|
];
|
|
25942
|
-
const loopFlavor = await optionalText2(
|
|
26083
|
+
const loopFlavor = await optionalText2(join34(input.workspace, "FLAVOR.md"));
|
|
25943
26084
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
25944
26085
|
const loopEnvironment = createPromptEnvironment({
|
|
25945
26086
|
now: /* @__PURE__ */ new Date(),
|
|
@@ -26184,7 +26325,7 @@ async function createProductionRuntime(options) {
|
|
|
26184
26325
|
contextEpoch: harness.main.context.snapshot().epoch?.id ?? "legacy"
|
|
26185
26326
|
});
|
|
26186
26327
|
const turnId = harnessJournal.startTurn(turnConfig, { prompt, initialUserMessage: runOptions?.initialUserMessage });
|
|
26187
|
-
return durableTurn(persistAndCheckpointAfter(runMain(
|
|
26328
|
+
return monitorMemoryRestart(durableTurn(persistAndCheckpointAfter(runMain(
|
|
26188
26329
|
harness,
|
|
26189
26330
|
skills,
|
|
26190
26331
|
prompt,
|
|
@@ -26204,7 +26345,7 @@ async function createProductionRuntime(options) {
|
|
|
26204
26345
|
prompt,
|
|
26205
26346
|
context: harness.main.context.snapshot(),
|
|
26206
26347
|
label: `turn: ${prompt.slice(0, 80)}`
|
|
26207
|
-
})), harnessJournal, turnId, turnConfig);
|
|
26348
|
+
})), harnessJournal, turnId, turnConfig), () => requestMemoryRestart(workspace, sessionId, emitOutput));
|
|
26208
26349
|
},
|
|
26209
26350
|
runSkill: (skill, prompt, signal) => persistAfter(
|
|
26210
26351
|
runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
|
|
@@ -26504,7 +26645,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
26504
26645
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
26505
26646
|
}
|
|
26506
26647
|
try {
|
|
26507
|
-
const tokenStore = createFileTokenStore(
|
|
26648
|
+
const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
26508
26649
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
26509
26650
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
26510
26651
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -26551,7 +26692,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
26551
26692
|
}
|
|
26552
26693
|
},
|
|
26553
26694
|
async logout() {
|
|
26554
|
-
const tokenStore = createFileTokenStore(
|
|
26695
|
+
const tokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
26555
26696
|
const tokens2 = await tokenStore.load();
|
|
26556
26697
|
const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
|
|
26557
26698
|
if (!hadCredentials) {
|
|
@@ -26953,6 +27094,31 @@ async function* durableTurn(source, journal, turnId, config) {
|
|
|
26953
27094
|
}
|
|
26954
27095
|
}
|
|
26955
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
|
+
}
|
|
26956
27122
|
async function runGitCommit(deps, hint, signal) {
|
|
26957
27123
|
if (!await isGitRepository(deps.workspace)) return "Not a git repository \u2014 /commit needs git.";
|
|
26958
27124
|
let summary2 = await changeSummary(deps.workspace);
|
|
@@ -27046,7 +27212,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
27046
27212
|
const configured = { ...providers };
|
|
27047
27213
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
27048
27214
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
27049
|
-
const oauthTokenStore = createFileTokenStore(
|
|
27215
|
+
const oauthTokenStore = createFileTokenStore(join34(home, ".flavor-code", "auth.json"));
|
|
27050
27216
|
const registered = [];
|
|
27051
27217
|
let effectiveLlm;
|
|
27052
27218
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -27479,11 +27645,11 @@ function createMemoryTaskId() {
|
|
|
27479
27645
|
import { createHash as createHash27 } from "crypto";
|
|
27480
27646
|
import { mkdir as mkdir27, readFile as readFile35, readdir as readdir12, rm as rm15, stat as stat15, writeFile as writeFile17 } from "fs/promises";
|
|
27481
27647
|
import { homedir as homedir4 } from "os";
|
|
27482
|
-
import { basename as basename9, join as
|
|
27648
|
+
import { basename as basename9, join as join44, resolve as resolve36 } from "path";
|
|
27483
27649
|
|
|
27484
27650
|
// src/d2c/workflow.ts
|
|
27485
27651
|
import { createHash as createHash22 } from "crypto";
|
|
27486
|
-
import { join as
|
|
27652
|
+
import { join as join35 } from "path";
|
|
27487
27653
|
import { z as z42 } from "zod";
|
|
27488
27654
|
|
|
27489
27655
|
// src/d2c/workflow-shared.ts
|
|
@@ -27629,7 +27795,7 @@ function reviewsFor(report, previous) {
|
|
|
27629
27795
|
function implementationDirectory(report) {
|
|
27630
27796
|
const source = report.implementation.source;
|
|
27631
27797
|
if (!/^https?:\/\//i.test(source)) return source;
|
|
27632
|
-
return
|
|
27798
|
+
return join35("src", "d2c-output", report.task);
|
|
27633
27799
|
}
|
|
27634
27800
|
function createWorkflow(report, framework) {
|
|
27635
27801
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -27753,7 +27919,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
|
|
|
27753
27919
|
};
|
|
27754
27920
|
}
|
|
27755
27921
|
function workflowPath(workspace, task) {
|
|
27756
|
-
return
|
|
27922
|
+
return join35(taskDir(workspace, task), "workflow.json");
|
|
27757
27923
|
}
|
|
27758
27924
|
async function readWorkflow(workspace, task) {
|
|
27759
27925
|
return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
|
|
@@ -28007,7 +28173,7 @@ function confirmApiMapping(mapping, operationKey, operations) {
|
|
|
28007
28173
|
|
|
28008
28174
|
// src/d2c/modules.ts
|
|
28009
28175
|
import { readFile as readFile28 } from "fs/promises";
|
|
28010
|
-
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";
|
|
28011
28177
|
import { z as z43 } from "zod";
|
|
28012
28178
|
var ModuleSchema = z43.object({
|
|
28013
28179
|
id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
@@ -28026,13 +28192,13 @@ function safeSource(path) {
|
|
|
28026
28192
|
}
|
|
28027
28193
|
function d2cOutputDirectory(workspace, task) {
|
|
28028
28194
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
|
|
28029
|
-
return
|
|
28195
|
+
return join36(workspace, "src", "d2c-output", task);
|
|
28030
28196
|
}
|
|
28031
28197
|
async function readD2cModules(workspace, task) {
|
|
28032
28198
|
const project = d2cOutputDirectory(workspace, task);
|
|
28033
28199
|
let raw;
|
|
28034
28200
|
try {
|
|
28035
|
-
raw = await readFile28(
|
|
28201
|
+
raw = await readFile28(join36(project, "d2c.modules.json"), "utf8");
|
|
28036
28202
|
} catch {
|
|
28037
28203
|
return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
|
|
28038
28204
|
}
|
|
@@ -28053,7 +28219,7 @@ async function readD2cModules(workspace, task) {
|
|
|
28053
28219
|
|
|
28054
28220
|
// src/d2c/integration.ts
|
|
28055
28221
|
import { mkdir as mkdir24, readFile as readFile29, writeFile as writeFile13 } from "fs/promises";
|
|
28056
|
-
import { join as
|
|
28222
|
+
import { join as join37 } from "path";
|
|
28057
28223
|
function record2(value) {
|
|
28058
28224
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
28059
28225
|
}
|
|
@@ -28189,7 +28355,7 @@ async def reset_acceptance(request: Request):
|
|
|
28189
28355
|
}
|
|
28190
28356
|
async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
|
|
28191
28357
|
if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
|
|
28192
|
-
const packagePath =
|
|
28358
|
+
const packagePath = join37(projectDir, "package.json");
|
|
28193
28359
|
let pkg;
|
|
28194
28360
|
try {
|
|
28195
28361
|
pkg = JSON.parse(await readFile29(packagePath, "utf8"));
|
|
@@ -28199,9 +28365,9 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
28199
28365
|
const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
|
|
28200
28366
|
const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
|
|
28201
28367
|
const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
|
|
28202
|
-
const apiDir =
|
|
28203
|
-
const mockDir =
|
|
28204
|
-
const serverDir =
|
|
28368
|
+
const apiDir = join37(projectDir, "src", "api");
|
|
28369
|
+
const mockDir = join37(projectDir, "mock");
|
|
28370
|
+
const serverDir = join37(projectDir, "server");
|
|
28205
28371
|
await Promise.all([
|
|
28206
28372
|
mkdir24(apiDir, { recursive: true }),
|
|
28207
28373
|
...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
|
|
@@ -28211,23 +28377,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
28211
28377
|
if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
|
|
28212
28378
|
else files.push("mock/server.mjs");
|
|
28213
28379
|
await Promise.all([
|
|
28214
|
-
writeFile13(
|
|
28380
|
+
writeFile13(join37(apiDir, "http.js"), `import axios from "axios";
|
|
28215
28381
|
|
|
28216
28382
|
export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
|
|
28217
28383
|
|
|
28218
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 }));
|
|
28219
28385
|
`),
|
|
28220
|
-
writeFile13(
|
|
28221
|
-
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)}
|
|
28222
28388
|
`),
|
|
28223
|
-
...options.pythonServer ? [] : [writeFile13(
|
|
28389
|
+
...options.pythonServer ? [] : [writeFile13(join37(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
|
|
28224
28390
|
writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
|
|
28225
28391
|
`),
|
|
28226
28392
|
...options.pythonServer ? [
|
|
28227
|
-
writeFile13(
|
|
28228
|
-
writeFile13(
|
|
28229
|
-
writeFile13(
|
|
28230
|
-
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({
|
|
28231
28397
|
schema: 1,
|
|
28232
28398
|
kind: "python-fastapi",
|
|
28233
28399
|
cwd: "server",
|
|
@@ -28248,7 +28414,7 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
|
|
|
28248
28414
|
import { spawn as spawn8 } from "child_process";
|
|
28249
28415
|
import { access as access3, readFile as readFile30 } from "fs/promises";
|
|
28250
28416
|
import { createServer as createServer5 } from "net";
|
|
28251
|
-
import { join as
|
|
28417
|
+
import { join as join38 } from "path";
|
|
28252
28418
|
async function exists3(path) {
|
|
28253
28419
|
try {
|
|
28254
28420
|
await access3(path);
|
|
@@ -28356,7 +28522,7 @@ ${output.trim()}` : ""}`);
|
|
|
28356
28522
|
}
|
|
28357
28523
|
}
|
|
28358
28524
|
async function runPythonBackend(projectDir, options) {
|
|
28359
|
-
const serverDir =
|
|
28525
|
+
const serverDir = join38(projectDir, "server");
|
|
28360
28526
|
if (options.installDependencies !== false) await installPython(serverDir, options.signal);
|
|
28361
28527
|
const selectedPort = await port();
|
|
28362
28528
|
const url = `http://127.0.0.1:${selectedPort}`;
|
|
@@ -28402,7 +28568,7 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28402
28568
|
};
|
|
28403
28569
|
}
|
|
28404
28570
|
async function runD2cMockServer(projectDir, options = {}) {
|
|
28405
|
-
const runtimePath =
|
|
28571
|
+
const runtimePath = join38(projectDir, "server", "flavor-runtime.json");
|
|
28406
28572
|
if (await exists3(runtimePath)) {
|
|
28407
28573
|
let runtime;
|
|
28408
28574
|
try {
|
|
@@ -28413,9 +28579,9 @@ async function runD2cMockServer(projectDir, options = {}) {
|
|
|
28413
28579
|
if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
|
|
28414
28580
|
return runPythonBackend(projectDir, options);
|
|
28415
28581
|
}
|
|
28416
|
-
const serverPath =
|
|
28582
|
+
const serverPath = join38(projectDir, "mock", "server.mjs");
|
|
28417
28583
|
if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
|
|
28418
|
-
if (options.installDependencies === false && !await exists3(
|
|
28584
|
+
if (options.installDependencies === false && !await exists3(join38(projectDir, "node_modules", "express", "package.json"))) {
|
|
28419
28585
|
throw new Error("D2C mock server dependencies are not installed");
|
|
28420
28586
|
}
|
|
28421
28587
|
if (options.installDependencies !== false) await install(projectDir, options.signal);
|
|
@@ -28458,7 +28624,7 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28458
28624
|
// src/d2c/product.ts
|
|
28459
28625
|
import { createHash as createHash24, randomUUID as randomUUID27 } from "crypto";
|
|
28460
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";
|
|
28461
|
-
import { join as
|
|
28627
|
+
import { join as join39 } from "path";
|
|
28462
28628
|
import { z as z44 } from "zod";
|
|
28463
28629
|
|
|
28464
28630
|
// src/e2e/prd-governance.ts
|
|
@@ -28613,13 +28779,13 @@ var PlanSchema2 = z44.object({
|
|
|
28613
28779
|
}).strict();
|
|
28614
28780
|
function d2cProductDirectory(workspace, task) {
|
|
28615
28781
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
|
|
28616
|
-
return
|
|
28782
|
+
return join39(taskDir(workspace, task), "product");
|
|
28617
28783
|
}
|
|
28618
28784
|
function d2cProductPrototypeDirectory(workspace, task) {
|
|
28619
|
-
return
|
|
28785
|
+
return join39(d2cProductDirectory(workspace, task), "prototype");
|
|
28620
28786
|
}
|
|
28621
28787
|
function planPath(workspace, task) {
|
|
28622
|
-
return
|
|
28788
|
+
return join39(taskDir(workspace, task), "product-plan.json");
|
|
28623
28789
|
}
|
|
28624
28790
|
function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
|
|
28625
28791
|
const frontendCandidates = [
|
|
@@ -28802,16 +28968,16 @@ async function readD2cProductPlan(workspace, task) {
|
|
|
28802
28968
|
}
|
|
28803
28969
|
async function discoverD2cProductArtifacts(workspace, plan) {
|
|
28804
28970
|
const product = d2cProductDirectory(workspace, plan.task);
|
|
28805
|
-
const prdUpdatedAt = await fileTimestamp(
|
|
28806
|
-
const prdHash = await fileHash(
|
|
28807
|
-
const prototypeUpdatedAt = await fileTimestamp(
|
|
28808
|
-
const prototypeHash = await fileHash(
|
|
28809
|
-
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");
|
|
28810
28976
|
let manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
28811
28977
|
if (manifestUpdatedAt === void 0) {
|
|
28812
|
-
const misplacedManifest =
|
|
28978
|
+
const misplacedManifest = join39(product, "interaction-manifest.json");
|
|
28813
28979
|
if (await fileTimestamp(misplacedManifest) !== void 0) {
|
|
28814
|
-
await mkdir25(
|
|
28980
|
+
await mkdir25(join39(product, "prototype"), { recursive: true });
|
|
28815
28981
|
await copyFile2(misplacedManifest, expectedManifest);
|
|
28816
28982
|
manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
28817
28983
|
}
|
|
@@ -28844,8 +29010,8 @@ async function readD2cProductPlanView(workspace, task) {
|
|
|
28844
29010
|
if (stored === void 0) return void 0;
|
|
28845
29011
|
const plan = await discoverD2cProductArtifacts(workspace, stored);
|
|
28846
29012
|
if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
|
|
28847
|
-
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile32(
|
|
28848
|
-
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");
|
|
28849
29015
|
const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
|
|
28850
29016
|
return {
|
|
28851
29017
|
plan,
|
|
@@ -29004,7 +29170,7 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
|
|
|
29004
29170
|
|
|
29005
29171
|
// src/e2e/delivery-run.ts
|
|
29006
29172
|
import { createHash as createHash26 } from "crypto";
|
|
29007
|
-
import { join as
|
|
29173
|
+
import { join as join40 } from "path";
|
|
29008
29174
|
import { z as z45 } from "zod";
|
|
29009
29175
|
var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
|
|
29010
29176
|
var ArtifactSchema = z45.object({
|
|
@@ -29121,7 +29287,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
|
|
|
29121
29287
|
return { ...run, nodes, updatedAt: timestamp2 };
|
|
29122
29288
|
}
|
|
29123
29289
|
function deliveryRunPath(workspace, task) {
|
|
29124
|
-
return
|
|
29290
|
+
return join40(taskDir(workspace, task), "delivery-run.json");
|
|
29125
29291
|
}
|
|
29126
29292
|
function decodeDeliveryRun(raw) {
|
|
29127
29293
|
return DeliveryRunSchema.parse(JSON.parse(raw));
|
|
@@ -29164,7 +29330,7 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
|
|
|
29164
29330
|
// src/skills/manager.ts
|
|
29165
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";
|
|
29166
29332
|
import { homedir as homedir3 } from "os";
|
|
29167
|
-
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";
|
|
29168
29334
|
import { stringify } from "yaml";
|
|
29169
29335
|
var SkillManager = class {
|
|
29170
29336
|
#workspace;
|
|
@@ -29187,7 +29353,7 @@ var SkillManager = class {
|
|
|
29187
29353
|
async create(draft) {
|
|
29188
29354
|
const value = parseDraft(draft);
|
|
29189
29355
|
const projectRoot = this.#projectRoot();
|
|
29190
|
-
const directory =
|
|
29356
|
+
const directory = join41(projectRoot, value.name);
|
|
29191
29357
|
await mkdir26(projectRoot, { recursive: true, mode: 448 });
|
|
29192
29358
|
try {
|
|
29193
29359
|
await mkdir26(directory, { mode: 448 });
|
|
@@ -29196,7 +29362,7 @@ var SkillManager = class {
|
|
|
29196
29362
|
throw error;
|
|
29197
29363
|
}
|
|
29198
29364
|
try {
|
|
29199
|
-
await writeFile16(
|
|
29365
|
+
await writeFile16(join41(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
29200
29366
|
await this.#persistEnabled(value.name, true);
|
|
29201
29367
|
return this.get(value.name);
|
|
29202
29368
|
} catch (error) {
|
|
@@ -29214,7 +29380,7 @@ var SkillManager = class {
|
|
|
29214
29380
|
throw error;
|
|
29215
29381
|
});
|
|
29216
29382
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
|
|
29217
|
-
const skillFile =
|
|
29383
|
+
const skillFile = join41(directory, "SKILL.md");
|
|
29218
29384
|
const fileInfo = await lstat13(skillFile);
|
|
29219
29385
|
if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
|
|
29220
29386
|
await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
|
|
@@ -29246,12 +29412,12 @@ var SkillManager = class {
|
|
|
29246
29412
|
async #registry() {
|
|
29247
29413
|
const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
|
|
29248
29414
|
const [globalPluginRoots, projectPluginRoots] = await Promise.all([
|
|
29249
|
-
discoverPluginSkillRoots(
|
|
29250
|
-
discoverPluginSkillRoots(
|
|
29415
|
+
discoverPluginSkillRoots(join41(this.#home, ".flavor-code", "plugins")),
|
|
29416
|
+
discoverPluginSkillRoots(join41(this.#workspace, ".flavor", "plugins"))
|
|
29251
29417
|
]);
|
|
29252
29418
|
return {
|
|
29253
29419
|
registry: new SkillRegistry({
|
|
29254
|
-
globalRoots: [
|
|
29420
|
+
globalRoots: [join41(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
|
|
29255
29421
|
projectRoots: [this.#projectRoot(), ...projectPluginRoots]
|
|
29256
29422
|
}),
|
|
29257
29423
|
disabled: new Set(loaded.config.skills.disabled)
|
|
@@ -29267,10 +29433,10 @@ var SkillManager = class {
|
|
|
29267
29433
|
};
|
|
29268
29434
|
}
|
|
29269
29435
|
#projectRoot() {
|
|
29270
|
-
return
|
|
29436
|
+
return join41(this.#workspace, ".flavor", "skills");
|
|
29271
29437
|
}
|
|
29272
29438
|
#projectSkillDirectory(name) {
|
|
29273
|
-
return
|
|
29439
|
+
return join41(this.#projectRoot(), name);
|
|
29274
29440
|
}
|
|
29275
29441
|
};
|
|
29276
29442
|
function parseName(value) {
|
|
@@ -29311,9 +29477,9 @@ async function discoverPluginSkillRoots(directory) {
|
|
|
29311
29477
|
const roots = [];
|
|
29312
29478
|
for (const entry2 of entries) {
|
|
29313
29479
|
if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
|
|
29314
|
-
const pluginRoot = await realpath13(
|
|
29480
|
+
const pluginRoot = await realpath13(join41(directory, entry2.name));
|
|
29315
29481
|
try {
|
|
29316
|
-
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(
|
|
29482
|
+
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile34(join41(pluginRoot, "flavor-plugin.json"), "utf8")));
|
|
29317
29483
|
for (const contribution of manifest.contributes.skillRoots) {
|
|
29318
29484
|
const candidate = resolve34(pluginRoot, contribution.path);
|
|
29319
29485
|
const info = await lstat13(candidate);
|
|
@@ -29388,11 +29554,11 @@ async function createProjectMemoryManager(options) {
|
|
|
29388
29554
|
}
|
|
29389
29555
|
|
|
29390
29556
|
// src/mcp/config-manager.ts
|
|
29391
|
-
import { join as
|
|
29557
|
+
import { join as join42 } from "path";
|
|
29392
29558
|
var ProjectMcpConfigManager = class {
|
|
29393
29559
|
path;
|
|
29394
29560
|
constructor(workspace) {
|
|
29395
|
-
this.path =
|
|
29561
|
+
this.path = join42(workspace, ".flavor", "flavor.json");
|
|
29396
29562
|
}
|
|
29397
29563
|
async list() {
|
|
29398
29564
|
const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
|
|
@@ -29489,7 +29655,7 @@ function isRecord7(value) {
|
|
|
29489
29655
|
}
|
|
29490
29656
|
|
|
29491
29657
|
// src/desktop/model-config.ts
|
|
29492
|
-
import { join as
|
|
29658
|
+
import { join as join43 } from "path";
|
|
29493
29659
|
var DEFAULT_DESKTOP_MODELS = [
|
|
29494
29660
|
{
|
|
29495
29661
|
id: "anthropic:deepseek-v4-pro",
|
|
@@ -29510,7 +29676,7 @@ var DEFAULT_DESKTOP_MODELS = [
|
|
|
29510
29676
|
];
|
|
29511
29677
|
async function loadDesktopModels(workspace, home) {
|
|
29512
29678
|
try {
|
|
29513
|
-
const tokens2 = await createFileTokenStore(
|
|
29679
|
+
const tokens2 = await createFileTokenStore(join43(home, ".flavor-code", "auth.json")).load();
|
|
29514
29680
|
const pkceModels = [];
|
|
29515
29681
|
const knownPkce = /* @__PURE__ */ new Set();
|
|
29516
29682
|
for (const token of Object.values(tokens2)) {
|
|
@@ -29655,33 +29821,33 @@ function pngDataUrl(png) {
|
|
|
29655
29821
|
}
|
|
29656
29822
|
async function detectD2cFramework(workspace, task) {
|
|
29657
29823
|
try {
|
|
29658
|
-
const pkg = JSON.parse(await readFile35(
|
|
29824
|
+
const pkg = JSON.parse(await readFile35(join44(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
|
|
29659
29825
|
return pkg.dependencies?.react === void 0 ? "vue" : "react";
|
|
29660
29826
|
} catch {
|
|
29661
29827
|
return "vue";
|
|
29662
29828
|
}
|
|
29663
29829
|
}
|
|
29664
29830
|
function integrationDirectory(workspace, task) {
|
|
29665
|
-
return
|
|
29831
|
+
return join44(workspace, ".flavor", "d2c", task, "integration");
|
|
29666
29832
|
}
|
|
29667
29833
|
var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
|
|
29668
29834
|
var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
|
|
29669
29835
|
async function d2cBackendSourceFingerprint(project) {
|
|
29670
|
-
const runtimeManifest =
|
|
29836
|
+
const runtimeManifest = join44(project, "server", "flavor-runtime.json");
|
|
29671
29837
|
const realBackend = (await stat15(runtimeManifest).catch(() => void 0))?.isFile() === true;
|
|
29672
|
-
const root = realBackend ?
|
|
29838
|
+
const root = realBackend ? join44(project, "server") : join44(project, "mock");
|
|
29673
29839
|
if ((await stat15(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
29674
29840
|
const files = [];
|
|
29675
29841
|
const visit = async (directory) => {
|
|
29676
29842
|
const entries = await readdir12(directory, { withFileTypes: true });
|
|
29677
29843
|
for (const entry2 of entries) {
|
|
29678
29844
|
if (entry2.isDirectory()) {
|
|
29679
|
-
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));
|
|
29680
29846
|
continue;
|
|
29681
29847
|
}
|
|
29682
29848
|
if (!entry2.isFile()) continue;
|
|
29683
29849
|
const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
|
|
29684
|
-
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(
|
|
29850
|
+
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join44(directory, entry2.name));
|
|
29685
29851
|
}
|
|
29686
29852
|
};
|
|
29687
29853
|
await visit(root);
|
|
@@ -30235,7 +30401,7 @@ var DesktopRuntimeController = class {
|
|
|
30235
30401
|
if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
|
|
30236
30402
|
throw new Error("Complete visual review before importing OpenAPI");
|
|
30237
30403
|
}
|
|
30238
|
-
const moduleManifest =
|
|
30404
|
+
const moduleManifest = join44(d2cOutputDirectory(workspace, task), "d2c.modules.json");
|
|
30239
30405
|
const moduleManifestInfo = await stat15(moduleManifest).catch(() => void 0);
|
|
30240
30406
|
if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
|
|
30241
30407
|
throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
|
|
@@ -30248,8 +30414,8 @@ var DesktopRuntimeController = class {
|
|
|
30248
30414
|
await mkdir27(dir, { recursive: true });
|
|
30249
30415
|
const hash = createHash27("sha256").update(raw).digest("hex");
|
|
30250
30416
|
await Promise.all([
|
|
30251
|
-
writeFile17(
|
|
30252
|
-
writeFile17(
|
|
30417
|
+
writeFile17(join44(dir, "swagger.json"), raw),
|
|
30418
|
+
writeFile17(join44(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
|
|
30253
30419
|
`)
|
|
30254
30420
|
]);
|
|
30255
30421
|
const next = {
|
|
@@ -30284,7 +30450,7 @@ var DesktopRuntimeController = class {
|
|
|
30284
30450
|
let workflow = await readWorkflow(workspace, task);
|
|
30285
30451
|
if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
|
|
30286
30452
|
const modules = await readD2cModules(workspace, task);
|
|
30287
|
-
const productOpenApiPath =
|
|
30453
|
+
const productOpenApiPath = join44(taskDir(workspace, task), "product", "openapi.json");
|
|
30288
30454
|
const authored = await readFile35(productOpenApiPath, "utf8").catch(() => void 0);
|
|
30289
30455
|
const generated = authored === void 0;
|
|
30290
30456
|
const prepared = await this.#storeD2cOpenApi(
|
|
@@ -30297,7 +30463,7 @@ var DesktopRuntimeController = class {
|
|
|
30297
30463
|
return prepared;
|
|
30298
30464
|
}
|
|
30299
30465
|
if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
|
|
30300
|
-
const document = JSON.parse(await readFile35(
|
|
30466
|
+
const document = JSON.parse(await readFile35(join44(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
|
|
30301
30467
|
return { workflow, document, mappings: workflow.mappings };
|
|
30302
30468
|
}
|
|
30303
30469
|
async confirmD2cMapping(task, moduleId, operationKey) {
|
|
@@ -30364,7 +30530,7 @@ var DesktopRuntimeController = class {
|
|
|
30364
30530
|
].join("\n");
|
|
30365
30531
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30366
30532
|
if (delivery?.nodes.api.status === "running") {
|
|
30367
|
-
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)))));
|
|
30368
30534
|
const completed = await updateDeliveryRun(
|
|
30369
30535
|
workspace,
|
|
30370
30536
|
task,
|
|
@@ -30391,7 +30557,7 @@ var DesktopRuntimeController = class {
|
|
|
30391
30557
|
try {
|
|
30392
30558
|
const fingerprint2 = await d2cBackendSourceFingerprint(project);
|
|
30393
30559
|
if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
|
|
30394
|
-
const environmentPath =
|
|
30560
|
+
const environmentPath = join44(project, ".env.local");
|
|
30395
30561
|
const existingEnvironment = await readFile35(environmentPath, "utf8").catch(() => "");
|
|
30396
30562
|
const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
|
|
30397
30563
|
VITE_API_BASE_URL=${running.url}
|
|
@@ -30472,7 +30638,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30472
30638
|
const readyPreview = this.#d2cPreviews.get(task);
|
|
30473
30639
|
const readyMock = this.#d2cMocks.get(task);
|
|
30474
30640
|
if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
|
|
30475
|
-
const manifestPath =
|
|
30641
|
+
const manifestPath = join44(taskDir(workspace, task), "design", "interaction-manifest.json");
|
|
30476
30642
|
const manifest = parseInteractionManifest(await readFile35(manifestPath, "utf8"));
|
|
30477
30643
|
const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
|
|
30478
30644
|
const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
|
|
@@ -30484,7 +30650,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30484
30650
|
await mkdir27(planDir, { recursive: true });
|
|
30485
30651
|
try {
|
|
30486
30652
|
const observations = await this.#observeD2cPages(manifest, readyPreview.url);
|
|
30487
|
-
const prd = await readFile35(
|
|
30653
|
+
const prd = await readFile35(join44(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
|
|
30488
30654
|
const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
|
|
30489
30655
|
const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
|
|
30490
30656
|
executableManifest = mergeInteractionManifests(manifest, plan.manifest);
|
|
@@ -30495,9 +30661,9 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30495
30661
|
plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
30496
30662
|
pageAnalyses: plan.pageAnalyses
|
|
30497
30663
|
};
|
|
30498
|
-
await writeFile17(
|
|
30664
|
+
await writeFile17(join44(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
|
|
30499
30665
|
`, { encoding: "utf8", mode: 384 });
|
|
30500
|
-
await rm15(
|
|
30666
|
+
await rm15(join44(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
|
|
30501
30667
|
} catch (cause) {
|
|
30502
30668
|
const detail = message(cause);
|
|
30503
30669
|
review = {
|
|
@@ -30505,8 +30671,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30505
30671
|
plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
30506
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}`
|
|
30507
30673
|
};
|
|
30508
|
-
await rm15(
|
|
30509
|
-
await writeFile17(
|
|
30674
|
+
await rm15(join44(planDir, "autonomous-interaction-plan.json"), { force: true });
|
|
30675
|
+
await writeFile17(join44(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
|
|
30510
30676
|
schema: 1,
|
|
30511
30677
|
stage: "planning",
|
|
30512
30678
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -30520,13 +30686,13 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30520
30686
|
let result = { ...rawResult, evidenceMode: review.mode };
|
|
30521
30687
|
if (coverage !== void 0) {
|
|
30522
30688
|
const evidence = createAcceptanceEvidence(coverage, result);
|
|
30523
|
-
await writeFile17(
|
|
30689
|
+
await writeFile17(join44(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
|
|
30524
30690
|
`, { encoding: "utf8", mode: 384 });
|
|
30525
30691
|
if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
|
|
30526
30692
|
}
|
|
30527
30693
|
const dir = integrationDirectory(workspace, task);
|
|
30528
30694
|
await mkdir27(dir, { recursive: true });
|
|
30529
|
-
await writeFile17(
|
|
30695
|
+
await writeFile17(join44(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
|
|
30530
30696
|
`);
|
|
30531
30697
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
|
|
30532
30698
|
return { workflow: next, result, review };
|
|
@@ -30539,7 +30705,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30539
30705
|
if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
|
|
30540
30706
|
const output = mock.output().trim();
|
|
30541
30707
|
try {
|
|
30542
|
-
const dir =
|
|
30708
|
+
const dir = join44(d2cOutputDirectory(workspace, task), "mock-server.log");
|
|
30543
30709
|
await writeFile17(dir, `${output}
|
|
30544
30710
|
`);
|
|
30545
30711
|
} catch {
|
|
@@ -30634,7 +30800,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30634
30800
|
if (current === void 0) throw new Error("D2C product plan is unavailable");
|
|
30635
30801
|
if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
|
|
30636
30802
|
if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
|
|
30637
|
-
const path =
|
|
30803
|
+
const path = join44(taskDir(workspace, task), "product", "prd.md");
|
|
30638
30804
|
const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
|
|
30639
30805
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30640
30806
|
await writeD2cProductPlan(workspace, {
|
|
@@ -30654,15 +30820,15 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30654
30820
|
async #assertProductPrdLocked(workspace, task) {
|
|
30655
30821
|
const current = await readD2cProductPlanView(workspace, task);
|
|
30656
30822
|
if (current?.plan.approvedPrd === void 0) return;
|
|
30657
|
-
await assertApprovedPrd(
|
|
30823
|
+
await assertApprovedPrd(join44(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
|
|
30658
30824
|
}
|
|
30659
30825
|
async #acceptancePaths(workspace, task) {
|
|
30660
|
-
const product =
|
|
30661
|
-
const openapi =
|
|
30826
|
+
const product = join44(taskDir(workspace, task), "product");
|
|
30827
|
+
const openapi = join44(product, "openapi.json");
|
|
30662
30828
|
return {
|
|
30663
|
-
prd:
|
|
30664
|
-
prototype:
|
|
30665
|
-
interaction:
|
|
30829
|
+
prd: join44(product, "prd.md"),
|
|
30830
|
+
prototype: join44(product, "prototype", "index.html"),
|
|
30831
|
+
interaction: join44(product, "prototype", "interaction-manifest.json"),
|
|
30666
30832
|
...await stat15(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
|
|
30667
30833
|
};
|
|
30668
30834
|
}
|
|
@@ -30670,17 +30836,17 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30670
30836
|
const product = await readD2cProductPlanView(workspace, task);
|
|
30671
30837
|
if (product?.plan.approvedPrd === void 0) return void 0;
|
|
30672
30838
|
await this.#assertProductPrdLocked(workspace, task);
|
|
30673
|
-
const baseline = JSON.parse(await readFile35(
|
|
30839
|
+
const baseline = JSON.parse(await readFile35(join44(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
|
|
30674
30840
|
await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
|
|
30675
|
-
const design =
|
|
30841
|
+
const design = join44(taskDir(workspace, task), "design");
|
|
30676
30842
|
await Promise.all([
|
|
30677
|
-
verifyAcceptanceArtifact(
|
|
30678
|
-
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")
|
|
30679
30845
|
]);
|
|
30680
30846
|
const workflow = await readWorkflow(workspace, task);
|
|
30681
30847
|
if (workflow?.openapi !== void 0) {
|
|
30682
30848
|
await verifyAcceptanceArtifact(
|
|
30683
|
-
|
|
30849
|
+
join44(taskDir(workspace, task), "integration", "swagger.json"),
|
|
30684
30850
|
{ hash: workflow.openapi.hash },
|
|
30685
30851
|
"runtime OpenAPI contract"
|
|
30686
30852
|
);
|
|
@@ -30701,14 +30867,14 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30701
30867
|
throw new Error("D2C PRD is empty and cannot be accepted");
|
|
30702
30868
|
}
|
|
30703
30869
|
if (stage === "design" && accepted) {
|
|
30704
|
-
const manifest =
|
|
30870
|
+
const manifest = join44(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
|
|
30705
30871
|
const normalizedManifest = parseInteractionManifest(await readFile35(manifest, "utf8"));
|
|
30706
30872
|
if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
|
|
30707
30873
|
verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
|
|
30708
30874
|
await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
|
|
30709
30875
|
`, { encoding: "utf8", mode: 384 });
|
|
30710
30876
|
const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
|
|
30711
|
-
await writeFile17(
|
|
30877
|
+
await writeFile17(join44(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
|
|
30712
30878
|
`, { encoding: "utf8", mode: 384 });
|
|
30713
30879
|
}
|
|
30714
30880
|
const plan = await writeD2cProductPlan(
|
|
@@ -30742,8 +30908,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30742
30908
|
const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
|
|
30743
30909
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30744
30910
|
if (delivery !== void 0) {
|
|
30745
|
-
const prototype = await readFile35(
|
|
30746
|
-
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");
|
|
30747
30913
|
const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
|
|
30748
30914
|
artifactRef("product/prototype/index.html", prototype),
|
|
30749
30915
|
artifactRef("product/prototype/interaction-manifest.json", interaction)
|
|
@@ -30827,10 +30993,10 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30827
30993
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
|
|
30828
30994
|
const qualityContent = `${JSON.stringify(judgment, null, 2)}
|
|
30829
30995
|
`;
|
|
30830
|
-
await writeFile17(
|
|
30996
|
+
await writeFile17(join44(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
|
|
30831
30997
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30832
30998
|
if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
|
|
30833
|
-
const interactionContent = await readFile35(
|
|
30999
|
+
const interactionContent = await readFile35(join44(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
|
|
30834
31000
|
const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
|
|
30835
31001
|
artifactRef("integration/interaction-results.json", interactionContent),
|
|
30836
31002
|
artifactRef("quality-judge.json", qualityContent)
|
|
@@ -31011,15 +31177,15 @@ function desktopWindowChrome(platform = process.platform) {
|
|
|
31011
31177
|
}
|
|
31012
31178
|
|
|
31013
31179
|
// src/utils/crash-guard.ts
|
|
31014
|
-
import { mkdirSync as
|
|
31015
|
-
import { join as
|
|
31180
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
31181
|
+
import { join as join45 } from "path";
|
|
31016
31182
|
var installed = false;
|
|
31017
31183
|
function installCrashGuard(options = {}) {
|
|
31018
31184
|
if (installed) return;
|
|
31019
31185
|
installed = true;
|
|
31020
31186
|
const crashLogDir = () => {
|
|
31021
31187
|
try {
|
|
31022
|
-
return
|
|
31188
|
+
return join45((options.workspace ?? process.cwd)(), ".flavor");
|
|
31023
31189
|
} catch {
|
|
31024
31190
|
return process.cwd();
|
|
31025
31191
|
}
|
|
@@ -31027,9 +31193,9 @@ function installCrashGuard(options = {}) {
|
|
|
31027
31193
|
const writeCrashLog = (kind, detail) => {
|
|
31028
31194
|
try {
|
|
31029
31195
|
const dir = crashLogDir();
|
|
31030
|
-
|
|
31031
|
-
const path =
|
|
31032
|
-
|
|
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, [
|
|
31033
31199
|
`kind: ${kind}`,
|
|
31034
31200
|
`version: ${process.version}`,
|
|
31035
31201
|
`platform: ${process.platform}`,
|
|
@@ -31221,7 +31387,7 @@ function safeGitTarget(value) {
|
|
|
31221
31387
|
// src/desktop/worktree-manager.ts
|
|
31222
31388
|
import { randomUUID as randomUUID28 } from "crypto";
|
|
31223
31389
|
import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat16, writeFile as writeFile18 } from "fs/promises";
|
|
31224
|
-
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";
|
|
31225
31391
|
var DesktopWorktreeManager = class {
|
|
31226
31392
|
#repository;
|
|
31227
31393
|
#storage;
|
|
@@ -31267,7 +31433,7 @@ var DesktopWorktreeManager = class {
|
|
|
31267
31433
|
}
|
|
31268
31434
|
const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
|
|
31269
31435
|
try {
|
|
31270
|
-
const stored = validateStored(JSON.parse(await readFile37(
|
|
31436
|
+
const stored = validateStored(JSON.parse(await readFile37(join46(this.#storage, name), "utf8")), this.#repository, this.#storage);
|
|
31271
31437
|
if ((await stat16(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
31272
31438
|
const status = await git2(stored.path, ["status", "--porcelain"]);
|
|
31273
31439
|
const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
|
|
@@ -31331,7 +31497,7 @@ function validateStored(value, repository, storage) {
|
|
|
31331
31497
|
// src/desktop/workbench-service.ts
|
|
31332
31498
|
import { readFile as readFile38, readdir as readdir14, stat as stat17 } from "fs/promises";
|
|
31333
31499
|
import { homedir as homedir5 } from "os";
|
|
31334
|
-
import { join as
|
|
31500
|
+
import { join as join47, resolve as resolve39 } from "path";
|
|
31335
31501
|
var MAX_TEXT = 128e3;
|
|
31336
31502
|
var MAX_ITEMS = 200;
|
|
31337
31503
|
var DesktopWorkbenchService = class {
|
|
@@ -31364,11 +31530,11 @@ var DesktopWorkbenchService = class {
|
|
|
31364
31530
|
return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
|
|
31365
31531
|
}
|
|
31366
31532
|
async #goals() {
|
|
31367
|
-
const root =
|
|
31533
|
+
const root = join47(this.#workspace, ".flavor", "goals");
|
|
31368
31534
|
const names = await readdir14(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
|
|
31369
31535
|
const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
|
|
31370
31536
|
try {
|
|
31371
|
-
return GoalStateSchema.parse(JSON.parse(await boundedRead2(
|
|
31537
|
+
return GoalStateSchema.parse(JSON.parse(await boundedRead2(join47(root, name))));
|
|
31372
31538
|
} catch {
|
|
31373
31539
|
return void 0;
|
|
31374
31540
|
}
|
|
@@ -31378,7 +31544,7 @@ var DesktopWorkbenchService = class {
|
|
|
31378
31544
|
async #instructions() {
|
|
31379
31545
|
const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
31380
31546
|
const items = await Promise.all(names.map(async (name) => {
|
|
31381
|
-
const path =
|
|
31547
|
+
const path = join47(this.#workspace, name);
|
|
31382
31548
|
try {
|
|
31383
31549
|
return { name, path, content: redactErrorText(await boundedRead2(path)) };
|
|
31384
31550
|
} catch (error) {
|
|
@@ -31391,9 +31557,9 @@ var DesktopWorkbenchService = class {
|
|
|
31391
31557
|
async #permissionFiles() {
|
|
31392
31558
|
const candidates = [
|
|
31393
31559
|
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve39(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
|
|
31394
|
-
{ tier: "user", path:
|
|
31395
|
-
{ tier: "project", path:
|
|
31396
|
-
{ 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: "" }
|
|
31397
31563
|
];
|
|
31398
31564
|
const values = await Promise.all(candidates.map(async (item) => {
|
|
31399
31565
|
try {
|
|
@@ -31408,7 +31574,7 @@ var DesktopWorkbenchService = class {
|
|
|
31408
31574
|
async #audit() {
|
|
31409
31575
|
let raw;
|
|
31410
31576
|
try {
|
|
31411
|
-
raw = await boundedRead2(
|
|
31577
|
+
raw = await boundedRead2(join47(this.#workspace, ".flavor", "audit.jsonl"));
|
|
31412
31578
|
} catch (error) {
|
|
31413
31579
|
if (error.code === "ENOENT") return [];
|
|
31414
31580
|
throw error;
|
|
@@ -31423,7 +31589,7 @@ var DesktopWorkbenchService = class {
|
|
|
31423
31589
|
}).reverse();
|
|
31424
31590
|
}
|
|
31425
31591
|
async #context(sessionId) {
|
|
31426
|
-
const usage = await readJsonLines2(
|
|
31592
|
+
const usage = await readJsonLines2(join47(this.#workspace, ".flavor", "usage.jsonl"));
|
|
31427
31593
|
if (sessionId === void 0) return { visibility: [], usage };
|
|
31428
31594
|
try {
|
|
31429
31595
|
const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
|
|
@@ -31463,14 +31629,14 @@ function redact2(value, key = "") {
|
|
|
31463
31629
|
|
|
31464
31630
|
// src/desktop/main.ts
|
|
31465
31631
|
installCrashGuard();
|
|
31466
|
-
var moduleDirectory =
|
|
31632
|
+
var moduleDirectory = dirname25(fileURLToPath4(import.meta.url));
|
|
31467
31633
|
var developmentUrl = process.env.FLAVOR_DESKTOP_DEV_URL;
|
|
31468
31634
|
var mainWindow;
|
|
31469
31635
|
var appMenu;
|
|
31470
31636
|
var quitting = false;
|
|
31471
31637
|
function logStartup(step, detail) {
|
|
31472
31638
|
try {
|
|
31473
|
-
const logPath =
|
|
31639
|
+
const logPath = join48(dirname25(process.execPath), "flavor-code-startup.log");
|
|
31474
31640
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
31475
31641
|
appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
|
|
31476
31642
|
`);
|
|
@@ -31495,7 +31661,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
|
|
|
31495
31661
|
};
|
|
31496
31662
|
});
|
|
31497
31663
|
var d2cJudgeClient = createD2cJudgeClient();
|
|
31498
|
-
var judgeStore = () => createD2cJudgeConfigStore(
|
|
31664
|
+
var judgeStore = () => createD2cJudgeConfigStore(join48(app.getPath("userData"), "d2c-judge.json"));
|
|
31499
31665
|
function judgeImage(png) {
|
|
31500
31666
|
const image = nativeImage.createFromBuffer(png);
|
|
31501
31667
|
const size = image.getSize();
|
|
@@ -31521,7 +31687,7 @@ function sessionMetaKey(workspace, sessionId) {
|
|
|
31521
31687
|
}
|
|
31522
31688
|
function worktreeManager(workspace) {
|
|
31523
31689
|
const key = createHash28("sha256").update(resolve40(workspace)).digest("hex").slice(0, 20);
|
|
31524
|
-
return new DesktopWorktreeManager({ repository: workspace, storage:
|
|
31690
|
+
return new DesktopWorktreeManager({ repository: workspace, storage: join48(app.getPath("userData"), "task-worktrees", key) });
|
|
31525
31691
|
}
|
|
31526
31692
|
function activeWorkingDirectory() {
|
|
31527
31693
|
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
@@ -31666,7 +31832,7 @@ function createDesktopController(workspace, owner2) {
|
|
|
31666
31832
|
alias: `desktop-${basename11(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
|
|
31667
31833
|
optional: true,
|
|
31668
31834
|
startBroker: (address) => {
|
|
31669
|
-
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");
|
|
31670
31836
|
return spawnPalBrokerProcess({
|
|
31671
31837
|
address,
|
|
31672
31838
|
executable: process.execPath,
|
|
@@ -31688,7 +31854,7 @@ function createDesktopController(workspace, owner2) {
|
|
|
31688
31854
|
var detachedController = createDesktopController();
|
|
31689
31855
|
var controller = detachedController;
|
|
31690
31856
|
function statePath() {
|
|
31691
|
-
return
|
|
31857
|
+
return join48(app.getPath("userData"), "desktop-state.json");
|
|
31692
31858
|
}
|
|
31693
31859
|
async function loadPersistedProjects() {
|
|
31694
31860
|
try {
|
|
@@ -31861,7 +32027,7 @@ async function chooseAndOpenWorkspace() {
|
|
|
31861
32027
|
var cachedAppIconDataUrl;
|
|
31862
32028
|
function appIconDataUrl() {
|
|
31863
32029
|
if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
|
|
31864
|
-
const icon = nativeImage.createFromPath(
|
|
32030
|
+
const icon = nativeImage.createFromPath(join48(app.getAppPath(), "assets", "icon.png"));
|
|
31865
32031
|
if (icon.isEmpty()) return void 0;
|
|
31866
32032
|
cachedAppIconDataUrl = icon.toDataURL();
|
|
31867
32033
|
return cachedAppIconDataUrl;
|
|
@@ -32279,7 +32445,7 @@ function applicationMenu() {
|
|
|
32279
32445
|
]);
|
|
32280
32446
|
}
|
|
32281
32447
|
async function createWindow() {
|
|
32282
|
-
const rendererPath =
|
|
32448
|
+
const rendererPath = join48(app.getAppPath(), "dist", "desktop-renderer", "index.html");
|
|
32283
32449
|
mainWindow = new BrowserWindow2({
|
|
32284
32450
|
title: "Flavor Code",
|
|
32285
32451
|
width: 1280,
|
|
@@ -32290,14 +32456,14 @@ async function createWindow() {
|
|
|
32290
32456
|
show: false,
|
|
32291
32457
|
...desktopWindowChrome(),
|
|
32292
32458
|
webPreferences: {
|
|
32293
|
-
preload:
|
|
32459
|
+
preload: join48(moduleDirectory, "preload.cjs"),
|
|
32294
32460
|
contextIsolation: true,
|
|
32295
32461
|
nodeIntegration: false,
|
|
32296
32462
|
sandbox: false,
|
|
32297
32463
|
webSecurity: true
|
|
32298
32464
|
}
|
|
32299
32465
|
});
|
|
32300
|
-
const iconPath =
|
|
32466
|
+
const iconPath = join48(app.getAppPath(), "assets", "icon.png");
|
|
32301
32467
|
const icon = nativeImage.createFromPath(iconPath);
|
|
32302
32468
|
if (!icon.isEmpty()) {
|
|
32303
32469
|
mainWindow.setIcon(icon);
|