flavor-code 1.3.0 → 1.3.3
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 +5 -3
- package/README.zh-CN.md +5 -3
- package/dist/{app-FOPTLLK5.js → app-NXOGWGSI.js} +1 -1
- package/dist/{chunk-IX65GYUF.js → chunk-JKJOQRAL.js} +1 -1
- package/dist/{chunk-KL4UHJ2Q.js → chunk-YY5H6GFN.js} +88 -33
- package/dist/cli.js +21 -5
- package/dist/desktop/main.js +1363 -178
- package/dist/desktop/pals-broker-entry.js +1308 -0
- package/dist/desktop/preload.cjs +91 -2
- package/dist/desktop-renderer/assets/index-CdeAi527.css +1 -0
- package/dist/desktop-renderer/assets/index-CnGHSdJ6.js +154 -0
- package/dist/desktop-renderer/assets/interactive-terminal-BpNgJ_fv.js +35 -0
- package/dist/desktop-renderer/index.html +3 -3
- package/dist/init/project.d.ts +2 -2
- package/dist/pals/broker-cli.d.ts +8 -0
- package/dist/production.d.ts +8 -3
- package/dist/sdk/index.js +2 -2
- package/dist/ui/session.d.ts +2 -0
- package/package.json +11 -8
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +2 -2
- package/dist/codeisland/activate.mjs +0 -74
- package/dist/codeisland/bridge.mjs +0 -97
- package/dist/codeisland/flavor-plugin.json +0 -27
- package/dist/desktop-renderer/assets/index-BmXr_vX3.js +0 -151
- package/dist/desktop-renderer/assets/index-DEL8gXBO.css +0 -1
package/dist/desktop/main.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src/desktop/main.ts
|
|
2
2
|
import { appendFileSync } from "fs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { createHash as createHash27, randomUUID as randomUUID27 } from "crypto";
|
|
4
|
+
import { readFile as readFile39, stat as stat18, writeFile as writeFile19 } from "fs/promises";
|
|
5
|
+
import { basename as basename11, dirname as dirname24, join as join46, resolve as resolve40 } from "path";
|
|
5
6
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6
|
-
import { app, BrowserWindow as BrowserWindow2, dialog, ipcMain, Menu, nativeImage, shell } from "electron";
|
|
7
|
+
import { app, BrowserWindow as BrowserWindow2, clipboard, dialog, ipcMain, Menu, nativeImage, Notification, shell } from "electron";
|
|
7
8
|
|
|
8
9
|
// src/desktop/contracts.ts
|
|
9
10
|
import { z as z3 } from "zod";
|
|
@@ -450,7 +451,49 @@ var DESKTOP_CHANNELS = {
|
|
|
450
451
|
chooseWorkspace: "desktop:choose-workspace",
|
|
451
452
|
openWorkspace: "desktop:open-workspace",
|
|
452
453
|
startSession: "desktop:start-session",
|
|
454
|
+
selectSession: "desktop:select-session",
|
|
453
455
|
deleteSession: "desktop:delete-session",
|
|
456
|
+
updateSession: "desktop:update-session",
|
|
457
|
+
acknowledgeSession: "desktop:acknowledge-session",
|
|
458
|
+
updateProject: "desktop:update-project",
|
|
459
|
+
closeProject: "desktop:close-project",
|
|
460
|
+
revealProject: "desktop:reveal-project",
|
|
461
|
+
copyProjectPath: "desktop:copy-project-path",
|
|
462
|
+
dismissRecovery: "desktop:dismiss-recovery",
|
|
463
|
+
gitStatus: "desktop:git-status",
|
|
464
|
+
gitDiff: "desktop:git-diff",
|
|
465
|
+
gitStage: "desktop:git-stage",
|
|
466
|
+
gitUnstage: "desktop:git-unstage",
|
|
467
|
+
gitDiscard: "desktop:git-discard",
|
|
468
|
+
gitCommit: "desktop:git-commit",
|
|
469
|
+
gitReview: "desktop:git-review",
|
|
470
|
+
gitHistory: "desktop:git-history",
|
|
471
|
+
listWorktrees: "desktop:list-worktrees",
|
|
472
|
+
removeWorktree: "desktop:remove-worktree",
|
|
473
|
+
mergeWorktree: "desktop:merge-worktree",
|
|
474
|
+
historySnapshot: "desktop:history-snapshot",
|
|
475
|
+
createCheckpoint: "desktop:create-checkpoint",
|
|
476
|
+
rewindHistory: "desktop:rewind-history",
|
|
477
|
+
unrevertHistory: "desktop:unrevert-history",
|
|
478
|
+
forkHistory: "desktop:fork-history",
|
|
479
|
+
terminalOpen: "desktop:terminal-open",
|
|
480
|
+
terminalList: "desktop:terminal-list",
|
|
481
|
+
terminalRead: "desktop:terminal-read",
|
|
482
|
+
terminalWrite: "desktop:terminal-write",
|
|
483
|
+
terminalResize: "desktop:terminal-resize",
|
|
484
|
+
terminalClose: "desktop:terminal-close",
|
|
485
|
+
jobRead: "desktop:job-read",
|
|
486
|
+
previewValidate: "desktop:preview-validate",
|
|
487
|
+
previewOpen: "desktop:preview-open",
|
|
488
|
+
inspectWorkbench: "desktop:inspect-workbench",
|
|
489
|
+
astStatus: "desktop:ast-status",
|
|
490
|
+
astSearch: "desktop:ast-search",
|
|
491
|
+
astRelations: "desktop:ast-relations",
|
|
492
|
+
listPals: "desktop:list-pals",
|
|
493
|
+
sendPalMessage: "desktop:send-pal-message",
|
|
494
|
+
startCoWork: "desktop:start-co-work",
|
|
495
|
+
coWorkStatus: "desktop:co-work-status",
|
|
496
|
+
cancelCoWork: "desktop:cancel-co-work",
|
|
454
497
|
showAppMenu: "desktop:show-app-menu",
|
|
455
498
|
submit: "desktop:submit",
|
|
456
499
|
finishTask: "desktop:finish-task",
|
|
@@ -473,6 +516,7 @@ var DESKTOP_CHANNELS = {
|
|
|
473
516
|
createMemory: "desktop:create-memory",
|
|
474
517
|
updateMemory: "desktop:update-memory",
|
|
475
518
|
deleteMemory: "desktop:delete-memory",
|
|
519
|
+
deleteColdMemory: "desktop:delete-cold-memory",
|
|
476
520
|
switchModel: "desktop:switch-model",
|
|
477
521
|
addModel: "desktop:add-model",
|
|
478
522
|
d2cImport: "desktop:d2c-import",
|
|
@@ -512,11 +556,49 @@ var DESKTOP_CHANNELS = {
|
|
|
512
556
|
// src/desktop/contracts.ts
|
|
513
557
|
var OpenWorkspaceInputSchema = z3.object({ path: z3.string().trim().min(1).max(32768) }).strict();
|
|
514
558
|
var StartSessionInputSchema = z3.object({
|
|
515
|
-
resumeSession: z3.string().trim().min(1).max(128).optional()
|
|
559
|
+
resumeSession: z3.string().trim().min(1).max(128).optional(),
|
|
560
|
+
environment: z3.enum(["local", "worktree"]).optional()
|
|
516
561
|
}).strict();
|
|
517
562
|
var DeleteSessionInputSchema = z3.object({
|
|
518
563
|
sessionId: z3.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/, "Invalid session id")
|
|
519
564
|
}).strict();
|
|
565
|
+
var UpdateSessionInputSchema = DeleteSessionInputSchema.extend({
|
|
566
|
+
title: z3.string().trim().max(120).optional(),
|
|
567
|
+
pinned: z3.boolean().optional(),
|
|
568
|
+
archived: z3.boolean().optional()
|
|
569
|
+
}).strict();
|
|
570
|
+
var UpdateProjectInputSchema = z3.object({
|
|
571
|
+
workspace: z3.string().trim().min(1).max(32768),
|
|
572
|
+
label: z3.string().trim().max(80).optional(),
|
|
573
|
+
pinned: z3.boolean().optional()
|
|
574
|
+
}).strict();
|
|
575
|
+
var ProjectPathInputSchema = z3.object({ workspace: z3.string().trim().min(1).max(32768) }).strict();
|
|
576
|
+
var CloseProjectInputSchema = ProjectPathInputSchema.extend({ force: z3.boolean().optional() }).strict();
|
|
577
|
+
var GitPathInputSchema = z3.object({ path: z3.string().min(1).max(32768) }).strict();
|
|
578
|
+
var GitDiffInputSchema = GitPathInputSchema.extend({ staged: z3.boolean().optional() }).strict();
|
|
579
|
+
var GitCommitInputSchema = z3.object({ message: z3.string().trim().min(1).max(2e4) }).strict();
|
|
580
|
+
var GitReviewInputSchema = z3.object({
|
|
581
|
+
scope: z3.enum(["working", "staged", "commit", "base", "last-turn"]),
|
|
582
|
+
target: z3.string().trim().min(1).max(256).regex(/^[A-Za-z0-9][A-Za-z0-9._/@{}~^:-]*$/).optional(),
|
|
583
|
+
paths: z3.array(z3.string().min(1).max(32768)).max(200).optional()
|
|
584
|
+
}).strict();
|
|
585
|
+
var SafeIdInput = z3.string().trim().min(1).max(512).regex(/^[A-Za-z0-9._:/#@+~-]+$/);
|
|
586
|
+
var WorktreeRemoveInputSchema = z3.object({ id: z3.string().regex(/^worktree-[a-z0-9._-]{1,80}$/), force: z3.boolean().optional() }).strict();
|
|
587
|
+
var HistoryNodeInputSchema = z3.object({ nodeId: SafeIdInput }).strict();
|
|
588
|
+
var CheckpointInputSchema = z3.object({ label: z3.string().trim().min(1).max(160).optional() }).strict();
|
|
589
|
+
var TerminalOpenInputSchema = z3.object({ cwd: z3.string().max(32768).optional(), shell: z3.string().max(1024).optional(), columns: z3.number().int().min(20).max(500).optional(), rows: z3.number().int().min(5).max(300).optional() }).strict();
|
|
590
|
+
var TerminalIdInputSchema = z3.object({ id: z3.string().regex(/^term-[A-Za-z0-9-]{1,80}$/) }).strict();
|
|
591
|
+
var TerminalReadInputSchema = TerminalIdInputSchema.extend({ cursor: z3.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional() }).strict();
|
|
592
|
+
var TerminalWriteInputSchema = TerminalIdInputSchema.extend({ data: z3.string().max(1e5) }).strict();
|
|
593
|
+
var TerminalResizeInputSchema = TerminalIdInputSchema.extend({ columns: z3.number().int().min(20).max(500), rows: z3.number().int().min(5).max(300) }).strict();
|
|
594
|
+
var JobReadInputSchema = z3.object({ id: z3.string().regex(/^job-[A-Za-z0-9-]{1,80}$/), cursor: z3.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional() }).strict();
|
|
595
|
+
var PreviewUrlInputSchema = z3.object({ url: z3.string().trim().min(1).max(2048) }).strict();
|
|
596
|
+
var PalMessageInputSchema = z3.object({ target: SafeIdInput, message: z3.string().trim().min(1).max(5e4), kind: z3.enum(["chat", "task"]) }).strict();
|
|
597
|
+
var CoWorkStartInputSchema = z3.object({ target: SafeIdInput, goal: z3.string().trim().min(1).max(5e4) }).strict();
|
|
598
|
+
var CoWorkStatusInputSchema = z3.object({ coWorkId: SafeIdInput.optional() }).strict();
|
|
599
|
+
var CoWorkCancelInputSchema = z3.object({ coWorkId: SafeIdInput, reason: z3.string().trim().min(1).max(4e3).optional() }).strict();
|
|
600
|
+
var AstSearchInputSchema = z3.object({ query: z3.string().trim().min(1).max(200) }).strict();
|
|
601
|
+
var AstNodeInputSchema = z3.object({ id: SafeIdInput, hops: z3.number().int().min(1).max(4).optional() }).strict();
|
|
520
602
|
var AppMenuInputSchema = z3.object({
|
|
521
603
|
menu: z3.enum(["file", "edit", "view", "help"]),
|
|
522
604
|
x: z3.number().int().min(0).max(32768),
|
|
@@ -1363,7 +1445,7 @@ async function runInteractionManifest(manifest, baseUrl, createDriver) {
|
|
|
1363
1445
|
}
|
|
1364
1446
|
const started = Date.now();
|
|
1365
1447
|
let driver;
|
|
1366
|
-
let
|
|
1448
|
+
let failure2;
|
|
1367
1449
|
let apiRequestCount = 0;
|
|
1368
1450
|
try {
|
|
1369
1451
|
driver = await createDriver({ id: scenario.id, pageUrl });
|
|
@@ -1382,9 +1464,9 @@ async function runInteractionManifest(manifest, baseUrl, createDriver) {
|
|
|
1382
1464
|
throw new Error("No API request was observed; the page is still behaving as a static implementation");
|
|
1383
1465
|
}
|
|
1384
1466
|
} catch (error) {
|
|
1385
|
-
|
|
1467
|
+
failure2 = error instanceof Error ? error.message : String(error);
|
|
1386
1468
|
const diagnostics = driver?.diagnostics?.();
|
|
1387
|
-
if (diagnostics !== void 0)
|
|
1469
|
+
if (diagnostics !== void 0) failure2 = `${failure2} | ${diagnostics}`;
|
|
1388
1470
|
apiRequestCount = driver?.apiRequestCount() ?? 0;
|
|
1389
1471
|
} finally {
|
|
1390
1472
|
await driver?.close().catch(() => void 0);
|
|
@@ -1393,14 +1475,14 @@ async function runInteractionManifest(manifest, baseUrl, createDriver) {
|
|
|
1393
1475
|
scenarios.push({
|
|
1394
1476
|
id: scenario.id,
|
|
1395
1477
|
pageUrl,
|
|
1396
|
-
passed:
|
|
1478
|
+
passed: failure2 === void 0,
|
|
1397
1479
|
durationMs: Date.now() - started,
|
|
1398
1480
|
apiRequestCount,
|
|
1399
|
-
...
|
|
1481
|
+
...failure2 === void 0 ? {} : { failure: failure2 },
|
|
1400
1482
|
...capturedRequests === void 0 ? {} : { requests: capturedRequests }
|
|
1401
1483
|
});
|
|
1402
|
-
if (
|
|
1403
|
-
blockedByAuthentication = { id: scenario.id, failure };
|
|
1484
|
+
if (failure2 !== void 0 && isAuthenticationPrerequisite(scenario)) {
|
|
1485
|
+
blockedByAuthentication = { id: scenario.id, failure: failure2 };
|
|
1404
1486
|
}
|
|
1405
1487
|
}
|
|
1406
1488
|
}
|
|
@@ -1900,7 +1982,7 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
1900
1982
|
const ready = await candidate.executeJavaScript("document.readyState", false).catch(() => void 0);
|
|
1901
1983
|
if (ready === "complete" || ready === "interactive") return candidate;
|
|
1902
1984
|
}
|
|
1903
|
-
await new Promise((
|
|
1985
|
+
await new Promise((resolve41) => setTimeout(resolve41, pollIntervalMs));
|
|
1904
1986
|
}
|
|
1905
1987
|
throw new Error(`D2C embedded preview did not navigate within ${navigationTimeoutMs} ms: ${url}${observedUrl === void 0 ? "" : `; last observed ${observedUrl}`}${observedFrames.length === 0 ? "; no live frames" : `; live frames ${observedFrames.join(", ")}`}`);
|
|
1906
1988
|
};
|
|
@@ -1915,13 +1997,13 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
1915
1997
|
while (Date.now() <= deadline) {
|
|
1916
1998
|
last = await frame(baseUrl).executeJavaScript(assertionScript(step), true);
|
|
1917
1999
|
if (last.passed) return last;
|
|
1918
|
-
await new Promise((
|
|
2000
|
+
await new Promise((resolve41) => setTimeout(resolve41, 50));
|
|
1919
2001
|
}
|
|
1920
2002
|
return last;
|
|
1921
2003
|
};
|
|
1922
2004
|
const settleAfterAction = async (previousUrl) => {
|
|
1923
2005
|
const origin = new URL(previousUrl).origin;
|
|
1924
|
-
await new Promise((
|
|
2006
|
+
await new Promise((resolve41) => setTimeout(resolve41, Math.min(180, Math.max(20, visualDelayMs * 0.25))));
|
|
1925
2007
|
const deadline = Date.now() + Math.min(2e3, navigationTimeoutMs);
|
|
1926
2008
|
while (Date.now() <= deadline) {
|
|
1927
2009
|
const candidate = host().mainFrame.framesInSubtree.find((item) => !item.isDestroyed() && sameOrigin(item.url, origin));
|
|
@@ -1929,7 +2011,7 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
1929
2011
|
const ready = await candidate.executeJavaScript("document.readyState", false).catch(() => void 0);
|
|
1930
2012
|
if (ready === "complete" || ready === "interactive") return candidate;
|
|
1931
2013
|
}
|
|
1932
|
-
await new Promise((
|
|
2014
|
+
await new Promise((resolve41) => setTimeout(resolve41, pollIntervalMs));
|
|
1933
2015
|
}
|
|
1934
2016
|
throw new Error("D2C preview frame was unavailable after an interaction action");
|
|
1935
2017
|
};
|
|
@@ -2016,7 +2098,7 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
2016
2098
|
return result;
|
|
2017
2099
|
},
|
|
2018
2100
|
settle: async () => {
|
|
2019
|
-
await new Promise((
|
|
2101
|
+
await new Promise((resolve41) => setTimeout(resolve41, 150));
|
|
2020
2102
|
requests = await frame(activeUrl).executeJavaScript(`performance.getEntriesByType("resource").filter((entry) => {
|
|
2021
2103
|
try { return ["fetch", "xmlhttprequest"].includes(entry.initiatorType) && new URL(entry.name).origin === ${JSON.stringify(mockOrigin)}; } catch { return false; }
|
|
2022
2104
|
}).length`, false).catch(() => 0);
|
|
@@ -2102,7 +2184,7 @@ function responseText(protocol, value) {
|
|
|
2102
2184
|
}
|
|
2103
2185
|
throw new Error("D2C Judge response did not contain text output");
|
|
2104
2186
|
}
|
|
2105
|
-
function createD2cJudgeClient(fetcher = globalThis.fetch, retryDelay = (ms) => new Promise((
|
|
2187
|
+
function createD2cJudgeClient(fetcher = globalThis.fetch, retryDelay = (ms) => new Promise((resolve41) => setTimeout(resolve41, ms))) {
|
|
2106
2188
|
const request = async (config, prompt, images, maxTokens) => {
|
|
2107
2189
|
const openAi = config.protocol === "openai-compatible";
|
|
2108
2190
|
const imageContent = openAi ? images.map((image) => ({ type: "image_url", image_url: { url: dataUrl(image), detail: "high" } })) : images.map((image) => ({ type: "image", source: { type: "base64", media_type: "image/png", data: image.toString("base64") } }));
|
|
@@ -2461,7 +2543,7 @@ import { z as z7 } from "zod";
|
|
|
2461
2543
|
import { Worker } from "worker_threads";
|
|
2462
2544
|
function comparePngsInWorker(left, right, signal) {
|
|
2463
2545
|
signal?.throwIfAborted();
|
|
2464
|
-
return new Promise((
|
|
2546
|
+
return new Promise((resolve41, reject) => {
|
|
2465
2547
|
const worker = new Worker(new URL("./pixel-worker.js", import.meta.url));
|
|
2466
2548
|
let settled = false;
|
|
2467
2549
|
const finish = (callback) => {
|
|
@@ -2479,7 +2561,7 @@ function comparePngsInWorker(left, right, signal) {
|
|
|
2479
2561
|
finish(() => reject(new Error(value.error ?? "D2C pixel worker failed")));
|
|
2480
2562
|
return;
|
|
2481
2563
|
}
|
|
2482
|
-
finish(() =>
|
|
2564
|
+
finish(() => resolve41({ ...value.result, heatmapPng: Buffer.from(value.result.heatmapPng) }));
|
|
2483
2565
|
});
|
|
2484
2566
|
worker.once("error", (error) => finish(() => reject(error)));
|
|
2485
2567
|
worker.once("exit", (code) => {
|
|
@@ -4349,12 +4431,12 @@ function hasCycle(nodes) {
|
|
|
4349
4431
|
|
|
4350
4432
|
// src/utils/async.ts
|
|
4351
4433
|
function withTimeout(promise, ms) {
|
|
4352
|
-
return new Promise((
|
|
4434
|
+
return new Promise((resolve41, reject) => {
|
|
4353
4435
|
let settled = false;
|
|
4354
4436
|
const timer = setTimeout(() => {
|
|
4355
4437
|
if (settled) return;
|
|
4356
4438
|
settled = true;
|
|
4357
|
-
|
|
4439
|
+
resolve41({ timedOut: true, value: void 0 });
|
|
4358
4440
|
}, ms);
|
|
4359
4441
|
timer.unref?.();
|
|
4360
4442
|
promise.then(
|
|
@@ -4362,7 +4444,7 @@ function withTimeout(promise, ms) {
|
|
|
4362
4444
|
if (!settled) {
|
|
4363
4445
|
settled = true;
|
|
4364
4446
|
clearTimeout(timer);
|
|
4365
|
-
|
|
4447
|
+
resolve41({ timedOut: false, value });
|
|
4366
4448
|
}
|
|
4367
4449
|
},
|
|
4368
4450
|
(error) => {
|
|
@@ -4377,13 +4459,13 @@ function withTimeout(promise, ms) {
|
|
|
4377
4459
|
}
|
|
4378
4460
|
function awaitWithSignal3(promise, signal) {
|
|
4379
4461
|
signal.throwIfAborted();
|
|
4380
|
-
return new Promise((
|
|
4462
|
+
return new Promise((resolve41, reject) => {
|
|
4381
4463
|
const onAbort = () => reject(signal.reason);
|
|
4382
4464
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
4383
4465
|
promise.then(
|
|
4384
4466
|
(value) => {
|
|
4385
4467
|
signal.removeEventListener("abort", onAbort);
|
|
4386
|
-
|
|
4468
|
+
resolve41(value);
|
|
4387
4469
|
},
|
|
4388
4470
|
(error) => {
|
|
4389
4471
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -4504,7 +4586,7 @@ var SubagentScheduler = class {
|
|
|
4504
4586
|
}
|
|
4505
4587
|
}
|
|
4506
4588
|
async #runTask(task, signal) {
|
|
4507
|
-
let
|
|
4589
|
+
let failure2;
|
|
4508
4590
|
let completion;
|
|
4509
4591
|
try {
|
|
4510
4592
|
const start = await this.#options.hooks.emit({
|
|
@@ -4521,14 +4603,14 @@ var SubagentScheduler = class {
|
|
|
4521
4603
|
completion = { id: task.id, result: parsed.data };
|
|
4522
4604
|
break;
|
|
4523
4605
|
}
|
|
4524
|
-
|
|
4606
|
+
failure2 = parsed.success ? new Error(`Subagent result taskId ${parsed.data.taskId} does not match ${task.id}`) : parsed.error;
|
|
4525
4607
|
}
|
|
4526
4608
|
completion ??= {
|
|
4527
4609
|
id: task.id,
|
|
4528
|
-
result: syntheticResult(task, "failed", `Invalid structured result after repair: ${message(
|
|
4610
|
+
result: syntheticResult(task, "failed", `Invalid structured result after repair: ${message(failure2)}`)
|
|
4529
4611
|
};
|
|
4530
4612
|
} catch (error) {
|
|
4531
|
-
|
|
4613
|
+
failure2 = error;
|
|
4532
4614
|
if (!signal.aborted) completion = { id: task.id, result: syntheticResult(task, "failed", message(error)) };
|
|
4533
4615
|
} finally {
|
|
4534
4616
|
try {
|
|
@@ -4538,18 +4620,18 @@ var SubagentScheduler = class {
|
|
|
4538
4620
|
payload: {
|
|
4539
4621
|
taskId: task.id,
|
|
4540
4622
|
status: signal.aborted ? "cancelled" : completion?.result.status ?? "failed",
|
|
4541
|
-
...
|
|
4623
|
+
...failure2 === void 0 ? {} : { error: message(failure2) }
|
|
4542
4624
|
}
|
|
4543
4625
|
});
|
|
4544
4626
|
} catch (stopError) {
|
|
4545
4627
|
if (!signal.aborted) {
|
|
4546
|
-
|
|
4628
|
+
failure2 = stopError;
|
|
4547
4629
|
completion = { id: task.id, result: syntheticResult(task, "failed", message(stopError)) };
|
|
4548
4630
|
}
|
|
4549
4631
|
}
|
|
4550
4632
|
}
|
|
4551
4633
|
if (signal.aborted) throw signal.reason;
|
|
4552
|
-
return completion ?? { id: task.id, result: syntheticResult(task, "failed", message(
|
|
4634
|
+
return completion ?? { id: task.id, result: syntheticResult(task, "failed", message(failure2)) };
|
|
4553
4635
|
}
|
|
4554
4636
|
};
|
|
4555
4637
|
function isReady(task, states) {
|
|
@@ -6292,10 +6374,10 @@ function sanitize(message2, secrets) {
|
|
|
6292
6374
|
}
|
|
6293
6375
|
function waitForRetry(delayMs, signal) {
|
|
6294
6376
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
6295
|
-
return new Promise((
|
|
6377
|
+
return new Promise((resolve41, reject) => {
|
|
6296
6378
|
const timer = setTimeout(() => {
|
|
6297
6379
|
signal?.removeEventListener("abort", onAbort);
|
|
6298
|
-
|
|
6380
|
+
resolve41();
|
|
6299
6381
|
}, delayMs);
|
|
6300
6382
|
const onAbort = () => {
|
|
6301
6383
|
clearTimeout(timer);
|
|
@@ -6897,10 +6979,10 @@ function serializeToolInput(input) {
|
|
|
6897
6979
|
}
|
|
6898
6980
|
function waitForRetry2(delayMs, signal) {
|
|
6899
6981
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
6900
|
-
return new Promise((
|
|
6982
|
+
return new Promise((resolve41, reject) => {
|
|
6901
6983
|
const timer = setTimeout(() => {
|
|
6902
6984
|
signal?.removeEventListener("abort", onAbort);
|
|
6903
|
-
|
|
6985
|
+
resolve41();
|
|
6904
6986
|
}, delayMs);
|
|
6905
6987
|
const onAbort = () => {
|
|
6906
6988
|
clearTimeout(timer);
|
|
@@ -8385,7 +8467,7 @@ var HookBus = class {
|
|
|
8385
8467
|
}
|
|
8386
8468
|
};
|
|
8387
8469
|
function runShellHandler(descriptor, event, signal) {
|
|
8388
|
-
return new Promise((
|
|
8470
|
+
return new Promise((resolve41, reject) => {
|
|
8389
8471
|
const child = spawn(descriptor.command, [...descriptor.args ?? []], {
|
|
8390
8472
|
env: { ...process.env, ...descriptor.env },
|
|
8391
8473
|
shell: false,
|
|
@@ -8428,7 +8510,7 @@ function runShellHandler(descriptor, event, signal) {
|
|
|
8428
8510
|
return;
|
|
8429
8511
|
}
|
|
8430
8512
|
try {
|
|
8431
|
-
|
|
8513
|
+
resolve41(HookDecisionSchema.parse(JSON.parse(stdout)));
|
|
8432
8514
|
} catch (error) {
|
|
8433
8515
|
reject(error);
|
|
8434
8516
|
}
|
|
@@ -8467,7 +8549,7 @@ function zodRecord(payload) {
|
|
|
8467
8549
|
// src/utils/execFileNoThrow.ts
|
|
8468
8550
|
import { execFile } from "child_process";
|
|
8469
8551
|
function execFileNoThrow(file, args, options = {}) {
|
|
8470
|
-
return new Promise((
|
|
8552
|
+
return new Promise((resolve41) => {
|
|
8471
8553
|
const child = execFile(file, args, {
|
|
8472
8554
|
timeout: options.timeout,
|
|
8473
8555
|
cwd: options.useCwd === false ? void 0 : process.cwd(),
|
|
@@ -8475,7 +8557,7 @@ function execFileNoThrow(file, args, options = {}) {
|
|
|
8475
8557
|
encoding: "utf8",
|
|
8476
8558
|
windowsHide: true
|
|
8477
8559
|
}, (error, stdout, stderr) => {
|
|
8478
|
-
|
|
8560
|
+
resolve41({
|
|
8479
8561
|
stdout,
|
|
8480
8562
|
stderr,
|
|
8481
8563
|
code: typeof error?.code === "number" ? error.code : error ? 1 : 0,
|
|
@@ -9777,7 +9859,7 @@ var USAGE = [
|
|
|
9777
9859
|
" clear reset signals, done markers, and verified markers"
|
|
9778
9860
|
].join("\n");
|
|
9779
9861
|
function runCommand(command, cwd, timeoutMs) {
|
|
9780
|
-
return new Promise((
|
|
9862
|
+
return new Promise((resolve41) => {
|
|
9781
9863
|
const isWindows = process.platform === "win32";
|
|
9782
9864
|
const child = spawn2(isWindows ? "cmd.exe" : "/bin/sh", isWindows ? ["/d", "/s", "/c", command] : ["-c", command], {
|
|
9783
9865
|
cwd,
|
|
@@ -9787,7 +9869,7 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
9787
9869
|
let stderr = "";
|
|
9788
9870
|
const timer = setTimeout(() => {
|
|
9789
9871
|
child.kill();
|
|
9790
|
-
|
|
9872
|
+
resolve41({ ok: false, stdout, stderr: `${stderr}
|
|
9791
9873
|
[killed: exceeded ${timeoutMs}ms]`, code: "timeout" });
|
|
9792
9874
|
}, timeoutMs);
|
|
9793
9875
|
child.stdout.on("data", (chunk) => {
|
|
@@ -9798,12 +9880,12 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
9798
9880
|
});
|
|
9799
9881
|
child.on("error", (error) => {
|
|
9800
9882
|
clearTimeout(timer);
|
|
9801
|
-
|
|
9883
|
+
resolve41({ ok: false, stdout, stderr: `${stderr}
|
|
9802
9884
|
${error.message}`, code: "spawn" });
|
|
9803
9885
|
});
|
|
9804
9886
|
child.on("close", (code) => {
|
|
9805
9887
|
clearTimeout(timer);
|
|
9806
|
-
|
|
9888
|
+
resolve41({ ok: code === 0, stdout, stderr, code: code ?? "unknown" });
|
|
9807
9889
|
});
|
|
9808
9890
|
});
|
|
9809
9891
|
}
|
|
@@ -10212,7 +10294,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
10212
10294
|
|
|
10213
10295
|
// src/init/project.ts
|
|
10214
10296
|
import { lstat as lstat4, mkdir as mkdir9, readdir as readdir4, readFile as readFile9, realpath as realpath7, writeFile as writeFile8 } from "fs/promises";
|
|
10215
|
-
import { copyFileSync, mkdirSync as mkdirSync2, readdirSync
|
|
10297
|
+
import { copyFileSync, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
10216
10298
|
import { basename as basename4, dirname as dirname9, isAbsolute as isAbsolute7, join as join12, relative as relative8, sep as sep8 } from "path";
|
|
10217
10299
|
import { fileURLToPath } from "url";
|
|
10218
10300
|
var GENERATED_START = "<!-- flavor-code:start -->";
|
|
@@ -10665,22 +10747,6 @@ async function createExampleFlavorConfig(cwd) {
|
|
|
10665
10747
|
};
|
|
10666
10748
|
await writeFile8(configPath, JSON.stringify(example, null, 2) + "\n");
|
|
10667
10749
|
}
|
|
10668
|
-
var CODEISLAND_SRC_DIR = join12(dirname9(fileURLToPath(import.meta.url)), "codeisland");
|
|
10669
|
-
var CODEISLAND_FILES = ["flavor-plugin.json", "activate.mjs", "bridge.mjs"];
|
|
10670
|
-
async function copyCodeIslandPlugin(cwd) {
|
|
10671
|
-
const pluginDir = join12(cwd, ".flavor", "plugins", "codeisland");
|
|
10672
|
-
try {
|
|
10673
|
-
await lstat4(pluginDir);
|
|
10674
|
-
return;
|
|
10675
|
-
} catch (error) {
|
|
10676
|
-
if (!isMissingPathError(error)) throw error;
|
|
10677
|
-
}
|
|
10678
|
-
await mkdir9(pluginDir, { recursive: true, mode: 448 });
|
|
10679
|
-
for (const name of CODEISLAND_FILES) {
|
|
10680
|
-
const content = readFileSync2(join12(CODEISLAND_SRC_DIR, name), "utf8");
|
|
10681
|
-
await writeFile8(join12(pluginDir, name), content);
|
|
10682
|
-
}
|
|
10683
|
-
}
|
|
10684
10750
|
var ASTGRAPH_SRC_DIR = join12(dirname9(fileURLToPath(import.meta.url)), "astgraph");
|
|
10685
10751
|
async function copyAstgraphPlugin(cwd) {
|
|
10686
10752
|
const pluginDir = join12(cwd, ".flavor", "plugins", "astgraph");
|
|
@@ -10703,7 +10769,6 @@ function copyDirectory(from, to) {
|
|
|
10703
10769
|
}
|
|
10704
10770
|
async function initializeFlavor(cwd) {
|
|
10705
10771
|
await ensureFlavorDirectories(cwd);
|
|
10706
|
-
await copyCodeIslandPlugin(cwd);
|
|
10707
10772
|
await copyAstgraphPlugin(cwd);
|
|
10708
10773
|
await createExampleFlavorConfig(cwd);
|
|
10709
10774
|
const facts = await inspectProject(cwd);
|
|
@@ -13604,7 +13669,7 @@ import {
|
|
|
13604
13669
|
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
13605
13670
|
|
|
13606
13671
|
// src/utils/version.ts
|
|
13607
|
-
import { readFileSync as
|
|
13672
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
13608
13673
|
import { dirname as dirname13, join as join20 } from "path";
|
|
13609
13674
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13610
13675
|
var cached;
|
|
@@ -13613,7 +13678,7 @@ function packageVersion() {
|
|
|
13613
13678
|
let directory = dirname13(fileURLToPath2(import.meta.url));
|
|
13614
13679
|
for (; ; ) {
|
|
13615
13680
|
try {
|
|
13616
|
-
const manifest = JSON.parse(
|
|
13681
|
+
const manifest = JSON.parse(readFileSync2(join20(directory, "package.json"), "utf8"));
|
|
13617
13682
|
if (typeof manifest.version === "string" && manifest.version.length > 0) {
|
|
13618
13683
|
cached = manifest.version;
|
|
13619
13684
|
return cached;
|
|
@@ -13846,8 +13911,8 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13846
13911
|
async #startCallbackServer(expectedState, signal) {
|
|
13847
13912
|
let serverResolve;
|
|
13848
13913
|
let serverReject;
|
|
13849
|
-
const codePromise = new Promise((
|
|
13850
|
-
serverResolve =
|
|
13914
|
+
const codePromise = new Promise((resolve41, reject) => {
|
|
13915
|
+
serverResolve = resolve41;
|
|
13851
13916
|
serverReject = reject;
|
|
13852
13917
|
});
|
|
13853
13918
|
const server = createServer2((req, res) => {
|
|
@@ -13881,7 +13946,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13881
13946
|
);
|
|
13882
13947
|
serverResolve(code);
|
|
13883
13948
|
});
|
|
13884
|
-
return new Promise((
|
|
13949
|
+
return new Promise((resolve41, reject) => {
|
|
13885
13950
|
server.once("error", (error) => {
|
|
13886
13951
|
reject(new Error(`OAuth callback server failed: ${error.message}`));
|
|
13887
13952
|
});
|
|
@@ -13898,7 +13963,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13898
13963
|
signal.addEventListener("abort", () => {
|
|
13899
13964
|
serverReject(new Error("OAuth flow aborted"));
|
|
13900
13965
|
}, { once: true });
|
|
13901
|
-
|
|
13966
|
+
resolve41({ redirectUri, promise: codePromise });
|
|
13902
13967
|
});
|
|
13903
13968
|
});
|
|
13904
13969
|
}
|
|
@@ -18527,8 +18592,8 @@ var LspConnection = class {
|
|
|
18527
18592
|
const id = this.#nextId++;
|
|
18528
18593
|
const request = { jsonrpc: "2.0", id, method, params };
|
|
18529
18594
|
const timeout = AbortSignal.timeout(LSP_REQUEST_TIMEOUT_MS);
|
|
18530
|
-
const promise = new Promise((
|
|
18531
|
-
this.#pending.set(id, { resolve:
|
|
18595
|
+
const promise = new Promise((resolve41, reject) => {
|
|
18596
|
+
this.#pending.set(id, { resolve: resolve41, reject });
|
|
18532
18597
|
timeout.addEventListener("abort", () => {
|
|
18533
18598
|
if (this.#pending.has(id)) {
|
|
18534
18599
|
this.#pending.delete(id);
|
|
@@ -18932,19 +18997,19 @@ async function changeSummary(workspace) {
|
|
|
18932
18997
|
};
|
|
18933
18998
|
}
|
|
18934
18999
|
async function stagedDiff(workspace, maxChars = 96e3) {
|
|
18935
|
-
const
|
|
19000
|
+
const stat19 = await git2(workspace, ["diff", "--staged", "--stat"]);
|
|
18936
19001
|
const diff = await git2(workspace, ["diff", "--staged", "--no-ext-diff", "--no-color"]);
|
|
18937
19002
|
if (!diff.ok) throw gitFailure(diff, "diff --staged");
|
|
18938
19003
|
const text = diff.stdout;
|
|
18939
19004
|
return {
|
|
18940
19005
|
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
18941
19006
|
\u2026[diff truncated]` : text,
|
|
18942
|
-
stat:
|
|
19007
|
+
stat: stat19.stdout.trim(),
|
|
18943
19008
|
truncated: text.length > maxChars
|
|
18944
19009
|
};
|
|
18945
19010
|
}
|
|
18946
19011
|
async function uncommittedDiff(workspace, maxChars = 96e3) {
|
|
18947
|
-
const
|
|
19012
|
+
const stat19 = await git2(workspace, ["diff", "HEAD", "--stat"]);
|
|
18948
19013
|
const diff = await git2(workspace, ["diff", "HEAD", "--no-ext-diff", "--no-color"]);
|
|
18949
19014
|
if (!diff.ok && !diff.stderr.includes("unknown revision")) throw gitFailure(diff, "diff HEAD");
|
|
18950
19015
|
const summary2 = await changeSummary(workspace);
|
|
@@ -18952,7 +19017,7 @@ async function uncommittedDiff(workspace, maxChars = 96e3) {
|
|
|
18952
19017
|
return {
|
|
18953
19018
|
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
18954
19019
|
\u2026[diff truncated]` : text,
|
|
18955
|
-
stat:
|
|
19020
|
+
stat: stat19.stdout.trim(),
|
|
18956
19021
|
untracked: summary2.untrackedFiles
|
|
18957
19022
|
};
|
|
18958
19023
|
}
|
|
@@ -19166,7 +19231,7 @@ var QuestionBridge = class {
|
|
|
19166
19231
|
ask(questions, signal) {
|
|
19167
19232
|
if (this.#pending !== void 0) return Promise.reject(new Error("A question is already pending"));
|
|
19168
19233
|
if (signal.aborted) return Promise.reject(signal.reason);
|
|
19169
|
-
return new Promise((
|
|
19234
|
+
return new Promise((resolve41, reject) => {
|
|
19170
19235
|
const onAbort = () => {
|
|
19171
19236
|
this.#pending = void 0;
|
|
19172
19237
|
reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
|
|
@@ -19178,7 +19243,7 @@ var QuestionBridge = class {
|
|
|
19178
19243
|
signal.removeEventListener("abort", onAbort);
|
|
19179
19244
|
this.#pending = void 0;
|
|
19180
19245
|
this.#onChange?.();
|
|
19181
|
-
|
|
19246
|
+
resolve41(answers);
|
|
19182
19247
|
},
|
|
19183
19248
|
reject: (error) => {
|
|
19184
19249
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -20795,9 +20860,9 @@ var FlavorSession = class {
|
|
|
20795
20860
|
}
|
|
20796
20861
|
#waitForPlanningIdle(key) {
|
|
20797
20862
|
if (!this.#activePlanning.has(key)) return Promise.resolve();
|
|
20798
|
-
return new Promise((
|
|
20863
|
+
return new Promise((resolve41) => {
|
|
20799
20864
|
const check2 = () => {
|
|
20800
|
-
if (!this.#activePlanning.has(key))
|
|
20865
|
+
if (!this.#activePlanning.has(key)) resolve41();
|
|
20801
20866
|
else this.#planningIdleWaiters.add(check2);
|
|
20802
20867
|
};
|
|
20803
20868
|
this.#planningIdleWaiters.add(check2);
|
|
@@ -22800,8 +22865,8 @@ var JobRegistry = class {
|
|
|
22800
22865
|
const id = `job-${randomUUID17().slice(0, 12)}`;
|
|
22801
22866
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22802
22867
|
let settle;
|
|
22803
|
-
const done = new Promise((
|
|
22804
|
-
settle =
|
|
22868
|
+
const done = new Promise((resolve41) => {
|
|
22869
|
+
settle = resolve41;
|
|
22805
22870
|
});
|
|
22806
22871
|
const record3 = {
|
|
22807
22872
|
id,
|
|
@@ -23305,15 +23370,15 @@ async function acquirePalFileLock(options) {
|
|
|
23305
23370
|
// src/pals/broker-cli.ts
|
|
23306
23371
|
var startupAttempts = /* @__PURE__ */ new Map();
|
|
23307
23372
|
function delay3(milliseconds) {
|
|
23308
|
-
return new Promise((
|
|
23373
|
+
return new Promise((resolve41) => setTimeout(resolve41, milliseconds));
|
|
23309
23374
|
}
|
|
23310
23375
|
function canConnect(address) {
|
|
23311
|
-
return new Promise((
|
|
23376
|
+
return new Promise((resolve41) => {
|
|
23312
23377
|
const socket = new Socket2();
|
|
23313
23378
|
const finish = (connected) => {
|
|
23314
23379
|
socket.removeAllListeners();
|
|
23315
23380
|
socket.destroy();
|
|
23316
|
-
|
|
23381
|
+
resolve41(connected);
|
|
23317
23382
|
};
|
|
23318
23383
|
socket.once("connect", () => finish(true));
|
|
23319
23384
|
socket.once("error", () => finish(false));
|
|
@@ -23327,18 +23392,25 @@ function startupLockPath(address) {
|
|
|
23327
23392
|
async function spawnProductionBroker(address) {
|
|
23328
23393
|
const entry2 = process.argv[1];
|
|
23329
23394
|
if (entry2 === void 0) throw new Error("Cannot auto-start pals broker without a CLI entry point");
|
|
23395
|
+
return spawnPalBrokerProcess({ address, executable: process.execPath, args: [entry2, "--pals-broker", address] });
|
|
23396
|
+
}
|
|
23397
|
+
async function spawnPalBrokerProcess(options) {
|
|
23398
|
+
if (options.executable.trim() === "" || options.args.length === 0) throw new Error("Pals broker process requires an executable and entry arguments");
|
|
23399
|
+
const address = options.address;
|
|
23330
23400
|
const lockPath = startupLockPath(address);
|
|
23331
23401
|
const lock = await acquirePalFileLock({ path: lockPath, endpointLive: () => canConnect(address) });
|
|
23332
23402
|
if (lock === void 0) return;
|
|
23333
23403
|
try {
|
|
23334
|
-
const child = spawn7(
|
|
23404
|
+
const child = spawn7(options.executable, [...options.args], {
|
|
23335
23405
|
detached: true,
|
|
23336
23406
|
stdio: "ignore",
|
|
23337
|
-
windowsHide: true
|
|
23407
|
+
windowsHide: true,
|
|
23408
|
+
...options.environment === void 0 ? {} : { env: options.environment }
|
|
23338
23409
|
});
|
|
23339
23410
|
child.unref();
|
|
23340
23411
|
const deadline = Date.now() + 5e3;
|
|
23341
23412
|
while (Date.now() < deadline && !await canConnect(address)) await delay3(25);
|
|
23413
|
+
if (!await canConnect(address)) throw new Error(`Pals broker process did not become ready at '${address}'`);
|
|
23342
23414
|
} finally {
|
|
23343
23415
|
await lock.release();
|
|
23344
23416
|
}
|
|
@@ -23497,8 +23569,8 @@ var PalClient = class {
|
|
|
23497
23569
|
let disconnectTimer;
|
|
23498
23570
|
await Promise.race([
|
|
23499
23571
|
this.request({ version: 1, type: "disconnect", requestId: randomUUID22() }).catch(() => void 0),
|
|
23500
|
-
new Promise((
|
|
23501
|
-
disconnectTimer = setTimeout(
|
|
23572
|
+
new Promise((resolve41) => {
|
|
23573
|
+
disconnectTimer = setTimeout(resolve41, Math.min(this.requestTimeoutMs, 250));
|
|
23502
23574
|
})
|
|
23503
23575
|
]);
|
|
23504
23576
|
if (disconnectTimer !== void 0) clearTimeout(disconnectTimer);
|
|
@@ -23544,7 +23616,7 @@ var PalClient = class {
|
|
|
23544
23616
|
return presence;
|
|
23545
23617
|
}
|
|
23546
23618
|
openSocket() {
|
|
23547
|
-
return new Promise((
|
|
23619
|
+
return new Promise((resolve41, reject) => {
|
|
23548
23620
|
let settled = false;
|
|
23549
23621
|
const rejectConnection = (error) => {
|
|
23550
23622
|
if (settled) return;
|
|
@@ -23567,7 +23639,7 @@ var PalClient = class {
|
|
|
23567
23639
|
settled = true;
|
|
23568
23640
|
if (this.connectingReject === rejectConnection) this.connectingReject = void 0;
|
|
23569
23641
|
socket.off("error", onInitialError);
|
|
23570
|
-
|
|
23642
|
+
resolve41();
|
|
23571
23643
|
};
|
|
23572
23644
|
const onInitialError = (error) => {
|
|
23573
23645
|
socket.off("connect", onConnect);
|
|
@@ -23598,12 +23670,12 @@ var PalClient = class {
|
|
|
23598
23670
|
if (this.closed || socket === void 0 || socket.destroyed || !socket.writable) {
|
|
23599
23671
|
return Promise.reject(new Error(this.closed ? "Pal client is closed" : "Pal client is not connected"));
|
|
23600
23672
|
}
|
|
23601
|
-
return new Promise((
|
|
23673
|
+
return new Promise((resolve41, reject) => {
|
|
23602
23674
|
const timer = setTimeout(() => {
|
|
23603
23675
|
this.pending.delete(request.requestId);
|
|
23604
23676
|
reject(new Error(`Pal request '${request.type}' timed out`));
|
|
23605
23677
|
}, this.requestTimeoutMs);
|
|
23606
|
-
this.pending.set(request.requestId, { resolve:
|
|
23678
|
+
this.pending.set(request.requestId, { resolve: resolve41, reject, timer });
|
|
23607
23679
|
let frame;
|
|
23608
23680
|
try {
|
|
23609
23681
|
frame = encodeControlFrame(BrokerRequestSchema.parse(request));
|
|
@@ -23916,6 +23988,51 @@ var INTERRUPTED_TASK_PLAN_CONTEXT = [
|
|
|
23916
23988
|
"Reassess the current query independently.",
|
|
23917
23989
|
"If the current work needs a plan, create a fresh one with TaskPlan before using TaskUpdate."
|
|
23918
23990
|
].join(" ");
|
|
23991
|
+
function recoverablePalClient(client, onRecovered) {
|
|
23992
|
+
let presence;
|
|
23993
|
+
let starting;
|
|
23994
|
+
const ensure = () => {
|
|
23995
|
+
if (presence !== void 0) return Promise.resolve(presence);
|
|
23996
|
+
if (starting !== void 0) return starting;
|
|
23997
|
+
starting = client.start().then((value) => {
|
|
23998
|
+
presence = value;
|
|
23999
|
+
onRecovered();
|
|
24000
|
+
return value;
|
|
24001
|
+
}).finally(() => {
|
|
24002
|
+
starting = void 0;
|
|
24003
|
+
});
|
|
24004
|
+
return starting;
|
|
24005
|
+
};
|
|
24006
|
+
const invoke = async (run) => {
|
|
24007
|
+
await ensure();
|
|
24008
|
+
try {
|
|
24009
|
+
return await run();
|
|
24010
|
+
} catch (error) {
|
|
24011
|
+
const detail = message(error);
|
|
24012
|
+
if (!/not connected|connection closed|ECONNRESET|ECONNREFUSED|EPIPE/i.test(detail)) throw error;
|
|
24013
|
+
presence = void 0;
|
|
24014
|
+
await ensure();
|
|
24015
|
+
return run();
|
|
24016
|
+
}
|
|
24017
|
+
};
|
|
24018
|
+
return {
|
|
24019
|
+
start: ensure,
|
|
24020
|
+
list: () => invoke(() => client.list()),
|
|
24021
|
+
rename: (alias) => invoke(() => client.rename(alias)),
|
|
24022
|
+
sendTask: (target, value) => invoke(() => client.sendTask(target, value)),
|
|
24023
|
+
sendChat: (target, value) => invoke(() => client.sendChat(target, value)),
|
|
24024
|
+
startCoWork: (input) => invoke(() => client.startCoWork(input)),
|
|
24025
|
+
coWorkAction: (action) => invoke(() => client.coWorkAction(action)),
|
|
24026
|
+
coWorkStatus: (coWorkId) => invoke(() => client.coWorkStatus(coWorkId)),
|
|
24027
|
+
integrateCoWork: (input) => invoke(() => client.integrateCoWork(input)),
|
|
24028
|
+
cancelCoWork: (coWorkId, reason) => invoke(() => client.cancelCoWork(coWorkId, reason)),
|
|
24029
|
+
subscribe: (listener) => client.subscribe(listener),
|
|
24030
|
+
close: async () => {
|
|
24031
|
+
presence = void 0;
|
|
24032
|
+
await client.close();
|
|
24033
|
+
}
|
|
24034
|
+
};
|
|
24035
|
+
}
|
|
23919
24036
|
var DEFAULT_SHUTDOWN_STEP_TIMEOUT_MS = 3e3;
|
|
23920
24037
|
function createPromptEnvironment(input = {}) {
|
|
23921
24038
|
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
@@ -24175,7 +24292,10 @@ async function createProductionRuntime(options) {
|
|
|
24175
24292
|
globalPluginDirs: [join31(home, ".flavor-code", "plugins")],
|
|
24176
24293
|
projectPluginDirs: [join31(workspace, ".flavor", "plugins")],
|
|
24177
24294
|
config,
|
|
24178
|
-
|
|
24295
|
+
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
24296
|
+
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
24297
|
+
// must stay opt-in until equivalent mediated capabilities are available.
|
|
24298
|
+
sandbox: options.pluginSandbox ?? false,
|
|
24179
24299
|
registrations: {
|
|
24180
24300
|
command(name, handler, description) {
|
|
24181
24301
|
if (typeof handler !== "function") throw new Error(`Plugin command "${name}" must be a function.`);
|
|
@@ -24236,6 +24356,7 @@ async function createProductionRuntime(options) {
|
|
|
24236
24356
|
tools.push(evolveService.toolDefinition());
|
|
24237
24357
|
let sleepScheduler;
|
|
24238
24358
|
let collaborationClient;
|
|
24359
|
+
let collaborationUnavailableDiagnostic;
|
|
24239
24360
|
let unsubscribeCollaboration;
|
|
24240
24361
|
let collaborationSession;
|
|
24241
24362
|
let deliverCollaborationEvent;
|
|
@@ -24278,7 +24399,7 @@ async function createProductionRuntime(options) {
|
|
|
24278
24399
|
userScope: home,
|
|
24279
24400
|
runtimeDir: join31(tmpdir3(), `flavor-code-pals-${createHash20("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
24280
24401
|
});
|
|
24281
|
-
|
|
24402
|
+
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
24282
24403
|
instanceId: collaboration.instanceId,
|
|
24283
24404
|
alias,
|
|
24284
24405
|
projectPath: workspace
|
|
@@ -24286,8 +24407,13 @@ async function createProductionRuntime(options) {
|
|
|
24286
24407
|
address,
|
|
24287
24408
|
authHome: home,
|
|
24288
24409
|
registration: { id: collaboration.instanceId, alias, projectPath: workspace },
|
|
24289
|
-
startBroker: () => ensurePalBrokerRunning({ address })
|
|
24410
|
+
startBroker: () => ensurePalBrokerRunning({ address, ...collaboration.startBroker === void 0 ? {} : { startBroker: () => collaboration.startBroker(address) } })
|
|
24290
24411
|
});
|
|
24412
|
+
collaborationClient = collaboration.optional ? recoverablePalClient(baseCollaborationClient, () => {
|
|
24413
|
+
if (collaborationUnavailableDiagnostic === void 0) return;
|
|
24414
|
+
remove(diagnostics, collaborationUnavailableDiagnostic);
|
|
24415
|
+
collaborationUnavailableDiagnostic = void 0;
|
|
24416
|
+
}) : baseCollaborationClient;
|
|
24291
24417
|
deliverCollaborationEvent = async (event) => {
|
|
24292
24418
|
const session2 = collaborationSession;
|
|
24293
24419
|
const client = collaborationClient;
|
|
@@ -24342,11 +24468,19 @@ async function createProductionRuntime(options) {
|
|
|
24342
24468
|
}
|
|
24343
24469
|
};
|
|
24344
24470
|
unsubscribeCollaboration = collaborationClient.subscribe(enqueueCollaborationEvent);
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24349
|
-
|
|
24471
|
+
try {
|
|
24472
|
+
await collaborationClient.start();
|
|
24473
|
+
} catch (error) {
|
|
24474
|
+
if (!collaboration.optional) throw error;
|
|
24475
|
+
collaborationUnavailableDiagnostic = `Pals unavailable: ${message(error)}`;
|
|
24476
|
+
diagnostics.push(collaborationUnavailableDiagnostic);
|
|
24477
|
+
}
|
|
24478
|
+
if (collaborationClient !== void 0) {
|
|
24479
|
+
tools.push(...createPalsTools(collaborationClient, {
|
|
24480
|
+
selfId: collaboration.instanceId,
|
|
24481
|
+
shareGuard: new CollaborationShareGuard({ redact: (value) => redactSecrets(value, secrets) })
|
|
24482
|
+
}));
|
|
24483
|
+
}
|
|
24350
24484
|
}
|
|
24351
24485
|
const syncMcpTools = () => {
|
|
24352
24486
|
for (const tool of mcpTools) remove(tools, tool);
|
|
@@ -25103,6 +25237,7 @@ async function createProductionRuntime(options) {
|
|
|
25103
25237
|
}),
|
|
25104
25238
|
rename: (alias) => collaborationClient.rename(alias),
|
|
25105
25239
|
info: (target) => resolveCollaborationTarget(target),
|
|
25240
|
+
sendChat: (target, message2) => collaborationClient.sendChat(target, message2),
|
|
25106
25241
|
sendTask: (target, goal) => collaborationClient.sendTask(target, goal),
|
|
25107
25242
|
startCoWork: async (target, goal) => {
|
|
25108
25243
|
const recipient = await resolveCollaborationTarget(target);
|
|
@@ -25363,6 +25498,7 @@ ${summary2}`;
|
|
|
25363
25498
|
return sessionHistory.checkpoint(tagged, harness.main.context.snapshot());
|
|
25364
25499
|
},
|
|
25365
25500
|
tree: () => sessionHistory.tree(),
|
|
25501
|
+
historyLeaf: () => sessionHistory.leafId,
|
|
25366
25502
|
rewind: async (nodeId) => {
|
|
25367
25503
|
await sessionHistory.rewind(nodeId, harness.main.context.snapshot());
|
|
25368
25504
|
await persist();
|
|
@@ -25551,7 +25687,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
25551
25687
|
approvals,
|
|
25552
25688
|
memoryReviews,
|
|
25553
25689
|
restoredTranscript,
|
|
25554
|
-
jobs: { list: () => jobs.list(), subscribe: (listener) => jobs.subscribe(listener) },
|
|
25690
|
+
jobs: { list: () => jobs.list(), read: (id, owner2, cursor) => jobs.read(id, owner2, cursor), subscribe: (listener) => jobs.subscribe(listener) },
|
|
25555
25691
|
get sessionId() {
|
|
25556
25692
|
return sessionId;
|
|
25557
25693
|
},
|
|
@@ -25832,23 +25968,23 @@ async function* persistEach(source, persist) {
|
|
|
25832
25968
|
}
|
|
25833
25969
|
}
|
|
25834
25970
|
async function* durableTurn(source, journal, turnId, config) {
|
|
25835
|
-
let
|
|
25971
|
+
let failure2;
|
|
25836
25972
|
let completed = false;
|
|
25837
25973
|
try {
|
|
25838
25974
|
for await (const event of source) {
|
|
25839
|
-
if (event.type === "error")
|
|
25975
|
+
if (event.type === "error") failure2 = event.error.message;
|
|
25840
25976
|
yield event;
|
|
25841
25977
|
}
|
|
25842
25978
|
completed = true;
|
|
25843
25979
|
} catch (error) {
|
|
25844
|
-
|
|
25980
|
+
failure2 = message(error);
|
|
25845
25981
|
throw error;
|
|
25846
25982
|
} finally {
|
|
25847
|
-
if (completed &&
|
|
25983
|
+
if (completed && failure2 === void 0) {
|
|
25848
25984
|
journal.completeTurn(turnId);
|
|
25849
25985
|
journal.savepoint("turn-complete", config);
|
|
25850
25986
|
} else {
|
|
25851
|
-
journal.interruptTurn(turnId,
|
|
25987
|
+
journal.interruptTurn(turnId, failure2 ?? "turn consumer interrupted before completion");
|
|
25852
25988
|
}
|
|
25853
25989
|
}
|
|
25854
25990
|
}
|
|
@@ -28232,11 +28368,22 @@ var ProjectMemoryManager = class {
|
|
|
28232
28368
|
this.#store = store;
|
|
28233
28369
|
this.#enabled = enabled;
|
|
28234
28370
|
}
|
|
28235
|
-
async snapshot() {
|
|
28371
|
+
async snapshot(now = /* @__PURE__ */ new Date()) {
|
|
28372
|
+
const references = this.#enabled ? await this.#store.references() : [];
|
|
28236
28373
|
return {
|
|
28237
28374
|
enabled: this.#enabled,
|
|
28238
28375
|
path: this.#store.path,
|
|
28239
|
-
entries:
|
|
28376
|
+
entries: references.map((reference) => {
|
|
28377
|
+
const lastRecalledAt = Object.values(reference.recalls).sort().at(-1);
|
|
28378
|
+
return {
|
|
28379
|
+
id: reference.id,
|
|
28380
|
+
type: reference.type,
|
|
28381
|
+
content: reference.summary,
|
|
28382
|
+
recallTotal: reference.recallTotal,
|
|
28383
|
+
heat: classifyMemoryHeat(reference, now),
|
|
28384
|
+
...lastRecalledAt === void 0 ? {} : { lastRecalledAt }
|
|
28385
|
+
};
|
|
28386
|
+
})
|
|
28240
28387
|
};
|
|
28241
28388
|
}
|
|
28242
28389
|
async remember(candidate) {
|
|
@@ -28253,6 +28400,10 @@ var ProjectMemoryManager = class {
|
|
|
28253
28400
|
this.#assertEnabled();
|
|
28254
28401
|
return this.#store.delete(id);
|
|
28255
28402
|
}
|
|
28403
|
+
async deleteCold(now = /* @__PURE__ */ new Date()) {
|
|
28404
|
+
this.#assertEnabled();
|
|
28405
|
+
return this.#store.forgetCold(now);
|
|
28406
|
+
}
|
|
28256
28407
|
#assertEnabled() {
|
|
28257
28408
|
if (!this.#enabled) throw new Error("Long-term memory is disabled for this project");
|
|
28258
28409
|
}
|
|
@@ -28577,6 +28728,7 @@ async function d2cBackendSourceFingerprint(project) {
|
|
|
28577
28728
|
var DesktopRuntimeController = class {
|
|
28578
28729
|
#home;
|
|
28579
28730
|
#createRuntime;
|
|
28731
|
+
#createTerminalService;
|
|
28580
28732
|
#listSessions;
|
|
28581
28733
|
#deleteStoredSession;
|
|
28582
28734
|
#loadModels;
|
|
@@ -28599,8 +28751,10 @@ var DesktopRuntimeController = class {
|
|
|
28599
28751
|
#skillManager;
|
|
28600
28752
|
#memoryManager;
|
|
28601
28753
|
#mcpManager;
|
|
28754
|
+
#terminal;
|
|
28602
28755
|
#models = DEFAULT_DESKTOP_MODELS;
|
|
28603
28756
|
#busy = false;
|
|
28757
|
+
#tasks;
|
|
28604
28758
|
#disposeJobSubscription;
|
|
28605
28759
|
#d2cMocks = /* @__PURE__ */ new Map();
|
|
28606
28760
|
#d2cPreviews = /* @__PURE__ */ new Map();
|
|
@@ -28612,6 +28766,7 @@ var DesktopRuntimeController = class {
|
|
|
28612
28766
|
constructor(options) {
|
|
28613
28767
|
this.#home = resolve35(options.home ?? homedir4());
|
|
28614
28768
|
this.#createRuntime = options.createRuntime ?? (async (runtimeOptions) => createProductionRuntime(runtimeOptions));
|
|
28769
|
+
this.#createTerminalService = options.createTerminalService ?? ((workspace) => new TerminalService(workspace));
|
|
28615
28770
|
this.#listSessions = options.listSessions ?? (async (workspace) => {
|
|
28616
28771
|
const store = new SessionStore({ workspace });
|
|
28617
28772
|
const entries = await store.list();
|
|
@@ -28678,7 +28833,8 @@ var DesktopRuntimeController = class {
|
|
|
28678
28833
|
},
|
|
28679
28834
|
diagnostics: runtime?.diagnostics ?? [],
|
|
28680
28835
|
models: this.#models,
|
|
28681
|
-
jobs: runtime?.jobs?.list() ?? []
|
|
28836
|
+
jobs: runtime?.jobs?.list() ?? [],
|
|
28837
|
+
...this.#tasks === void 0 ? {} : { tasks: this.#tasks }
|
|
28682
28838
|
};
|
|
28683
28839
|
}
|
|
28684
28840
|
async openWorkspace(path) {
|
|
@@ -28690,6 +28846,8 @@ var DesktopRuntimeController = class {
|
|
|
28690
28846
|
await this.#stopAllD2cMocks();
|
|
28691
28847
|
}
|
|
28692
28848
|
this.#workspace = workspace;
|
|
28849
|
+
this.#terminal?.dispose();
|
|
28850
|
+
this.#terminal = this.#createTerminalService(workspace);
|
|
28693
28851
|
this.#skillManager = new SkillManager({ workspace, home: this.#home });
|
|
28694
28852
|
this.#memoryManager = await this.#loadMemoryManager(workspace, this.#home);
|
|
28695
28853
|
this.#mcpManager = this.#loadMcpManager(workspace);
|
|
@@ -28720,6 +28878,7 @@ var DesktopRuntimeController = class {
|
|
|
28720
28878
|
bufferedOutput.push(event);
|
|
28721
28879
|
return;
|
|
28722
28880
|
}
|
|
28881
|
+
this.#captureSessionOutput(event);
|
|
28723
28882
|
this.#emit({ type: "session-output", sessionId: outputSessionId, event });
|
|
28724
28883
|
},
|
|
28725
28884
|
onApprovalChange: () => {
|
|
@@ -28743,6 +28902,7 @@ var DesktopRuntimeController = class {
|
|
|
28743
28902
|
this.#emit({ type: "session-started", payload });
|
|
28744
28903
|
outputReady = true;
|
|
28745
28904
|
for (const event of bufferedOutput) {
|
|
28905
|
+
this.#captureSessionOutput(event);
|
|
28746
28906
|
this.#emit({ type: "session-output", sessionId: runtime.sessionId, event });
|
|
28747
28907
|
}
|
|
28748
28908
|
this.#publishSnapshot();
|
|
@@ -28913,6 +29073,83 @@ var DesktopRuntimeController = class {
|
|
|
28913
29073
|
if (deleted) await this.#runtime?.services.refreshMemory?.();
|
|
28914
29074
|
return deleted;
|
|
28915
29075
|
}
|
|
29076
|
+
async deleteColdMemory() {
|
|
29077
|
+
const manager = this.#requireMemoryManager();
|
|
29078
|
+
if (manager.deleteCold === void 0) throw new Error("Cold-memory cleanup is unavailable");
|
|
29079
|
+
const result = await manager.deleteCold();
|
|
29080
|
+
if (result.removed > 0) await this.#runtime?.services.refreshMemory?.();
|
|
29081
|
+
return result.removed;
|
|
29082
|
+
}
|
|
29083
|
+
async historySnapshot() {
|
|
29084
|
+
const services = this.#requireRuntime().services;
|
|
29085
|
+
if (services.tree === void 0) throw new Error("Session history is unavailable");
|
|
29086
|
+
return { leafId: services.historyLeaf?.() ?? null, nodes: services.tree() };
|
|
29087
|
+
}
|
|
29088
|
+
async createCheckpoint(label) {
|
|
29089
|
+
const checkpoint = this.#requireRuntime().services.checkpoint;
|
|
29090
|
+
if (checkpoint === void 0) throw new Error("Session checkpoints are unavailable");
|
|
29091
|
+
return checkpoint(label?.trim() || void 0);
|
|
29092
|
+
}
|
|
29093
|
+
async rewindHistory(nodeId) {
|
|
29094
|
+
const rewind = this.#requireRuntime().services.rewind;
|
|
29095
|
+
if (rewind === void 0) throw new Error("Session rewind is unavailable");
|
|
29096
|
+
await rewind(nodeId);
|
|
29097
|
+
}
|
|
29098
|
+
async unrevertHistory() {
|
|
29099
|
+
const unrevert = this.#requireRuntime().services.unrevert;
|
|
29100
|
+
if (unrevert === void 0) throw new Error("Session unrevert is unavailable");
|
|
29101
|
+
await unrevert();
|
|
29102
|
+
}
|
|
29103
|
+
async forkHistory(nodeId) {
|
|
29104
|
+
const fork = this.#requireRuntime().services.fork;
|
|
29105
|
+
if (fork === void 0) throw new Error("Session fork is unavailable");
|
|
29106
|
+
await fork(nodeId);
|
|
29107
|
+
}
|
|
29108
|
+
openTerminal(input = {}) {
|
|
29109
|
+
return this.#requireTerminal().open({ ...input, owner: this.#requireRuntime().sessionId });
|
|
29110
|
+
}
|
|
29111
|
+
listTerminals() {
|
|
29112
|
+
return this.#requireTerminal().list(this.#requireRuntime().sessionId).filter((terminal) => terminal.state !== "closed");
|
|
29113
|
+
}
|
|
29114
|
+
readTerminal(id, cursor = 0) {
|
|
29115
|
+
return this.#requireTerminal().read(id, this.#requireRuntime().sessionId, cursor);
|
|
29116
|
+
}
|
|
29117
|
+
writeTerminal(id, data2) {
|
|
29118
|
+
this.#requireTerminal().write(id, this.#requireRuntime().sessionId, data2);
|
|
29119
|
+
}
|
|
29120
|
+
resizeTerminal(id, columns, rows) {
|
|
29121
|
+
this.#requireTerminal().resize(id, this.#requireRuntime().sessionId, columns, rows);
|
|
29122
|
+
}
|
|
29123
|
+
closeTerminal(id) {
|
|
29124
|
+
this.#requireTerminal().close(id, this.#requireRuntime().sessionId);
|
|
29125
|
+
}
|
|
29126
|
+
readJob(id, cursor = 0) {
|
|
29127
|
+
const runtime = this.#requireRuntime();
|
|
29128
|
+
if (runtime.jobs === void 0) throw new Error("Background job output is unavailable");
|
|
29129
|
+
const job = runtime.jobs.list().find((item) => item.id === id);
|
|
29130
|
+
if (job === void 0) throw new Error(`Unknown background job: ${id}`);
|
|
29131
|
+
return runtime.jobs.read(id, job.owner, cursor);
|
|
29132
|
+
}
|
|
29133
|
+
async listPals(verbose = true) {
|
|
29134
|
+
return this.#requirePals().list(verbose);
|
|
29135
|
+
}
|
|
29136
|
+
async sendPalMessage(target, message2, kind) {
|
|
29137
|
+
const pals = this.#requirePals();
|
|
29138
|
+
if (kind === "chat") {
|
|
29139
|
+
if (pals.sendChat === void 0) throw new Error("Pal chat is unavailable");
|
|
29140
|
+
return pals.sendChat(target, message2);
|
|
29141
|
+
}
|
|
29142
|
+
return pals.sendTask(target, message2);
|
|
29143
|
+
}
|
|
29144
|
+
async startCoWork(target, goal) {
|
|
29145
|
+
return this.#requirePals().startCoWork(target, goal);
|
|
29146
|
+
}
|
|
29147
|
+
async coWorkStatus(coWorkId) {
|
|
29148
|
+
return this.#requirePals().coWorkStatus(coWorkId);
|
|
29149
|
+
}
|
|
29150
|
+
async cancelCoWork(coWorkId, reason) {
|
|
29151
|
+
return this.#requirePals().cancelCoWork(coWorkId, reason);
|
|
29152
|
+
}
|
|
28916
29153
|
async switchModel(modelId) {
|
|
28917
29154
|
if (this.#busy) throw new Error("Stop the active task before switching models");
|
|
28918
29155
|
if (!this.#models.some((model) => model.id === modelId)) {
|
|
@@ -29675,6 +29912,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29675
29912
|
await this.#disposeRuntime();
|
|
29676
29913
|
await this.#stopAllD2cPreviews();
|
|
29677
29914
|
await this.#stopAllD2cMocks();
|
|
29915
|
+
this.#terminal?.dispose();
|
|
29916
|
+
this.#terminal = void 0;
|
|
29678
29917
|
}
|
|
29679
29918
|
#publishSnapshot() {
|
|
29680
29919
|
const snapshot4 = this.snapshot();
|
|
@@ -29695,6 +29934,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29695
29934
|
this.#disposeJobSubscription = void 0;
|
|
29696
29935
|
this.#runtime = void 0;
|
|
29697
29936
|
this.#busy = false;
|
|
29937
|
+
this.#tasks = void 0;
|
|
29698
29938
|
if (runtime === void 0) return;
|
|
29699
29939
|
await runtime.session.close();
|
|
29700
29940
|
await runtime.dispose();
|
|
@@ -29732,6 +29972,23 @@ VITE_API_BASE_URL=${running.url}
|
|
|
29732
29972
|
if (this.#mcpManager === void 0) throw new Error("Open a project before managing MCP services");
|
|
29733
29973
|
return this.#mcpManager;
|
|
29734
29974
|
}
|
|
29975
|
+
#captureSessionOutput(event) {
|
|
29976
|
+
if (event.type === "tasks") this.#tasks = event.snapshot;
|
|
29977
|
+
else if (event.type === "tasks-cleared") this.#tasks = void 0;
|
|
29978
|
+
}
|
|
29979
|
+
#requireRuntime() {
|
|
29980
|
+
if (this.#runtime === void 0) throw new Error("Start a session before using this feature");
|
|
29981
|
+
return this.#runtime;
|
|
29982
|
+
}
|
|
29983
|
+
#requireTerminal() {
|
|
29984
|
+
if (this.#terminal === void 0) throw new Error("Open a project before using the terminal");
|
|
29985
|
+
return this.#terminal;
|
|
29986
|
+
}
|
|
29987
|
+
#requirePals() {
|
|
29988
|
+
const pals = this.#requireRuntime().services.pals;
|
|
29989
|
+
if (pals === void 0) throw new Error("Pals are unavailable for this session");
|
|
29990
|
+
return pals;
|
|
29991
|
+
}
|
|
29735
29992
|
};
|
|
29736
29993
|
|
|
29737
29994
|
// src/desktop/security.ts
|
|
@@ -29748,6 +30005,16 @@ function normalizePersistedWorkspace(value) {
|
|
|
29748
30005
|
const workspace = value.workspace;
|
|
29749
30006
|
return typeof workspace === "string" && workspace.trim().length > 0 && workspace.length <= 32768 ? workspace : void 0;
|
|
29750
30007
|
}
|
|
30008
|
+
function normalizePersistedDesktopProjects(value) {
|
|
30009
|
+
const workspace = normalizePersistedWorkspace(value);
|
|
30010
|
+
if (typeof value !== "object" || value === null) {
|
|
30011
|
+
return { ...workspace === void 0 ? {} : { workspace }, projects: workspace === void 0 ? [] : [workspace] };
|
|
30012
|
+
}
|
|
30013
|
+
const rawProjects = value.projects;
|
|
30014
|
+
const projects = Array.isArray(rawProjects) ? rawProjects.filter((item) => typeof item === "string" && item.trim().length > 0 && item.length <= 32768) : [];
|
|
30015
|
+
const unique = [.../* @__PURE__ */ new Set([...workspace === void 0 ? [] : [workspace], ...projects])].slice(0, 50);
|
|
30016
|
+
return { ...workspace === void 0 ? {} : { workspace }, projects: unique };
|
|
30017
|
+
}
|
|
29751
30018
|
function isTrustedNavigation(target, current, trustedRenderer) {
|
|
29752
30019
|
return target === trustedRenderer || current === trustedRenderer && target === current;
|
|
29753
30020
|
}
|
|
@@ -29831,6 +30098,472 @@ ${reason.stack ?? ""}`;
|
|
|
29831
30098
|
process.on("uncaughtException", (error) => crash("uncaughtException", error));
|
|
29832
30099
|
}
|
|
29833
30100
|
|
|
30101
|
+
// src/desktop/git-manager.ts
|
|
30102
|
+
import { readFile as readFile36, rm as rm16 } from "fs/promises";
|
|
30103
|
+
import { isAbsolute as isAbsolute18, relative as relative26, resolve as resolve36 } from "path";
|
|
30104
|
+
function failure(action, stderr, stdout) {
|
|
30105
|
+
return new Error(`${action}\u5931\u8D25\uFF1A${(stderr.trim() || stdout.trim() || "\u672A\u77E5 Git \u9519\u8BEF").split("\n")[0]}`);
|
|
30106
|
+
}
|
|
30107
|
+
async function desktopGitStatus(workspace) {
|
|
30108
|
+
if (!await isGitRepository(workspace)) return { repository: false, branch: "", head: "", files: [] };
|
|
30109
|
+
const summary2 = await changeSummary(workspace);
|
|
30110
|
+
return {
|
|
30111
|
+
repository: true,
|
|
30112
|
+
branch: summary2.branch,
|
|
30113
|
+
head: summary2.head,
|
|
30114
|
+
files: summary2.statusLines.map((line) => {
|
|
30115
|
+
const index = line[0] ?? " ";
|
|
30116
|
+
const worktree = line[1] ?? " ";
|
|
30117
|
+
return {
|
|
30118
|
+
path: line.slice(3).replace(/^.* -> /, ""),
|
|
30119
|
+
index,
|
|
30120
|
+
worktree,
|
|
30121
|
+
staged: index !== " " && index !== "?",
|
|
30122
|
+
unstaged: worktree !== " " && worktree !== "?",
|
|
30123
|
+
untracked: index === "?" && worktree === "?"
|
|
30124
|
+
};
|
|
30125
|
+
})
|
|
30126
|
+
};
|
|
30127
|
+
}
|
|
30128
|
+
async function desktopGitDiff(workspace, path, staged = false) {
|
|
30129
|
+
const status = await desktopGitStatus(workspace);
|
|
30130
|
+
const file = status.files.find((item) => item.path === path);
|
|
30131
|
+
if (file === void 0) throw new Error("\u8BE5\u6587\u4EF6\u5DF2\u4E0D\u5728\u53D8\u66F4\u5217\u8868\u4E2D");
|
|
30132
|
+
if (file.untracked) {
|
|
30133
|
+
const target = safeWorkspacePath(workspace, path);
|
|
30134
|
+
const value = await readFile36(target, "utf8");
|
|
30135
|
+
return value.length > 12e4 ? `${value.slice(0, 12e4)}
|
|
30136
|
+
\u2026[\u5185\u5BB9\u5DF2\u622A\u65AD]` : value;
|
|
30137
|
+
}
|
|
30138
|
+
const result = await git2(workspace, ["diff", ...staged ? ["--staged"] : [], "--no-ext-diff", "--no-color", "--", path]);
|
|
30139
|
+
if (!result.ok) throw failure("\u8BFB\u53D6\u5DEE\u5F02", result.stderr, result.stdout);
|
|
30140
|
+
return result.stdout || "\uFF08\u6B64\u533A\u57DF\u6CA1\u6709\u5DEE\u5F02\uFF09";
|
|
30141
|
+
}
|
|
30142
|
+
async function desktopGitStage(workspace, path) {
|
|
30143
|
+
await assertListed(workspace, path);
|
|
30144
|
+
const result = await git2(workspace, ["add", "--", path]);
|
|
30145
|
+
if (!result.ok) throw failure("\u6682\u5B58", result.stderr, result.stdout);
|
|
30146
|
+
return desktopGitStatus(workspace);
|
|
30147
|
+
}
|
|
30148
|
+
async function desktopGitUnstage(workspace, path) {
|
|
30149
|
+
const file = await assertListed(workspace, path);
|
|
30150
|
+
if (!file.staged) return desktopGitStatus(workspace);
|
|
30151
|
+
const head = await git2(workspace, ["rev-parse", "--verify", "HEAD"]);
|
|
30152
|
+
const result = head.ok ? await git2(workspace, ["restore", "--staged", "--", path]) : await git2(workspace, ["rm", "--cached", "--", path]);
|
|
30153
|
+
if (!result.ok) throw failure("\u53D6\u6D88\u6682\u5B58", result.stderr, result.stdout);
|
|
30154
|
+
return desktopGitStatus(workspace);
|
|
30155
|
+
}
|
|
30156
|
+
async function desktopGitDiscard(workspace, path) {
|
|
30157
|
+
const file = await assertListed(workspace, path);
|
|
30158
|
+
if (file.untracked) await rm16(safeWorkspacePath(workspace, path), { recursive: false, force: false });
|
|
30159
|
+
else {
|
|
30160
|
+
const result = await git2(workspace, ["restore", "--worktree", "--", path]);
|
|
30161
|
+
if (!result.ok) throw failure("\u8FD8\u539F", result.stderr, result.stdout);
|
|
30162
|
+
}
|
|
30163
|
+
return desktopGitStatus(workspace);
|
|
30164
|
+
}
|
|
30165
|
+
async function desktopGitCommit(workspace, message2) {
|
|
30166
|
+
return { result: await commit(workspace, message2), status: await desktopGitStatus(workspace) };
|
|
30167
|
+
}
|
|
30168
|
+
async function desktopGitReviewDiff(workspace, input) {
|
|
30169
|
+
if (!await isGitRepository(workspace)) throw new Error("Review requires a Git repository");
|
|
30170
|
+
const paths = [...new Set(input.paths ?? [])].slice(0, 200);
|
|
30171
|
+
for (const path of paths) safeWorkspacePath(workspace, path);
|
|
30172
|
+
const suffix = paths.length === 0 ? [] : ["--", ...paths];
|
|
30173
|
+
let args;
|
|
30174
|
+
if (input.scope === "working") args = ["diff", "HEAD", "--no-ext-diff", "--no-color", ...suffix];
|
|
30175
|
+
else if (input.scope === "staged") args = ["diff", "--staged", "--no-ext-diff", "--no-color", ...suffix];
|
|
30176
|
+
else if (input.scope === "last-turn") args = ["diff", "HEAD", "--no-ext-diff", "--no-color", ...suffix];
|
|
30177
|
+
else {
|
|
30178
|
+
const target = safeGitTarget(input.target);
|
|
30179
|
+
args = input.scope === "commit" ? ["show", "--format=", "--no-ext-diff", "--no-color", target] : ["diff", `${target}...HEAD`, "--no-ext-diff", "--no-color"];
|
|
30180
|
+
}
|
|
30181
|
+
const result = await git2(workspace, args, 3e4);
|
|
30182
|
+
if (!result.ok) throw failure("\u8BFB\u53D6\u5BA1\u67E5\u5DEE\u5F02", result.stderr, result.stdout);
|
|
30183
|
+
return result.stdout.length > 24e4 ? `${result.stdout.slice(0, 24e4)}
|
|
30184
|
+
\u2026[diff truncated]` : result.stdout || "\uFF08\u6B64\u8303\u56F4\u6CA1\u6709\u5DEE\u5F02\uFF09";
|
|
30185
|
+
}
|
|
30186
|
+
function desktopGitHistory(workspace) {
|
|
30187
|
+
return fileHistory(workspace, void 0, 50);
|
|
30188
|
+
}
|
|
30189
|
+
async function desktopLastTurnDiff(workspace, sessionId, paths = []) {
|
|
30190
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(sessionId)) throw new Error("Invalid session id");
|
|
30191
|
+
const requested = new Set(paths.map((path) => {
|
|
30192
|
+
safeWorkspacePath(workspace, path);
|
|
30193
|
+
return path.replaceAll("\\", "/");
|
|
30194
|
+
}));
|
|
30195
|
+
try {
|
|
30196
|
+
const tree = JSON.parse(await readFile36(resolve36(workspace, ".flavor", "session-trees", sessionId, "tree.json"), "utf8"));
|
|
30197
|
+
const leaf = tree.nodes?.find((node) => node.id === tree.leafId);
|
|
30198
|
+
const parent = leaf?.parentId == null ? void 0 : tree.nodes?.find((node) => node.id === leaf.parentId);
|
|
30199
|
+
if (leaf === void 0 || parent === void 0) return desktopGitReviewDiff(workspace, { scope: "last-turn", paths });
|
|
30200
|
+
const load = async (id) => {
|
|
30201
|
+
if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
|
|
30202
|
+
return JSON.parse(await readFile36(resolve36(workspace, ".flavor", "checkpoints", "manifests", `${id}.json`), "utf8"));
|
|
30203
|
+
};
|
|
30204
|
+
const [before, after] = await Promise.all([load(parent.checkpointId), load(leaf.checkpointId)]);
|
|
30205
|
+
const oldFiles = new Map(before.files.map((file) => [file.path, file.digest]));
|
|
30206
|
+
const newFiles = new Map(after.files.map((file) => [file.path, file.digest]));
|
|
30207
|
+
const changed = [.../* @__PURE__ */ new Set([...oldFiles.keys(), ...newFiles.keys()])].filter((path) => oldFiles.get(path) !== newFiles.get(path) && (requested.size === 0 || requested.has(path))).slice(0, 200);
|
|
30208
|
+
const object = async (digest) => {
|
|
30209
|
+
if (digest === void 0) return "";
|
|
30210
|
+
if (!/^[a-f0-9]{64}$/.test(digest)) throw new Error("Invalid checkpoint object");
|
|
30211
|
+
const value = await readFile36(resolve36(workspace, ".flavor", "checkpoints", "objects", digest));
|
|
30212
|
+
return value.includes(0) ? "[binary content]" : value.toString("utf8");
|
|
30213
|
+
};
|
|
30214
|
+
const chunks = await Promise.all(changed.map(async (path) => wholeFileDiff(path, await object(oldFiles.get(path)), await object(newFiles.get(path)))));
|
|
30215
|
+
const result = chunks.join("\n");
|
|
30216
|
+
return result.length > 24e4 ? `${result.slice(0, 24e4)}
|
|
30217
|
+
\u2026[diff truncated]` : result || "\uFF08\u4E0A\u4E00\u4E2A assistant turn \u6CA1\u6709\u6587\u4EF6\u53D8\u5316\uFF09";
|
|
30218
|
+
} catch (error) {
|
|
30219
|
+
if (error.code === "ENOENT") return desktopGitReviewDiff(workspace, { scope: "last-turn", paths });
|
|
30220
|
+
throw error;
|
|
30221
|
+
}
|
|
30222
|
+
}
|
|
30223
|
+
function wholeFileDiff(path, before, after) {
|
|
30224
|
+
const oldLines = before.replace(/\n$/, "").split("\n");
|
|
30225
|
+
const newLines = after.replace(/\n$/, "").split("\n");
|
|
30226
|
+
return [`diff --flavor a/${path} b/${path}`, `--- ${before === "" ? "/dev/null" : `a/${path}`}`, `+++ ${after === "" ? "/dev/null" : `b/${path}`}`, `@@ -1,${before === "" ? 0 : oldLines.length} +1,${after === "" ? 0 : newLines.length} @@`, ...before === "" ? [] : oldLines.map((line) => `-${line}`), ...after === "" ? [] : newLines.map((line) => `+${line}`)].join("\n");
|
|
30227
|
+
}
|
|
30228
|
+
async function assertListed(workspace, path) {
|
|
30229
|
+
const file = (await desktopGitStatus(workspace)).files.find((item) => item.path === path);
|
|
30230
|
+
if (file === void 0) throw new Error("\u8BE5\u6587\u4EF6\u5DF2\u4E0D\u5728\u53D8\u66F4\u5217\u8868\u4E2D");
|
|
30231
|
+
safeWorkspacePath(workspace, path);
|
|
30232
|
+
return file;
|
|
30233
|
+
}
|
|
30234
|
+
function safeWorkspacePath(workspace, path) {
|
|
30235
|
+
if (isAbsolute18(path)) throw new Error("Git \u6587\u4EF6\u8DEF\u5F84\u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684\u76F8\u5BF9\u8DEF\u5F84");
|
|
30236
|
+
const root = resolve36(workspace);
|
|
30237
|
+
const target = resolve36(root, path);
|
|
30238
|
+
const child = relative26(root, target);
|
|
30239
|
+
if (child === "" || child.startsWith("..") || isAbsolute18(child)) throw new Error("\u62D2\u7EDD\u8BBF\u95EE\u9879\u76EE\u4E4B\u5916\u7684\u8DEF\u5F84");
|
|
30240
|
+
return target;
|
|
30241
|
+
}
|
|
30242
|
+
function safeGitTarget(value) {
|
|
30243
|
+
const target = value?.trim();
|
|
30244
|
+
if (target === void 0 || target.length === 0 || target.length > 256 || target.startsWith("-") || !/^[A-Za-z0-9][A-Za-z0-9._/@{}~^:-]*$/.test(target)) throw new Error("Invalid Git review target");
|
|
30245
|
+
return target;
|
|
30246
|
+
}
|
|
30247
|
+
|
|
30248
|
+
// src/desktop/worktree-manager.ts
|
|
30249
|
+
import { randomUUID as randomUUID26 } from "crypto";
|
|
30250
|
+
import { mkdir as mkdir28, readFile as readFile37, readdir as readdir13, rm as rm17, stat as stat15, writeFile as writeFile18 } from "fs/promises";
|
|
30251
|
+
import { basename as basename10, isAbsolute as isAbsolute19, join as join43, relative as relative27, resolve as resolve37, sep as sep27 } from "path";
|
|
30252
|
+
var DesktopWorktreeManager = class {
|
|
30253
|
+
#repository;
|
|
30254
|
+
#storage;
|
|
30255
|
+
constructor(options) {
|
|
30256
|
+
this.#repository = resolve37(options.repository);
|
|
30257
|
+
this.#storage = resolve37(options.storage);
|
|
30258
|
+
}
|
|
30259
|
+
async create(label) {
|
|
30260
|
+
if (!await isGitRepository(this.#repository)) throw new Error("Task worktrees require a Git repository");
|
|
30261
|
+
const safe = label.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 36) || "task";
|
|
30262
|
+
const suffix = randomUUID26().slice(0, 8);
|
|
30263
|
+
const id = `worktree-${safe}-${suffix}`;
|
|
30264
|
+
const branch = `flavor/desktop-${safe}-${suffix}`;
|
|
30265
|
+
const path = this.#safeStoragePath(id);
|
|
30266
|
+
await mkdir28(this.#storage, { recursive: true, mode: 448 });
|
|
30267
|
+
const result = await git2(this.#repository, ["worktree", "add", "-b", branch, path, "HEAD"], 6e4);
|
|
30268
|
+
if (!result.ok) throw new Error(`git worktree add failed: ${(result.stderr || result.stdout).trim().split("\n")[0]}`);
|
|
30269
|
+
const stored = {
|
|
30270
|
+
version: 1,
|
|
30271
|
+
id,
|
|
30272
|
+
mode: "worktree",
|
|
30273
|
+
repository: this.#repository,
|
|
30274
|
+
path,
|
|
30275
|
+
branch,
|
|
30276
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30277
|
+
};
|
|
30278
|
+
try {
|
|
30279
|
+
await writeFile18(this.#metadataPath(id), `${JSON.stringify(stored)}
|
|
30280
|
+
`, { encoding: "utf8", mode: 384 });
|
|
30281
|
+
} catch (error) {
|
|
30282
|
+
await git2(this.#repository, ["worktree", "remove", "--force", path], 6e4).catch(() => void 0);
|
|
30283
|
+
throw error;
|
|
30284
|
+
}
|
|
30285
|
+
return { ...stored, dirty: false, merged: false };
|
|
30286
|
+
}
|
|
30287
|
+
async list() {
|
|
30288
|
+
let names;
|
|
30289
|
+
try {
|
|
30290
|
+
names = await readdir13(this.#storage);
|
|
30291
|
+
} catch (error) {
|
|
30292
|
+
if (error.code === "ENOENT") return [];
|
|
30293
|
+
throw error;
|
|
30294
|
+
}
|
|
30295
|
+
const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
|
|
30296
|
+
try {
|
|
30297
|
+
const stored = validateStored(JSON.parse(await readFile37(join43(this.#storage, name), "utf8")), this.#repository, this.#storage);
|
|
30298
|
+
if ((await stat15(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
30299
|
+
const status = await git2(stored.path, ["status", "--porcelain"]);
|
|
30300
|
+
const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
|
|
30301
|
+
const { version: _version, ...item } = stored;
|
|
30302
|
+
return { ...item, dirty: status.ok ? status.stdout.trim().length > 0 : true, merged: merged.ok };
|
|
30303
|
+
} catch {
|
|
30304
|
+
return void 0;
|
|
30305
|
+
}
|
|
30306
|
+
}));
|
|
30307
|
+
return values.filter((item) => item !== void 0).sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
30308
|
+
}
|
|
30309
|
+
async remove(id, force = false) {
|
|
30310
|
+
const item = await this.#load(id);
|
|
30311
|
+
const status = await git2(item.path, ["status", "--porcelain"]);
|
|
30312
|
+
const dirty = !status.ok || status.stdout.trim().length > 0;
|
|
30313
|
+
if (dirty && !force) throw new Error("Worktree is dirty; review or explicitly force removal");
|
|
30314
|
+
const result = await git2(this.#repository, ["worktree", "remove", ...force ? ["--force"] : [], item.path], 6e4);
|
|
30315
|
+
if (!result.ok) throw new Error(`git worktree remove failed: ${(result.stderr || result.stdout).trim().split("\n")[0]}`);
|
|
30316
|
+
await rm17(this.#metadataPath(id), { force: true });
|
|
30317
|
+
}
|
|
30318
|
+
async merge(id) {
|
|
30319
|
+
const item = await this.#load(id);
|
|
30320
|
+
const [taskStatus, repositoryStatus] = await Promise.all([
|
|
30321
|
+
git2(item.path, ["status", "--porcelain"]),
|
|
30322
|
+
git2(this.#repository, ["status", "--porcelain"])
|
|
30323
|
+
]);
|
|
30324
|
+
if (!taskStatus.ok || taskStatus.stdout.trim().length > 0) throw new Error("Commit or discard task worktree changes before handoff");
|
|
30325
|
+
if (!repositoryStatus.ok || repositoryStatus.stdout.trim().length > 0) throw new Error("The destination checkout must be clean before handoff");
|
|
30326
|
+
const result = await git2(this.#repository, ["merge", "--no-ff", "--no-edit", item.branch], 6e4);
|
|
30327
|
+
if (!result.ok) throw new Error(`Worktree handoff merge failed: ${(result.stderr || result.stdout).trim().split("\n")[0]}`);
|
|
30328
|
+
}
|
|
30329
|
+
async #load(id) {
|
|
30330
|
+
const path = this.#metadataPath(id);
|
|
30331
|
+
return validateStored(JSON.parse(await readFile37(path, "utf8")), this.#repository, this.#storage);
|
|
30332
|
+
}
|
|
30333
|
+
#metadataPath(id) {
|
|
30334
|
+
if (!/^worktree-[a-z0-9._-]{1,80}$/.test(id)) throw new Error("Invalid worktree id");
|
|
30335
|
+
return this.#safeStoragePath(`${id}.json`);
|
|
30336
|
+
}
|
|
30337
|
+
#safeStoragePath(name) {
|
|
30338
|
+
const target = resolve37(this.#storage, name);
|
|
30339
|
+
const delta = relative27(this.#storage, target);
|
|
30340
|
+
if (delta === "" || delta === ".." || delta.startsWith(`..${sep27}`) || isAbsolute19(delta)) throw new Error("Worktree path escapes storage");
|
|
30341
|
+
return target;
|
|
30342
|
+
}
|
|
30343
|
+
};
|
|
30344
|
+
function validateStored(value, repository, storage) {
|
|
30345
|
+
if (typeof value !== "object" || value === null) throw new Error("Invalid worktree metadata");
|
|
30346
|
+
const item = value;
|
|
30347
|
+
if (item.version !== 1 || item.mode !== "worktree" || typeof item.id !== "string" || typeof item.path !== "string" || typeof item.branch !== "string" || typeof item.createdAt !== "string" || resolve37(item.repository ?? "") !== repository) {
|
|
30348
|
+
throw new Error("Invalid worktree metadata");
|
|
30349
|
+
}
|
|
30350
|
+
const path = resolve37(item.path);
|
|
30351
|
+
const delta = relative27(storage, path);
|
|
30352
|
+
if (delta === "" || delta === ".." || delta.startsWith(`..${sep27}`) || isAbsolute19(delta) || basename10(path) !== item.id) {
|
|
30353
|
+
throw new Error("Worktree metadata escapes storage");
|
|
30354
|
+
}
|
|
30355
|
+
return { version: 1, id: item.id, mode: "worktree", repository, path, branch: item.branch, createdAt: item.createdAt };
|
|
30356
|
+
}
|
|
30357
|
+
|
|
30358
|
+
// src/desktop/workbench-service.ts
|
|
30359
|
+
import { readFile as readFile38, readdir as readdir14, stat as stat16 } from "fs/promises";
|
|
30360
|
+
import { homedir as homedir5 } from "os";
|
|
30361
|
+
import { join as join44, resolve as resolve38 } from "path";
|
|
30362
|
+
var MAX_TEXT = 128e3;
|
|
30363
|
+
var MAX_ITEMS = 200;
|
|
30364
|
+
var DesktopWorkbenchService = class {
|
|
30365
|
+
#workspace;
|
|
30366
|
+
#home;
|
|
30367
|
+
constructor(workspace, home = homedir5()) {
|
|
30368
|
+
this.#workspace = resolve38(workspace);
|
|
30369
|
+
this.#home = resolve38(home);
|
|
30370
|
+
}
|
|
30371
|
+
async inspect(sessionId) {
|
|
30372
|
+
const [goals, instructions, permissionFiles, audit, context] = await Promise.all([
|
|
30373
|
+
this.#goals(),
|
|
30374
|
+
this.#instructions(),
|
|
30375
|
+
this.#permissionFiles(),
|
|
30376
|
+
this.#audit(),
|
|
30377
|
+
this.#context(sessionId)
|
|
30378
|
+
]);
|
|
30379
|
+
return { goals, instructions, permissionFiles, audit, ...context === void 0 ? {} : { context } };
|
|
30380
|
+
}
|
|
30381
|
+
static normalizePreviewUrl(input) {
|
|
30382
|
+
let url;
|
|
30383
|
+
try {
|
|
30384
|
+
url = new URL(input.trim());
|
|
30385
|
+
} catch {
|
|
30386
|
+
throw new Error("Preview URL must be a valid loopback HTTP(S) URL");
|
|
30387
|
+
}
|
|
30388
|
+
if (!["http:", "https:"].includes(url.protocol) || !["127.0.0.1", "localhost", "::1", "[::1]"].includes(url.hostname.toLowerCase()) || url.username !== "" || url.password !== "") {
|
|
30389
|
+
throw new Error("Preview URL must use a loopback HTTP(S) origin");
|
|
30390
|
+
}
|
|
30391
|
+
return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
|
|
30392
|
+
}
|
|
30393
|
+
async #goals() {
|
|
30394
|
+
const root = join44(this.#workspace, ".flavor", "goals");
|
|
30395
|
+
const names = await readdir14(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
|
|
30396
|
+
const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
|
|
30397
|
+
try {
|
|
30398
|
+
return GoalStateSchema.parse(JSON.parse(await boundedRead2(join44(root, name))));
|
|
30399
|
+
} catch {
|
|
30400
|
+
return void 0;
|
|
30401
|
+
}
|
|
30402
|
+
}));
|
|
30403
|
+
return values.filter((item) => item !== void 0).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
30404
|
+
}
|
|
30405
|
+
async #instructions() {
|
|
30406
|
+
const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
30407
|
+
const items = await Promise.all(names.map(async (name) => {
|
|
30408
|
+
const path = join44(this.#workspace, name);
|
|
30409
|
+
try {
|
|
30410
|
+
return { name, path, content: redactErrorText(await boundedRead2(path)) };
|
|
30411
|
+
} catch (error) {
|
|
30412
|
+
if (error.code === "ENOENT") return void 0;
|
|
30413
|
+
throw error;
|
|
30414
|
+
}
|
|
30415
|
+
}));
|
|
30416
|
+
return items.filter((item) => item !== void 0);
|
|
30417
|
+
}
|
|
30418
|
+
async #permissionFiles() {
|
|
30419
|
+
const candidates = [
|
|
30420
|
+
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve38(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
|
|
30421
|
+
{ tier: "user", path: join44(this.#home, ".flavor-code", "permissions.json"), content: "" },
|
|
30422
|
+
{ tier: "project", path: join44(this.#workspace, ".flavor", "permissions.json"), content: "" },
|
|
30423
|
+
{ tier: "local", path: join44(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
|
|
30424
|
+
];
|
|
30425
|
+
const values = await Promise.all(candidates.map(async (item) => {
|
|
30426
|
+
try {
|
|
30427
|
+
return { ...item, content: redactErrorText(await boundedRead2(item.path)) };
|
|
30428
|
+
} catch (error) {
|
|
30429
|
+
if (error.code === "ENOENT") return void 0;
|
|
30430
|
+
throw error;
|
|
30431
|
+
}
|
|
30432
|
+
}));
|
|
30433
|
+
return values.filter((item) => item !== void 0);
|
|
30434
|
+
}
|
|
30435
|
+
async #audit() {
|
|
30436
|
+
let raw;
|
|
30437
|
+
try {
|
|
30438
|
+
raw = await boundedRead2(join44(this.#workspace, ".flavor", "audit.jsonl"));
|
|
30439
|
+
} catch (error) {
|
|
30440
|
+
if (error.code === "ENOENT") return [];
|
|
30441
|
+
throw error;
|
|
30442
|
+
}
|
|
30443
|
+
return raw.split(/\r?\n/).filter(Boolean).slice(-MAX_ITEMS).flatMap((line) => {
|
|
30444
|
+
try {
|
|
30445
|
+
const value = JSON.parse(line);
|
|
30446
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? [redact2(value)] : [];
|
|
30447
|
+
} catch {
|
|
30448
|
+
return [];
|
|
30449
|
+
}
|
|
30450
|
+
}).reverse();
|
|
30451
|
+
}
|
|
30452
|
+
async #context(sessionId) {
|
|
30453
|
+
const usage = await readJsonLines2(join44(this.#workspace, ".flavor", "usage.jsonl"));
|
|
30454
|
+
if (sessionId === void 0) return { visibility: [], usage };
|
|
30455
|
+
try {
|
|
30456
|
+
const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
|
|
30457
|
+
return { ...document.conversation.epoch === void 0 ? {} : { epoch: redact2(document.conversation.epoch) }, visibility: (document.conversation.visibilityLog ?? []).slice(-MAX_ITEMS).map((item) => redact2(item)), usage };
|
|
30458
|
+
} catch {
|
|
30459
|
+
return { visibility: [], usage };
|
|
30460
|
+
}
|
|
30461
|
+
}
|
|
30462
|
+
};
|
|
30463
|
+
async function boundedRead2(path) {
|
|
30464
|
+
const info = await stat16(path);
|
|
30465
|
+
if (!info.isFile()) throw new Error(`Expected a file: ${path}`);
|
|
30466
|
+
if (info.size > MAX_TEXT) throw new Error(`Workbench file exceeds ${MAX_TEXT} bytes: ${path}`);
|
|
30467
|
+
return readFile38(path, "utf8");
|
|
30468
|
+
}
|
|
30469
|
+
async function readJsonLines2(path) {
|
|
30470
|
+
try {
|
|
30471
|
+
return (await boundedRead2(path)).split(/\r?\n/).filter(Boolean).slice(-MAX_ITEMS).flatMap((line) => {
|
|
30472
|
+
try {
|
|
30473
|
+
const value = JSON.parse(line);
|
|
30474
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? [redact2(value)] : [];
|
|
30475
|
+
} catch {
|
|
30476
|
+
return [];
|
|
30477
|
+
}
|
|
30478
|
+
}).reverse();
|
|
30479
|
+
} catch (error) {
|
|
30480
|
+
if (error.code === "ENOENT") return [];
|
|
30481
|
+
throw error;
|
|
30482
|
+
}
|
|
30483
|
+
}
|
|
30484
|
+
function redact2(value, key = "") {
|
|
30485
|
+
if (/token|secret|password|authorization|api[-_]?key/i.test(key)) return "[REDACTED]";
|
|
30486
|
+
if (Array.isArray(value)) return value.slice(0, MAX_ITEMS).map((item) => redact2(item));
|
|
30487
|
+
if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).slice(0, MAX_ITEMS).map(([name, item]) => [name, redact2(item, name)]));
|
|
30488
|
+
return typeof value === "string" && value.length > MAX_TEXT ? `${value.slice(0, MAX_TEXT)}\u2026` : value;
|
|
30489
|
+
}
|
|
30490
|
+
|
|
30491
|
+
// src/desktop/astgraph-service.ts
|
|
30492
|
+
import { stat as stat17 } from "fs/promises";
|
|
30493
|
+
import { createRequire as nodeCreateRequire2 } from "module";
|
|
30494
|
+
import { join as join45, resolve as resolve39 } from "path";
|
|
30495
|
+
var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
|
|
30496
|
+
var DesktopAstGraphService = class {
|
|
30497
|
+
#path;
|
|
30498
|
+
constructor(workspace) {
|
|
30499
|
+
this.#path = join45(resolve39(workspace), ".flavor", "astgraph", "index.db");
|
|
30500
|
+
}
|
|
30501
|
+
async status() {
|
|
30502
|
+
if ((await stat17(this.#path).catch(() => void 0))?.isFile() !== true) {
|
|
30503
|
+
return { available: false, path: this.#path, files: 0, nodes: 0, edges: 0 };
|
|
30504
|
+
}
|
|
30505
|
+
return this.#read((db) => {
|
|
30506
|
+
const count = (table) => Number(db.prepare(`SELECT COUNT(*) AS count FROM ${table}`).get().count);
|
|
30507
|
+
const metadata = db.prepare("SELECT value FROM project_metadata WHERE key = 'last_index'").get();
|
|
30508
|
+
let indexedAt;
|
|
30509
|
+
try {
|
|
30510
|
+
const at = Number(JSON.parse(metadata?.value ?? "{}").at);
|
|
30511
|
+
if (Number.isFinite(at)) indexedAt = new Date(at).toISOString();
|
|
30512
|
+
} catch {
|
|
30513
|
+
}
|
|
30514
|
+
return { available: true, path: this.#path, files: count("files"), nodes: count("nodes"), edges: count("edges"), ...indexedAt === void 0 ? {} : { indexedAt } };
|
|
30515
|
+
});
|
|
30516
|
+
}
|
|
30517
|
+
async search(query, limit = 40) {
|
|
30518
|
+
const value = query.trim().slice(0, 200);
|
|
30519
|
+
if (value.length === 0 || !(await this.status()).available) return [];
|
|
30520
|
+
return this.#read((db) => db.prepare(`SELECT ${FIELDS} FROM nodes WHERE lower(name) LIKE ? OR lower(qualified_name) LIKE ? ORDER BY CASE WHEN lower(name) = ? THEN 0 ELSE 1 END, name LIMIT ?`).all(`%${value.toLowerCase()}%`, `%${value.toLowerCase()}%`, value.toLowerCase(), Math.min(Math.max(limit, 1), 100)).map(mapNode));
|
|
30521
|
+
}
|
|
30522
|
+
async relations(id, hops = 2) {
|
|
30523
|
+
if (!/^[A-Za-z0-9._:/#@+~-]{1,512}$/.test(id)) throw new Error("Invalid AST node id");
|
|
30524
|
+
if (!(await this.status()).available) return { callers: [], callees: [], impact: [] };
|
|
30525
|
+
return this.#read((db) => {
|
|
30526
|
+
const originRow = db.prepare(`SELECT ${FIELDS} FROM nodes WHERE id = ?`).get(id);
|
|
30527
|
+
const linked = (direction) => {
|
|
30528
|
+
const source = direction === "callers" ? "e.source" : "e.target";
|
|
30529
|
+
const predicate = direction === "callers" ? "e.target = ?" : "e.source = ?";
|
|
30530
|
+
return db.prepare(`SELECT DISTINCT n.${FIELDS.split(", ").join(", n.")} FROM edges e JOIN nodes n ON n.id = ${source} WHERE ${predicate} AND e.kind IN ('calls','imports') LIMIT 100`).all(id).map(mapNode);
|
|
30531
|
+
};
|
|
30532
|
+
const seen = /* @__PURE__ */ new Map();
|
|
30533
|
+
let frontier = [id];
|
|
30534
|
+
for (let hop = 1; hop <= Math.min(Math.max(hops, 1), 4) && frontier.length > 0 && seen.size < 200; hop += 1) {
|
|
30535
|
+
const next = [];
|
|
30536
|
+
for (const current of frontier) {
|
|
30537
|
+
const rows = db.prepare("SELECT source AS other FROM edges WHERE target = ? UNION SELECT target AS other FROM edges WHERE source = ? LIMIT 200").all(current, current);
|
|
30538
|
+
for (const row of rows) if (row.other !== id && !seen.has(row.other)) {
|
|
30539
|
+
seen.set(row.other, hop);
|
|
30540
|
+
next.push(row.other);
|
|
30541
|
+
}
|
|
30542
|
+
}
|
|
30543
|
+
frontier = next;
|
|
30544
|
+
}
|
|
30545
|
+
const impact = [...seen].flatMap(([nodeId, hop]) => {
|
|
30546
|
+
const row = db.prepare(`SELECT ${FIELDS} FROM nodes WHERE id = ?`).get(nodeId);
|
|
30547
|
+
return row === void 0 ? [] : [{ ...mapNode(row), hop }];
|
|
30548
|
+
});
|
|
30549
|
+
return { ...originRow === void 0 ? {} : { origin: mapNode(originRow) }, callers: linked("callers"), callees: linked("callees"), impact };
|
|
30550
|
+
});
|
|
30551
|
+
}
|
|
30552
|
+
#read(run) {
|
|
30553
|
+
const sqlite = nodeCreateRequire2(import.meta.url)("node:sqlite");
|
|
30554
|
+
const db = new sqlite.DatabaseSync(this.#path, { readOnly: true });
|
|
30555
|
+
try {
|
|
30556
|
+
return run(db);
|
|
30557
|
+
} finally {
|
|
30558
|
+
db.close();
|
|
30559
|
+
}
|
|
30560
|
+
}
|
|
30561
|
+
};
|
|
30562
|
+
function mapNode(value) {
|
|
30563
|
+
const row = value;
|
|
30564
|
+
return { id: String(row.id), kind: String(row.kind), name: String(row.name), qualifiedName: String(row.qualified_name), filePath: String(row.file_path), language: String(row.language), startLine: Number(row.start_line), endLine: Number(row.end_line), ...row.signature == null ? {} : { signature: String(row.signature) } };
|
|
30565
|
+
}
|
|
30566
|
+
|
|
29834
30567
|
// src/desktop/main.ts
|
|
29835
30568
|
installCrashGuard();
|
|
29836
30569
|
var moduleDirectory = dirname24(fileURLToPath4(import.meta.url));
|
|
@@ -29840,7 +30573,7 @@ var appMenu;
|
|
|
29840
30573
|
var quitting = false;
|
|
29841
30574
|
function logStartup(step, detail) {
|
|
29842
30575
|
try {
|
|
29843
|
-
const logPath =
|
|
30576
|
+
const logPath = join46(dirname24(process.execPath), "flavor-code-startup.log");
|
|
29844
30577
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
29845
30578
|
appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
|
|
29846
30579
|
`);
|
|
@@ -29865,7 +30598,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
|
|
|
29865
30598
|
};
|
|
29866
30599
|
});
|
|
29867
30600
|
var d2cJudgeClient = createD2cJudgeClient();
|
|
29868
|
-
var judgeStore = () => createD2cJudgeConfigStore(
|
|
30601
|
+
var judgeStore = () => createD2cJudgeConfigStore(join46(app.getPath("userData"), "d2c-judge.json"));
|
|
29869
30602
|
function judgeImage(png) {
|
|
29870
30603
|
const image = nativeImage.createFromBuffer(png);
|
|
29871
30604
|
const size = image.getSize();
|
|
@@ -29874,70 +30607,342 @@ function judgeImage(png) {
|
|
|
29874
30607
|
const ratio = 2048 / longest;
|
|
29875
30608
|
return image.resize({ width: Math.max(1, Math.round(size.width * ratio)), height: Math.max(1, Math.round(size.height * ratio)), quality: "best" }).toPNG();
|
|
29876
30609
|
}
|
|
29877
|
-
var
|
|
29878
|
-
|
|
29879
|
-
|
|
29880
|
-
|
|
29881
|
-
|
|
29882
|
-
|
|
29883
|
-
|
|
29884
|
-
|
|
29885
|
-
|
|
29886
|
-
|
|
29887
|
-
|
|
29888
|
-
|
|
29889
|
-
|
|
29890
|
-
|
|
29891
|
-
|
|
29892
|
-
|
|
29893
|
-
|
|
30610
|
+
var workbench = {
|
|
30611
|
+
projectMeta: {},
|
|
30612
|
+
sessionMeta: {},
|
|
30613
|
+
activities: [],
|
|
30614
|
+
recoveryItems: [],
|
|
30615
|
+
runningSessions: [],
|
|
30616
|
+
taskCheckouts: [],
|
|
30617
|
+
cleanShutdown: true
|
|
30618
|
+
};
|
|
30619
|
+
var managedProjects = /* @__PURE__ */ new Map();
|
|
30620
|
+
var projectOrder = [];
|
|
30621
|
+
var activeWorkspace;
|
|
30622
|
+
function sessionMetaKey(workspace, sessionId) {
|
|
30623
|
+
return `${workspace}\0${sessionId}`;
|
|
30624
|
+
}
|
|
30625
|
+
function worktreeManager(workspace) {
|
|
30626
|
+
const key = createHash27("sha256").update(resolve40(workspace)).digest("hex").slice(0, 20);
|
|
30627
|
+
return new DesktopWorktreeManager({ repository: workspace, storage: join46(app.getPath("userData"), "task-worktrees", key) });
|
|
30628
|
+
}
|
|
30629
|
+
function activeWorkingDirectory() {
|
|
30630
|
+
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
30631
|
+
return managedProjects.get(activeWorkspace)?.selectedTask?.workingDirectory ?? activeWorkspace;
|
|
30632
|
+
}
|
|
30633
|
+
function projectSessions(workspace, project) {
|
|
30634
|
+
const merged = new Map(project.snapshot.sessions.map((session) => [session.sessionId, session]));
|
|
30635
|
+
const taskBySession = new Map([...project.tasks].flatMap((task) => task.sessionId === void 0 ? [] : [[task.sessionId, task]]));
|
|
30636
|
+
for (const task of project.tasks) for (const session of task.snapshot.sessions) {
|
|
30637
|
+
const previous = merged.get(session.sessionId);
|
|
30638
|
+
if (previous === void 0 || session.updatedAt >= previous.updatedAt) merged.set(session.sessionId, session);
|
|
30639
|
+
}
|
|
30640
|
+
return [...merged.values()].map((session) => {
|
|
30641
|
+
const meta = workbench.sessionMeta[sessionMetaKey(workspace, session.sessionId)] ?? {};
|
|
30642
|
+
const activity = workbench.activities.find((item) => item.workspace === workspace && item.sessionId === session.sessionId);
|
|
30643
|
+
const running = [...project.tasks].some((task2) => task2.sessionId === session.sessionId && task2.snapshot.activeSession?.busy);
|
|
30644
|
+
const task = taskBySession.get(session.sessionId);
|
|
30645
|
+
return { ...session, ...meta, ...task === void 0 ? {} : { environment: task.environment, workingDirectory: task.workingDirectory, ...task.worktreeId === void 0 ? {} : { worktreeId: task.worktreeId } }, ...running ? { activity: "running" } : activity === void 0 ? {} : { activity: activity.kind, unread: activity.unread } };
|
|
30646
|
+
}).sort((left, right) => Number(Boolean(right.pinned)) - Number(Boolean(left.pinned)) || right.updatedAt.localeCompare(left.updatedAt));
|
|
30647
|
+
}
|
|
30648
|
+
function projectSummaries() {
|
|
30649
|
+
return [...projectOrder].sort((left, right) => Number(Boolean(workbench.projectMeta[right]?.pinned)) - Number(Boolean(workbench.projectMeta[left]?.pinned))).flatMap((workspace) => {
|
|
30650
|
+
const project = managedProjects.get(workspace);
|
|
30651
|
+
if (project === void 0) return [];
|
|
30652
|
+
const snapshot4 = project.selectedTask?.snapshot ?? project.snapshot;
|
|
30653
|
+
const active = snapshot4.activeSession;
|
|
30654
|
+
const runningSessions = [...project.tasks].flatMap((task) => task.sessionId !== void 0 && task.snapshot.activeSession?.busy ? [task.sessionId] : []);
|
|
30655
|
+
return [{
|
|
30656
|
+
workspace,
|
|
30657
|
+
...workbench.projectMeta[workspace],
|
|
30658
|
+
sessions: projectSessions(workspace, project),
|
|
30659
|
+
...active === void 0 ? {} : { activeSession: { sessionId: active.sessionId, busy: active.busy } },
|
|
30660
|
+
running: runningSessions.length > 0 || snapshot4.jobs.some((job) => job.state === "running"),
|
|
30661
|
+
runningSessions,
|
|
30662
|
+
unreadCount: workbench.activities.filter((item) => item.workspace === workspace && item.unread).length
|
|
30663
|
+
}];
|
|
30664
|
+
});
|
|
30665
|
+
}
|
|
30666
|
+
function decorateSnapshot(snapshot4) {
|
|
30667
|
+
const workspace = snapshot4.workspace !== void 0 && managedProjects.has(snapshot4.workspace) ? snapshot4.workspace : activeWorkspace ?? snapshot4.workspace;
|
|
30668
|
+
const selected = workspace === void 0 ? void 0 : managedProjects.get(workspace)?.selectedTask;
|
|
30669
|
+
return {
|
|
30670
|
+
...snapshot4,
|
|
30671
|
+
...workspace === void 0 ? {} : { workspace },
|
|
30672
|
+
...snapshot4.activeSession === void 0 || selected === void 0 ? {} : { activeSession: { ...snapshot4.activeSession, environment: selected.environment, workingDirectory: selected.workingDirectory, ...selected.worktreeId === void 0 ? {} : { worktreeId: selected.worktreeId } } },
|
|
30673
|
+
sessions: workspace === void 0 ? snapshot4.sessions : projectSessions(workspace, managedProjects.get(workspace) ?? { controller, snapshot: snapshot4, tasks: /* @__PURE__ */ new Set() }),
|
|
30674
|
+
projects: projectSummaries(),
|
|
30675
|
+
activities: workbench.activities,
|
|
30676
|
+
recoveryItems: workbench.recoveryItems
|
|
30677
|
+
};
|
|
30678
|
+
}
|
|
30679
|
+
function decorateSessionStarted(payload) {
|
|
30680
|
+
return { ...payload, snapshot: decorateSnapshot(payload.snapshot) };
|
|
30681
|
+
}
|
|
30682
|
+
function emitManagedDesktopEvent(workspace, owner2, event) {
|
|
30683
|
+
if (workspace === void 0) {
|
|
30684
|
+
emitDesktopEvent(event.type === "snapshot" ? { ...event, snapshot: decorateSnapshot(event.snapshot) } : event);
|
|
30685
|
+
return;
|
|
30686
|
+
}
|
|
30687
|
+
const project = managedProjects.get(workspace);
|
|
30688
|
+
if (project !== void 0) {
|
|
30689
|
+
if (event.type === "snapshot") {
|
|
30690
|
+
event = { ...event, snapshot: { ...event.snapshot, workspace } };
|
|
30691
|
+
const previous = owner2?.snapshot;
|
|
30692
|
+
if (owner2 !== void 0) {
|
|
30693
|
+
owner2.snapshot = event.snapshot;
|
|
30694
|
+
if (owner2.payload !== void 0) owner2.payload = { ...owner2.payload, snapshot: event.snapshot };
|
|
30695
|
+
} else project.snapshot = event.snapshot;
|
|
30696
|
+
trackTaskTransition(workspace, owner2, previous, event.snapshot);
|
|
30697
|
+
} else if (event.type === "session-started") {
|
|
30698
|
+
event = { ...event, payload: { ...event.payload, snapshot: { ...event.payload.snapshot, workspace } } };
|
|
30699
|
+
if (owner2 !== void 0) {
|
|
30700
|
+
owner2.sessionId = event.payload.sessionId;
|
|
30701
|
+
owner2.payload = event.payload;
|
|
30702
|
+
owner2.snapshot = event.payload.snapshot;
|
|
30703
|
+
} else project.snapshot = event.payload.snapshot;
|
|
30704
|
+
} else if (event.type === "session-output" && owner2 !== void 0) {
|
|
30705
|
+
if (event.event.type === "done") owner2.terminal = "completed";
|
|
30706
|
+
else if (event.event.type === "error") owner2.terminal = "failed";
|
|
30707
|
+
else if (event.event.type === "exit") owner2.terminal = "interrupted";
|
|
30708
|
+
} else if (event.type === "runtime-error") {
|
|
30709
|
+
if (owner2 !== void 0) owner2.terminal = "failed";
|
|
30710
|
+
addActivity(workspace, event.sessionId ?? owner2?.sessionId, "failed", "\u4EFB\u52A1\u6267\u884C\u5931\u8D25", event.message);
|
|
30711
|
+
}
|
|
30712
|
+
}
|
|
30713
|
+
const ownerSelected = owner2 === void 0 ? project?.selectedTask === void 0 : project?.selectedTask === owner2;
|
|
30714
|
+
if (workspace === activeWorkspace && ownerSelected) {
|
|
30715
|
+
if (event.type === "snapshot") emitDesktopEvent({ ...event, workspace, snapshot: decorateSnapshot(event.snapshot) });
|
|
30716
|
+
else if (event.type === "session-started") emitDesktopEvent({ ...event, workspace, payload: decorateSessionStarted(event.payload) });
|
|
30717
|
+
else emitDesktopEvent({ ...event, workspace });
|
|
30718
|
+
return;
|
|
30719
|
+
}
|
|
30720
|
+
if (event.type === "session-output") emitDesktopEvent({ ...event, workspace });
|
|
30721
|
+
if ((event.type === "snapshot" || event.type === "session-started") && activeWorkspace !== void 0) {
|
|
30722
|
+
emitDesktopEvent({ type: "snapshot", workspace: activeWorkspace, snapshot: decorateSnapshot(controller.snapshot()) });
|
|
30723
|
+
}
|
|
30724
|
+
}
|
|
30725
|
+
function createDesktopController(workspace, owner2) {
|
|
30726
|
+
const collaborationId = randomUUID27();
|
|
30727
|
+
return new DesktopRuntimeController({
|
|
30728
|
+
emit: (event) => emitManagedDesktopEvent(workspace, owner2, event),
|
|
30729
|
+
runD2cInteractionTests: (manifest, baseUrl, mockUrl) => d2cEmbedded.run(manifest, baseUrl, mockUrl),
|
|
30730
|
+
observeD2cPages: (manifest, baseUrl) => d2cEmbedded.observe(manifest, baseUrl),
|
|
30731
|
+
captureD2cPreview: (url) => d2cEmbedded.capture(url),
|
|
30732
|
+
d2cJudge: {
|
|
30733
|
+
config: () => judgeStore().view(),
|
|
30734
|
+
saveConfig: (input) => judgeStore().save(input),
|
|
30735
|
+
evaluate: async ({ report, interaction, designPng, implementationPng }) => {
|
|
30736
|
+
const config = await judgeStore().load();
|
|
30737
|
+
if (config === void 0) throw new Error("Configure a multimodal D2C judge model before running the quality gate");
|
|
30738
|
+
const assessment = await d2cJudgeClient.evaluate(config, {
|
|
30739
|
+
prompt: buildD2cJudgePrompt({ report, interaction }),
|
|
30740
|
+
designPng: judgeImage(designPng),
|
|
30741
|
+
implementationPng: judgeImage(implementationPng)
|
|
30742
|
+
});
|
|
30743
|
+
return finalizeD2cQualityJudgment({ assessment, report, interaction, model: config.model, passThreshold: config.passThreshold });
|
|
30744
|
+
},
|
|
30745
|
+
planInteractions: async (input) => {
|
|
30746
|
+
const config = await judgeStore().load();
|
|
30747
|
+
if (config === void 0) throw new Error("Configure a multimodal D2C judge model before autonomous interaction review");
|
|
30748
|
+
return d2cJudgeClient.planInteractions(config, {
|
|
30749
|
+
prompt: buildD2cAutonomousPlanPrompt(input),
|
|
30750
|
+
screenshots: input.observations.map((page) => judgeImage(page.screenshot)),
|
|
30751
|
+
observedPages: input.observations.map((page) => page.url),
|
|
30752
|
+
observedSelectors: input.observations.flatMap((page) => page.elements.map((element) => element.selector))
|
|
30753
|
+
});
|
|
30754
|
+
}
|
|
29894
30755
|
},
|
|
29895
|
-
|
|
29896
|
-
|
|
29897
|
-
|
|
29898
|
-
|
|
29899
|
-
|
|
29900
|
-
|
|
29901
|
-
|
|
29902
|
-
|
|
29903
|
-
|
|
29904
|
-
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
|
|
29909
|
-
|
|
29910
|
-
|
|
29911
|
-
|
|
29912
|
-
|
|
29913
|
-
|
|
29914
|
-
|
|
29915
|
-
|
|
29916
|
-
})
|
|
30756
|
+
createRuntime: async (runtimeOptions) => createProductionRuntime({
|
|
30757
|
+
...runtimeOptions,
|
|
30758
|
+
collaboration: {
|
|
30759
|
+
instanceId: collaborationId,
|
|
30760
|
+
alias: `desktop-${basename11(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
|
|
30761
|
+
optional: true,
|
|
30762
|
+
startBroker: (address) => {
|
|
30763
|
+
const entry2 = app.isPackaged ? join46(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join46(moduleDirectory, "pals-broker-entry.js");
|
|
30764
|
+
return spawnPalBrokerProcess({
|
|
30765
|
+
address,
|
|
30766
|
+
executable: process.execPath,
|
|
30767
|
+
args: [entry2, address],
|
|
30768
|
+
environment: { ...process.env, ELECTRON_RUN_AS_NODE: "1" }
|
|
30769
|
+
});
|
|
30770
|
+
}
|
|
30771
|
+
},
|
|
30772
|
+
...runtimeOptions.workspace === void 0 ? {} : {
|
|
30773
|
+
extraTools: createD2cTools(runtimeOptions.workspace, {
|
|
30774
|
+
capture: d2cCapture,
|
|
30775
|
+
onProgress: (progress) => emitManagedDesktopEvent(workspace, owner2, { type: "d2c-progress", payload: progress }),
|
|
30776
|
+
onReport: (report) => emitManagedDesktopEvent(workspace, owner2, { type: "d2c-report", payload: report })
|
|
30777
|
+
})
|
|
30778
|
+
}
|
|
30779
|
+
})
|
|
30780
|
+
});
|
|
30781
|
+
}
|
|
30782
|
+
var detachedController = createDesktopController();
|
|
30783
|
+
var controller = detachedController;
|
|
29917
30784
|
function statePath() {
|
|
29918
|
-
return
|
|
30785
|
+
return join46(app.getPath("userData"), "desktop-state.json");
|
|
29919
30786
|
}
|
|
29920
|
-
async function
|
|
30787
|
+
async function loadPersistedProjects() {
|
|
29921
30788
|
try {
|
|
29922
|
-
const raw = await
|
|
29923
|
-
if (raw.length >
|
|
29924
|
-
|
|
30789
|
+
const raw = await readFile39(statePath(), "utf8");
|
|
30790
|
+
if (raw.length > 1e5) return { projects: [] };
|
|
30791
|
+
const parsed = JSON.parse(raw);
|
|
30792
|
+
Object.assign(workbench, {
|
|
30793
|
+
projectMeta: validRecord(parsed.projectMeta),
|
|
30794
|
+
sessionMeta: validRecord(parsed.sessionMeta),
|
|
30795
|
+
activities: Array.isArray(parsed.activities) ? parsed.activities.slice(0, 500) : [],
|
|
30796
|
+
recoveryItems: Array.isArray(parsed.recoveryItems) ? parsed.recoveryItems.slice(0, 100) : [],
|
|
30797
|
+
runningSessions: Array.isArray(parsed.runningSessions) ? parsed.runningSessions.slice(0, 100) : [],
|
|
30798
|
+
taskCheckouts: Array.isArray(parsed.taskCheckouts) ? parsed.taskCheckouts.filter((item) => typeof item === "object" && item !== null && item.environment === "worktree" && typeof item.workspace === "string" && typeof item.sessionId === "string" && typeof item.workingDirectory === "string" && typeof item.worktreeId === "string").slice(0, 100) : [],
|
|
30799
|
+
cleanShutdown: parsed.cleanShutdown !== false
|
|
30800
|
+
});
|
|
30801
|
+
if (!workbench.cleanShutdown) {
|
|
30802
|
+
const interruptedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
30803
|
+
workbench.recoveryItems = workbench.runningSessions.map((item) => ({ ...item, interruptedAt, reason: "\u5E94\u7528\u4E0A\u6B21\u9000\u51FA\u65F6\u4EFB\u52A1\u4ECD\u5728\u8FD0\u884C" }));
|
|
30804
|
+
workbench.activities = [...workbench.runningSessions.map((item, index) => ({
|
|
30805
|
+
id: `recovery-${Date.now()}-${index}`,
|
|
30806
|
+
...item,
|
|
30807
|
+
kind: "interrupted",
|
|
30808
|
+
title: "\u4EFB\u52A1\u56E0\u5E94\u7528\u9000\u51FA\u800C\u4E2D\u65AD",
|
|
30809
|
+
detail: "\u53EF\u4EE5\u6062\u590D\u4F1A\u8BDD\u5E76\u7EE7\u7EED\u6267\u884C",
|
|
30810
|
+
createdAt: interruptedAt,
|
|
30811
|
+
unread: true
|
|
30812
|
+
})), ...workbench.activities].slice(0, 500);
|
|
30813
|
+
}
|
|
30814
|
+
return normalizePersistedDesktopProjects(parsed);
|
|
29925
30815
|
} catch {
|
|
29926
|
-
return
|
|
30816
|
+
return { projects: [] };
|
|
29927
30817
|
}
|
|
29928
30818
|
}
|
|
29929
|
-
async function
|
|
29930
|
-
await
|
|
30819
|
+
async function savePersistedProjects() {
|
|
30820
|
+
await writeFile19(statePath(), `${JSON.stringify({ workspace: activeWorkspace, projects: projectOrder, ...workbench })}
|
|
29931
30821
|
`, { encoding: "utf8", mode: 384 });
|
|
29932
30822
|
}
|
|
30823
|
+
function validRecord(value) {
|
|
30824
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
30825
|
+
}
|
|
29933
30826
|
async function assertDirectory(path) {
|
|
29934
|
-
if (!(await
|
|
30827
|
+
if (!(await stat18(path)).isDirectory()) throw new Error("The selected project is not a directory");
|
|
29935
30828
|
}
|
|
29936
30829
|
async function openWorkspace(path) {
|
|
29937
|
-
|
|
29938
|
-
|
|
29939
|
-
|
|
29940
|
-
|
|
30830
|
+
const workspace = resolve40(path);
|
|
30831
|
+
await assertDirectory(workspace);
|
|
30832
|
+
let project = managedProjects.get(workspace);
|
|
30833
|
+
if (project === void 0) {
|
|
30834
|
+
const nextController = createDesktopController(workspace);
|
|
30835
|
+
project = { controller: nextController, snapshot: nextController.snapshot(), tasks: /* @__PURE__ */ new Set() };
|
|
30836
|
+
managedProjects.set(workspace, project);
|
|
30837
|
+
projectOrder.unshift(workspace);
|
|
30838
|
+
activeWorkspace = workspace;
|
|
30839
|
+
controller = nextController;
|
|
30840
|
+
project.snapshot = await nextController.openWorkspace(workspace);
|
|
30841
|
+
for (const saved of workbench.taskCheckouts.filter((item) => resolve40(item.workspace) === workspace)) {
|
|
30842
|
+
try {
|
|
30843
|
+
await assertDirectory(saved.workingDirectory);
|
|
30844
|
+
const task = { environment: "worktree", workingDirectory: resolve40(saved.workingDirectory), worktreeId: saved.worktreeId };
|
|
30845
|
+
task.controller = createDesktopController(workspace, task);
|
|
30846
|
+
task.snapshot = await task.controller.openWorkspace(task.workingDirectory);
|
|
30847
|
+
task.sessionId = saved.sessionId;
|
|
30848
|
+
project.tasks.add(task);
|
|
30849
|
+
} catch {
|
|
30850
|
+
}
|
|
30851
|
+
}
|
|
30852
|
+
} else {
|
|
30853
|
+
activeWorkspace = workspace;
|
|
30854
|
+
controller = project.selectedTask?.controller ?? project.controller;
|
|
30855
|
+
project.snapshot = await project.controller.refreshSessions();
|
|
30856
|
+
}
|
|
30857
|
+
await savePersistedProjects();
|
|
30858
|
+
return decorateSnapshot(project.selectedTask?.snapshot ?? project.snapshot);
|
|
30859
|
+
}
|
|
30860
|
+
function trackTaskTransition(workspace, task, previous, next) {
|
|
30861
|
+
const sessionId = task?.sessionId ?? next.activeSession?.sessionId;
|
|
30862
|
+
if (sessionId === void 0) return;
|
|
30863
|
+
const wasBusy = previous?.activeSession?.busy ?? false;
|
|
30864
|
+
const busy = next.activeSession?.busy ?? false;
|
|
30865
|
+
const key = sessionMetaKey(workspace, sessionId);
|
|
30866
|
+
if (!wasBusy && busy) {
|
|
30867
|
+
if (task !== void 0) delete task.terminal;
|
|
30868
|
+
workbench.activities = workbench.activities.filter((item) => !(item.workspace === workspace && item.sessionId === sessionId));
|
|
30869
|
+
workbench.runningSessions = [...workbench.runningSessions.filter((item) => sessionMetaKey(item.workspace, item.sessionId) !== key), { workspace, sessionId }];
|
|
30870
|
+
} else if (wasBusy && !busy) {
|
|
30871
|
+
workbench.runningSessions = workbench.runningSessions.filter((item) => sessionMetaKey(item.workspace, item.sessionId) !== key);
|
|
30872
|
+
const outcome = task?.terminal ?? "completed";
|
|
30873
|
+
addActivity(workspace, sessionId, outcome, outcome === "failed" ? "\u4EFB\u52A1\u6267\u884C\u5931\u8D25" : outcome === "interrupted" ? "\u4EFB\u52A1\u5DF2\u4E2D\u65AD" : "\u4EFB\u52A1\u5DF2\u8FD0\u884C\u5B8C\u6210");
|
|
30874
|
+
}
|
|
30875
|
+
const needsAttention = next.approval !== void 0 || (next.questions?.length ?? 0) > 0;
|
|
30876
|
+
const hadAttention = previous?.approval !== void 0 || (previous?.questions?.length ?? 0) > 0;
|
|
30877
|
+
if (needsAttention && !hadAttention) addActivity(workspace, sessionId, "attention", "\u4EFB\u52A1\u7B49\u5F85\u4F60\u7684\u786E\u8BA4");
|
|
30878
|
+
void savePersistedProjects();
|
|
30879
|
+
}
|
|
30880
|
+
function addActivity(workspace, sessionId, kind, title, detail) {
|
|
30881
|
+
const item = { id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, workspace, ...sessionId === void 0 ? {} : { sessionId }, kind, title, ...detail === void 0 ? {} : { detail }, createdAt: (/* @__PURE__ */ new Date()).toISOString(), unread: true };
|
|
30882
|
+
workbench.activities = [item, ...workbench.activities.filter((old) => !(old.workspace === workspace && old.sessionId === sessionId && old.kind === kind))].slice(0, 500);
|
|
30883
|
+
if (Notification.isSupported()) {
|
|
30884
|
+
const notification = new Notification({ title, body: detail ?? `${workbench.projectMeta[workspace]?.label ?? workspace.split(/[\\/]/).pop() ?? workspace}` });
|
|
30885
|
+
notification.on("click", () => {
|
|
30886
|
+
void activateManagedSession(workspace, sessionId);
|
|
30887
|
+
});
|
|
30888
|
+
notification.show();
|
|
30889
|
+
}
|
|
30890
|
+
void savePersistedProjects();
|
|
30891
|
+
}
|
|
30892
|
+
async function startManagedSession(resumeSession, environment = "local") {
|
|
30893
|
+
if (activeWorkspace === void 0) throw new Error("Open a project before starting a session");
|
|
30894
|
+
const project = managedProjects.get(activeWorkspace);
|
|
30895
|
+
if (resumeSession !== void 0) {
|
|
30896
|
+
const existing = [...project.tasks].find((task2) => task2.sessionId === resumeSession);
|
|
30897
|
+
if (existing?.payload !== void 0) {
|
|
30898
|
+
project.selectedTask = existing;
|
|
30899
|
+
controller = existing.controller;
|
|
30900
|
+
return decorateSessionStarted(existing.payload);
|
|
30901
|
+
}
|
|
30902
|
+
}
|
|
30903
|
+
if ([...project.tasks].filter((task2) => task2.snapshot.activeSession?.busy).length >= 4) throw new Error("\u6BCF\u4E2A\u9879\u76EE\u6700\u591A\u540C\u65F6\u8FD0\u884C 4 \u4E2A\u4EFB\u52A1");
|
|
30904
|
+
const isolated = environment === "worktree" ? await worktreeManager(activeWorkspace).create(`task-${Date.now()}`) : void 0;
|
|
30905
|
+
const task = {
|
|
30906
|
+
environment,
|
|
30907
|
+
workingDirectory: isolated?.path ?? activeWorkspace,
|
|
30908
|
+
...isolated === void 0 ? {} : { worktreeId: isolated.id }
|
|
30909
|
+
};
|
|
30910
|
+
task.controller = createDesktopController(activeWorkspace, task);
|
|
30911
|
+
task.snapshot = task.controller.snapshot();
|
|
30912
|
+
project.tasks.add(task);
|
|
30913
|
+
project.selectedTask = task;
|
|
30914
|
+
controller = task.controller;
|
|
30915
|
+
await task.controller.openWorkspace(task.workingDirectory);
|
|
30916
|
+
const payload = await task.controller.startSession(resumeSession);
|
|
30917
|
+
task.sessionId = payload.sessionId;
|
|
30918
|
+
task.payload = payload;
|
|
30919
|
+
task.snapshot = payload.snapshot;
|
|
30920
|
+
if (task.environment === "worktree" && task.worktreeId !== void 0) {
|
|
30921
|
+
workbench.taskCheckouts = [...workbench.taskCheckouts.filter((item) => !(item.workspace === activeWorkspace && item.sessionId === payload.sessionId)), { workspace: activeWorkspace, sessionId: payload.sessionId, environment: "worktree", workingDirectory: task.workingDirectory, worktreeId: task.worktreeId }];
|
|
30922
|
+
}
|
|
30923
|
+
project.snapshot = { ...project.snapshot, sessions: payload.snapshot.sessions };
|
|
30924
|
+
await savePersistedProjects();
|
|
30925
|
+
return decorateSessionStarted(payload);
|
|
30926
|
+
}
|
|
30927
|
+
async function activateManagedSession(workspace, sessionId) {
|
|
30928
|
+
await openWorkspace(workspace);
|
|
30929
|
+
if (sessionId === void 0) return void 0;
|
|
30930
|
+
const project = managedProjects.get(workspace);
|
|
30931
|
+
const task = [...project.tasks].find((item) => item.sessionId === sessionId);
|
|
30932
|
+
let payload;
|
|
30933
|
+
if (task !== void 0) {
|
|
30934
|
+
project.selectedTask = task;
|
|
30935
|
+
controller = task.controller;
|
|
30936
|
+
payload = task.payload ?? await task.controller.startSession(sessionId);
|
|
30937
|
+
task.payload = payload;
|
|
30938
|
+
task.snapshot = payload.snapshot;
|
|
30939
|
+
} else payload = await startManagedSession(sessionId);
|
|
30940
|
+
workbench.activities = workbench.activities.map((item) => item.workspace === workspace && item.sessionId === sessionId ? { ...item, unread: false } : item);
|
|
30941
|
+
mainWindow?.show();
|
|
30942
|
+
mainWindow?.focus();
|
|
30943
|
+
emitDesktopEvent({ type: "session-started", workspace, payload: decorateSessionStarted(payload) });
|
|
30944
|
+
await savePersistedProjects();
|
|
30945
|
+
return decorateSessionStarted(payload);
|
|
29941
30946
|
}
|
|
29942
30947
|
async function chooseAndOpenWorkspace() {
|
|
29943
30948
|
const result = await dialog.showOpenDialog(mainWindow, {
|
|
@@ -29950,7 +30955,7 @@ async function chooseAndOpenWorkspace() {
|
|
|
29950
30955
|
var cachedAppIconDataUrl;
|
|
29951
30956
|
function appIconDataUrl() {
|
|
29952
30957
|
if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
|
|
29953
|
-
const icon = nativeImage.createFromPath(
|
|
30958
|
+
const icon = nativeImage.createFromPath(join46(app.getAppPath(), "assets", "icon.png"));
|
|
29954
30959
|
if (icon.isEmpty()) return void 0;
|
|
29955
30960
|
cachedAppIconDataUrl = icon.toDataURL();
|
|
29956
30961
|
return cachedAppIconDataUrl;
|
|
@@ -29958,13 +30963,23 @@ function appIconDataUrl() {
|
|
|
29958
30963
|
function installIpcHandlers() {
|
|
29959
30964
|
ipcMain.handle(DESKTOP_CHANNELS.appIcon, () => appIconDataUrl());
|
|
29960
30965
|
ipcMain.handle(DESKTOP_CHANNELS.bootstrap, async () => {
|
|
29961
|
-
const
|
|
29962
|
-
|
|
29963
|
-
|
|
29964
|
-
|
|
29965
|
-
|
|
29966
|
-
|
|
30966
|
+
const persisted = await loadPersistedProjects();
|
|
30967
|
+
workbench.cleanShutdown = false;
|
|
30968
|
+
for (const path of [...persisted.projects].reverse()) {
|
|
30969
|
+
try {
|
|
30970
|
+
await openWorkspace(path);
|
|
30971
|
+
} catch {
|
|
30972
|
+
}
|
|
30973
|
+
}
|
|
30974
|
+
if (persisted.workspace !== void 0 && managedProjects.has(resolve40(persisted.workspace))) {
|
|
30975
|
+
try {
|
|
30976
|
+
return await openWorkspace(persisted.workspace);
|
|
30977
|
+
} catch {
|
|
30978
|
+
}
|
|
29967
30979
|
}
|
|
30980
|
+
const fallback = projectOrder[0];
|
|
30981
|
+
if (fallback !== void 0) return openWorkspace(fallback);
|
|
30982
|
+
return decorateSnapshot(controller.snapshot());
|
|
29968
30983
|
});
|
|
29969
30984
|
ipcMain.handle(DESKTOP_CHANNELS.chooseWorkspace, chooseAndOpenWorkspace);
|
|
29970
30985
|
ipcMain.handle(DESKTOP_CHANNELS.openWorkspace, async (_event, value) => {
|
|
@@ -29972,12 +30987,170 @@ function installIpcHandlers() {
|
|
|
29972
30987
|
return openWorkspace(path);
|
|
29973
30988
|
});
|
|
29974
30989
|
ipcMain.handle(DESKTOP_CHANNELS.startSession, async (_event, value) => {
|
|
29975
|
-
const { resumeSession } = StartSessionInputSchema.parse(value);
|
|
29976
|
-
return
|
|
30990
|
+
const { resumeSession, environment } = StartSessionInputSchema.parse(value);
|
|
30991
|
+
return startManagedSession(resumeSession, environment);
|
|
30992
|
+
});
|
|
30993
|
+
ipcMain.handle(DESKTOP_CHANNELS.selectSession, async (_event, value) => {
|
|
30994
|
+
const { sessionId } = DeleteSessionInputSchema.parse(value);
|
|
30995
|
+
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
30996
|
+
return activateManagedSession(activeWorkspace, sessionId);
|
|
29977
30997
|
});
|
|
29978
30998
|
ipcMain.handle(DESKTOP_CHANNELS.deleteSession, async (_event, value) => {
|
|
29979
30999
|
const { sessionId } = DeleteSessionInputSchema.parse(value);
|
|
29980
|
-
return controller.deleteSession(sessionId);
|
|
31000
|
+
if (activeWorkspace === void 0) return decorateSnapshot(await controller.deleteSession(sessionId));
|
|
31001
|
+
const project = managedProjects.get(activeWorkspace);
|
|
31002
|
+
const task = [...project.tasks].find((item) => item.sessionId === sessionId);
|
|
31003
|
+
if (task !== void 0) {
|
|
31004
|
+
if (task.snapshot.activeSession?.busy) throw new Error("\u8BF7\u5148\u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1");
|
|
31005
|
+
await task.controller.dispose();
|
|
31006
|
+
project.tasks.delete(task);
|
|
31007
|
+
if (project.selectedTask === task) {
|
|
31008
|
+
delete project.selectedTask;
|
|
31009
|
+
controller = project.controller;
|
|
31010
|
+
}
|
|
31011
|
+
}
|
|
31012
|
+
delete workbench.sessionMeta[sessionMetaKey(activeWorkspace, sessionId)];
|
|
31013
|
+
workbench.taskCheckouts = workbench.taskCheckouts.filter((item) => !(item.workspace === activeWorkspace && item.sessionId === sessionId));
|
|
31014
|
+
workbench.activities = workbench.activities.filter((item) => !(item.workspace === activeWorkspace && item.sessionId === sessionId));
|
|
31015
|
+
project.snapshot = await project.controller.deleteSession(sessionId);
|
|
31016
|
+
await savePersistedProjects();
|
|
31017
|
+
return decorateSnapshot(project.selectedTask?.snapshot ?? project.snapshot);
|
|
31018
|
+
});
|
|
31019
|
+
ipcMain.handle(DESKTOP_CHANNELS.updateSession, async (_event, value) => {
|
|
31020
|
+
const input = UpdateSessionInputSchema.parse(value);
|
|
31021
|
+
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
31022
|
+
const key = sessionMetaKey(activeWorkspace, input.sessionId);
|
|
31023
|
+
workbench.sessionMeta[key] = { ...workbench.sessionMeta[key], ...input.title === void 0 ? {} : { title: input.title || void 0 }, ...input.pinned === void 0 ? {} : { pinned: input.pinned }, ...input.archived === void 0 ? {} : { archived: input.archived } };
|
|
31024
|
+
await savePersistedProjects();
|
|
31025
|
+
return decorateSnapshot(controller.snapshot());
|
|
31026
|
+
});
|
|
31027
|
+
ipcMain.handle(DESKTOP_CHANNELS.acknowledgeSession, async (_event, value) => {
|
|
31028
|
+
const sessionId = Object.keys(value).length === 0 ? void 0 : DeleteSessionInputSchema.parse(value).sessionId;
|
|
31029
|
+
workbench.activities = workbench.activities.map((item) => item.workspace === activeWorkspace && (sessionId === void 0 || item.sessionId === sessionId) ? { ...item, unread: false } : item);
|
|
31030
|
+
await savePersistedProjects();
|
|
31031
|
+
return decorateSnapshot(controller.snapshot());
|
|
31032
|
+
});
|
|
31033
|
+
ipcMain.handle(DESKTOP_CHANNELS.updateProject, async (_event, value) => {
|
|
31034
|
+
const input = UpdateProjectInputSchema.parse(value);
|
|
31035
|
+
const workspace = resolve40(input.workspace);
|
|
31036
|
+
if (!managedProjects.has(workspace)) throw new Error("\u9879\u76EE\u672A\u6253\u5F00");
|
|
31037
|
+
workbench.projectMeta[workspace] = { ...workbench.projectMeta[workspace], ...input.label === void 0 ? {} : { label: input.label || void 0 }, ...input.pinned === void 0 ? {} : { pinned: input.pinned } };
|
|
31038
|
+
await savePersistedProjects();
|
|
31039
|
+
return decorateSnapshot(controller.snapshot());
|
|
31040
|
+
});
|
|
31041
|
+
ipcMain.handle(DESKTOP_CHANNELS.closeProject, async (_event, value) => {
|
|
31042
|
+
const input = CloseProjectInputSchema.parse(value);
|
|
31043
|
+
const workspace = resolve40(input.workspace);
|
|
31044
|
+
const project = managedProjects.get(workspace);
|
|
31045
|
+
if (project === void 0) return decorateSnapshot(controller.snapshot());
|
|
31046
|
+
if ([...project.tasks].some((task) => task.snapshot.activeSession?.busy) && !input.force) throw new Error("\u9879\u76EE\u4E2D\u4ECD\u6709\u4EFB\u52A1\u8FD0\u884C\uFF1B\u786E\u8BA4\u540E\u518D\u5173\u95ED\u9879\u76EE");
|
|
31047
|
+
await Promise.all([project.controller, ...[...project.tasks].map((task) => task.controller)].map((item) => item.dispose()));
|
|
31048
|
+
managedProjects.delete(workspace);
|
|
31049
|
+
projectOrder.splice(projectOrder.indexOf(workspace), 1);
|
|
31050
|
+
if (activeWorkspace === workspace) {
|
|
31051
|
+
activeWorkspace = projectOrder[0];
|
|
31052
|
+
const next = activeWorkspace === void 0 ? void 0 : managedProjects.get(activeWorkspace);
|
|
31053
|
+
controller = next?.selectedTask?.controller ?? next?.controller ?? detachedController;
|
|
31054
|
+
}
|
|
31055
|
+
await savePersistedProjects();
|
|
31056
|
+
return decorateSnapshot(controller.snapshot());
|
|
31057
|
+
});
|
|
31058
|
+
ipcMain.handle(DESKTOP_CHANNELS.revealProject, async (_event, value) => {
|
|
31059
|
+
const workspace = resolve40(ProjectPathInputSchema.parse(value).workspace);
|
|
31060
|
+
await assertDirectory(workspace);
|
|
31061
|
+
await shell.openPath(workspace);
|
|
31062
|
+
});
|
|
31063
|
+
ipcMain.handle(DESKTOP_CHANNELS.copyProjectPath, async (_event, value) => {
|
|
31064
|
+
clipboard.writeText(resolve40(ProjectPathInputSchema.parse(value).workspace));
|
|
31065
|
+
});
|
|
31066
|
+
ipcMain.handle(DESKTOP_CHANNELS.dismissRecovery, async (_event, value) => {
|
|
31067
|
+
const { sessionId } = DeleteSessionInputSchema.parse(value);
|
|
31068
|
+
workbench.recoveryItems = workbench.recoveryItems.filter((item) => !(item.workspace === activeWorkspace && item.sessionId === sessionId));
|
|
31069
|
+
await savePersistedProjects();
|
|
31070
|
+
return decorateSnapshot(controller.snapshot());
|
|
31071
|
+
});
|
|
31072
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitStatus, async () => activeWorkspace === void 0 ? { repository: false, branch: "", head: "", files: [] } : desktopGitStatus(activeWorkingDirectory()));
|
|
31073
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitDiff, async (_event, value) => {
|
|
31074
|
+
const input = GitDiffInputSchema.parse(value);
|
|
31075
|
+
return desktopGitDiff(activeWorkingDirectory(), input.path, input.staged);
|
|
31076
|
+
});
|
|
31077
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitStage, async (_event, value) => desktopGitStage(activeWorkingDirectory(), GitPathInputSchema.parse(value).path));
|
|
31078
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitUnstage, async (_event, value) => desktopGitUnstage(activeWorkingDirectory(), GitPathInputSchema.parse(value).path));
|
|
31079
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitDiscard, async (_event, value) => desktopGitDiscard(activeWorkingDirectory(), GitPathInputSchema.parse(value).path));
|
|
31080
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitCommit, async (_event, value) => desktopGitCommit(activeWorkingDirectory(), GitCommitInputSchema.parse(value).message));
|
|
31081
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitReview, async (_event, value) => {
|
|
31082
|
+
const input = GitReviewInputSchema.parse(value);
|
|
31083
|
+
const workspace = activeWorkingDirectory();
|
|
31084
|
+
return input.scope === "last-turn" && controller.snapshot().activeSession !== void 0 ? desktopLastTurnDiff(workspace, controller.snapshot().activeSession.sessionId, input.paths ?? []) : desktopGitReviewDiff(workspace, { scope: input.scope, ...input.target === void 0 ? {} : { target: input.target }, ...input.paths === void 0 ? {} : { paths: input.paths } });
|
|
31085
|
+
});
|
|
31086
|
+
ipcMain.handle(DESKTOP_CHANNELS.gitHistory, async () => desktopGitHistory(activeWorkingDirectory()));
|
|
31087
|
+
ipcMain.handle(DESKTOP_CHANNELS.listWorktrees, async () => activeWorkspace === void 0 ? [] : worktreeManager(activeWorkspace).list());
|
|
31088
|
+
ipcMain.handle(DESKTOP_CHANNELS.removeWorktree, async (_event, value) => {
|
|
31089
|
+
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
31090
|
+
const input = WorktreeRemoveInputSchema.parse(value);
|
|
31091
|
+
const task = [...managedProjects.get(activeWorkspace).tasks].find((item) => item.worktreeId === input.id);
|
|
31092
|
+
if (task?.snapshot.activeSession?.busy) throw new Error("\u4E0D\u80FD\u5220\u9664\u6B63\u5728\u8FD0\u884C\u4EFB\u52A1\u7684\u5DE5\u4F5C\u6811");
|
|
31093
|
+
if (task !== void 0) {
|
|
31094
|
+
await task.controller.dispose();
|
|
31095
|
+
managedProjects.get(activeWorkspace).tasks.delete(task);
|
|
31096
|
+
}
|
|
31097
|
+
await worktreeManager(activeWorkspace).remove(input.id, input.force);
|
|
31098
|
+
workbench.taskCheckouts = workbench.taskCheckouts.filter((item) => !(item.workspace === activeWorkspace && item.worktreeId === input.id));
|
|
31099
|
+
await savePersistedProjects();
|
|
31100
|
+
});
|
|
31101
|
+
ipcMain.handle(DESKTOP_CHANNELS.mergeWorktree, async (_event, value) => {
|
|
31102
|
+
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
31103
|
+
await worktreeManager(activeWorkspace).merge(WorktreeRemoveInputSchema.parse(value).id);
|
|
31104
|
+
});
|
|
31105
|
+
ipcMain.handle(DESKTOP_CHANNELS.historySnapshot, async () => controller.historySnapshot());
|
|
31106
|
+
ipcMain.handle(DESKTOP_CHANNELS.createCheckpoint, async (_event, value) => controller.createCheckpoint(CheckpointInputSchema.parse(value).label));
|
|
31107
|
+
ipcMain.handle(DESKTOP_CHANNELS.rewindHistory, async (_event, value) => controller.rewindHistory(HistoryNodeInputSchema.parse(value).nodeId));
|
|
31108
|
+
ipcMain.handle(DESKTOP_CHANNELS.unrevertHistory, async () => controller.unrevertHistory());
|
|
31109
|
+
ipcMain.handle(DESKTOP_CHANNELS.forkHistory, async (_event, value) => controller.forkHistory(HistoryNodeInputSchema.parse(value).nodeId));
|
|
31110
|
+
ipcMain.handle(DESKTOP_CHANNELS.terminalOpen, async (_event, value) => {
|
|
31111
|
+
const input = TerminalOpenInputSchema.parse(value);
|
|
31112
|
+
return controller.openTerminal({ ...input.cwd === void 0 ? {} : { cwd: input.cwd }, ...input.shell === void 0 ? {} : { shell: input.shell }, ...input.columns === void 0 ? {} : { columns: input.columns }, ...input.rows === void 0 ? {} : { rows: input.rows } });
|
|
31113
|
+
});
|
|
31114
|
+
ipcMain.handle(DESKTOP_CHANNELS.terminalList, async () => controller.listTerminals());
|
|
31115
|
+
ipcMain.handle(DESKTOP_CHANNELS.terminalRead, async (_event, value) => {
|
|
31116
|
+
const input = TerminalReadInputSchema.parse(value);
|
|
31117
|
+
return controller.readTerminal(input.id, input.cursor);
|
|
31118
|
+
});
|
|
31119
|
+
ipcMain.handle(DESKTOP_CHANNELS.terminalWrite, async (_event, value) => {
|
|
31120
|
+
const input = TerminalWriteInputSchema.parse(value);
|
|
31121
|
+
controller.writeTerminal(input.id, input.data);
|
|
31122
|
+
});
|
|
31123
|
+
ipcMain.handle(DESKTOP_CHANNELS.terminalResize, async (_event, value) => {
|
|
31124
|
+
const input = TerminalResizeInputSchema.parse(value);
|
|
31125
|
+
controller.resizeTerminal(input.id, input.columns, input.rows);
|
|
31126
|
+
});
|
|
31127
|
+
ipcMain.handle(DESKTOP_CHANNELS.terminalClose, async (_event, value) => controller.closeTerminal(TerminalIdInputSchema.parse(value).id));
|
|
31128
|
+
ipcMain.handle(DESKTOP_CHANNELS.jobRead, async (_event, value) => {
|
|
31129
|
+
const input = JobReadInputSchema.parse(value);
|
|
31130
|
+
return controller.readJob(input.id, input.cursor);
|
|
31131
|
+
});
|
|
31132
|
+
ipcMain.handle(DESKTOP_CHANNELS.previewValidate, async (_event, value) => DesktopWorkbenchService.normalizePreviewUrl(PreviewUrlInputSchema.parse(value).url));
|
|
31133
|
+
ipcMain.handle(DESKTOP_CHANNELS.previewOpen, async (_event, value) => shell.openExternal(DesktopWorkbenchService.normalizePreviewUrl(PreviewUrlInputSchema.parse(value).url)));
|
|
31134
|
+
ipcMain.handle(DESKTOP_CHANNELS.inspectWorkbench, async () => new DesktopWorkbenchService(activeWorkingDirectory()).inspect(controller.snapshot().activeSession?.sessionId));
|
|
31135
|
+
ipcMain.handle(DESKTOP_CHANNELS.astStatus, async () => new DesktopAstGraphService(activeWorkingDirectory()).status());
|
|
31136
|
+
ipcMain.handle(DESKTOP_CHANNELS.astSearch, async (_event, value) => new DesktopAstGraphService(activeWorkingDirectory()).search(AstSearchInputSchema.parse(value).query));
|
|
31137
|
+
ipcMain.handle(DESKTOP_CHANNELS.astRelations, async (_event, value) => {
|
|
31138
|
+
const input = AstNodeInputSchema.parse(value);
|
|
31139
|
+
return new DesktopAstGraphService(activeWorkingDirectory()).relations(input.id, input.hops);
|
|
31140
|
+
});
|
|
31141
|
+
ipcMain.handle(DESKTOP_CHANNELS.listPals, async () => controller.listPals());
|
|
31142
|
+
ipcMain.handle(DESKTOP_CHANNELS.sendPalMessage, async (_event, value) => {
|
|
31143
|
+
const input = PalMessageInputSchema.parse(value);
|
|
31144
|
+
return controller.sendPalMessage(input.target, input.message, input.kind);
|
|
31145
|
+
});
|
|
31146
|
+
ipcMain.handle(DESKTOP_CHANNELS.startCoWork, async (_event, value) => {
|
|
31147
|
+
const input = CoWorkStartInputSchema.parse(value);
|
|
31148
|
+
return controller.startCoWork(input.target, input.goal);
|
|
31149
|
+
});
|
|
31150
|
+
ipcMain.handle(DESKTOP_CHANNELS.coWorkStatus, async (_event, value) => controller.coWorkStatus(CoWorkStatusInputSchema.parse(value).coWorkId));
|
|
31151
|
+
ipcMain.handle(DESKTOP_CHANNELS.cancelCoWork, async (_event, value) => {
|
|
31152
|
+
const input = CoWorkCancelInputSchema.parse(value);
|
|
31153
|
+
return controller.cancelCoWork(input.coWorkId, input.reason);
|
|
29981
31154
|
});
|
|
29982
31155
|
ipcMain.handle(DESKTOP_CHANNELS.showAppMenu, async (_event, value) => {
|
|
29983
31156
|
const { menu, x, y } = AppMenuInputSchema.parse(value);
|
|
@@ -30046,11 +31219,13 @@ function installIpcHandlers() {
|
|
|
30046
31219
|
ipcMain.handle(DESKTOP_CHANNELS.deleteMemory, async (_event, value) => {
|
|
30047
31220
|
return controller.deleteMemory(DeleteMemoryInputSchema.parse(value).id);
|
|
30048
31221
|
});
|
|
31222
|
+
ipcMain.handle(DESKTOP_CHANNELS.deleteColdMemory, async () => controller.deleteColdMemory());
|
|
30049
31223
|
ipcMain.handle(DESKTOP_CHANNELS.switchModel, async (_event, value) => {
|
|
30050
|
-
return controller.switchModel(SwitchDesktopModelInputSchema.parse(value).modelId);
|
|
31224
|
+
return decorateSnapshot(await controller.switchModel(SwitchDesktopModelInputSchema.parse(value).modelId));
|
|
30051
31225
|
});
|
|
30052
31226
|
ipcMain.handle(DESKTOP_CHANNELS.addModel, async (_event, value) => {
|
|
30053
|
-
|
|
31227
|
+
const result = await controller.addModel(AddDesktopModelInputSchema.parse(value));
|
|
31228
|
+
return { ...result, snapshot: decorateSnapshot(result.snapshot) };
|
|
30054
31229
|
});
|
|
30055
31230
|
ipcMain.handle(DESKTOP_CHANNELS.d2cImport, async (_event, value) => {
|
|
30056
31231
|
const { task } = D2cImportInputSchema.parse(value);
|
|
@@ -30198,7 +31373,7 @@ function applicationMenu() {
|
|
|
30198
31373
|
]);
|
|
30199
31374
|
}
|
|
30200
31375
|
async function createWindow() {
|
|
30201
|
-
const rendererPath =
|
|
31376
|
+
const rendererPath = join46(app.getAppPath(), "dist", "desktop-renderer", "index.html");
|
|
30202
31377
|
mainWindow = new BrowserWindow2({
|
|
30203
31378
|
title: "Flavor Code",
|
|
30204
31379
|
width: 1280,
|
|
@@ -30209,14 +31384,14 @@ async function createWindow() {
|
|
|
30209
31384
|
show: false,
|
|
30210
31385
|
...desktopWindowChrome(),
|
|
30211
31386
|
webPreferences: {
|
|
30212
|
-
preload:
|
|
31387
|
+
preload: join46(moduleDirectory, "preload.cjs"),
|
|
30213
31388
|
contextIsolation: true,
|
|
30214
31389
|
nodeIntegration: false,
|
|
30215
31390
|
sandbox: false,
|
|
30216
31391
|
webSecurity: true
|
|
30217
31392
|
}
|
|
30218
31393
|
});
|
|
30219
|
-
const iconPath =
|
|
31394
|
+
const iconPath = join46(app.getAppPath(), "assets", "icon.png");
|
|
30220
31395
|
const icon = nativeImage.createFromPath(iconPath);
|
|
30221
31396
|
if (!icon.isEmpty()) {
|
|
30222
31397
|
mainWindow.setIcon(icon);
|
|
@@ -30231,7 +31406,9 @@ async function createWindow() {
|
|
|
30231
31406
|
if (quitting) return;
|
|
30232
31407
|
event.preventDefault();
|
|
30233
31408
|
quitting = true;
|
|
30234
|
-
|
|
31409
|
+
workbench.cleanShutdown = true;
|
|
31410
|
+
const controllers = [detachedController, ...[...managedProjects.values()].flatMap((project) => [project.controller, ...[...project.tasks].map((task) => task.controller)])];
|
|
31411
|
+
void savePersistedProjects().catch(() => void 0).then(() => Promise.all(controllers.map((item) => item.dispose()))).finally(() => {
|
|
30235
31412
|
mainWindow?.destroy();
|
|
30236
31413
|
app.quit();
|
|
30237
31414
|
});
|
|
@@ -30264,8 +31441,16 @@ app.whenReady().then(async () => {
|
|
|
30264
31441
|
dialog.showErrorBox("Flavor Code \u65E0\u6CD5\u542F\u52A8", error instanceof Error ? error.message : String(error));
|
|
30265
31442
|
app.quit();
|
|
30266
31443
|
});
|
|
30267
|
-
app.on("before-quit", () => {
|
|
31444
|
+
app.on("before-quit", (event) => {
|
|
31445
|
+
if (quitting) return;
|
|
31446
|
+
event.preventDefault();
|
|
30268
31447
|
quitting = true;
|
|
31448
|
+
workbench.cleanShutdown = true;
|
|
31449
|
+
const controllers = [detachedController, ...[...managedProjects.values()].flatMap((project) => [project.controller, ...[...project.tasks].map((task) => task.controller)])];
|
|
31450
|
+
void savePersistedProjects().catch(() => void 0).then(() => Promise.all(controllers.map((item) => item.dispose()))).finally(() => {
|
|
31451
|
+
mainWindow?.destroy();
|
|
31452
|
+
app.quit();
|
|
31453
|
+
});
|
|
30269
31454
|
});
|
|
30270
31455
|
app.on("window-all-closed", () => {
|
|
30271
31456
|
if (process.platform !== "darwin") app.quit();
|