@runfusion/fusion 0.14.2 → 0.14.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/dist/bin.js +739 -67
- package/dist/client/assets/AgentDetailView-BBCnqhqI.js +18 -0
- package/dist/client/assets/{AgentsView-DkX0tzrN.js → AgentsView-BY-Yq-Te.js} +3 -3
- package/dist/client/assets/{ChatView-CEm2Hw6m.js → ChatView-DkoJNxFW.js} +1 -1
- package/dist/client/assets/{DevServerView-Bumvo_ge.js → DevServerView-qvs6pp6c.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-CXN11cBp.js → DirectoryPicker-BkAIXNrP.js} +1 -1
- package/dist/client/assets/{DocumentsView-B71IqAxA.js → DocumentsView-BcaUGgaL.js} +1 -1
- package/dist/client/assets/{InsightsView-Bs4Rldu6.js → InsightsView-Dz9Ivclw.js} +1 -1
- package/dist/client/assets/{MemoryView-Bs7b_L2Q.js → MemoryView-BsweARBT.js} +1 -1
- package/dist/client/assets/{NodesView-BvAGTXbO.js → NodesView-bAU-v4bJ.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-3Kcc4uhA.js → PiExtensionsManager-C_U2g7y3.js} +1 -1
- package/dist/client/assets/{PluginManager-Ch-Xynlm.js → PluginManager-pIDsTk5v.js} +1 -1
- package/dist/client/assets/{ResearchView-Bj6Saqf6.js → ResearchView-D4Eib_uR.js} +1 -1
- package/dist/client/assets/{RoadmapsView-9qT8Vwd0.js → RoadmapsView-BaGwsUGS.js} +1 -1
- package/dist/client/assets/{SettingsModal-D4ERGQNQ.js → SettingsModal-BiZVi3cI.js} +1 -1
- package/dist/client/assets/SettingsModal-CRyg643t.js +31 -0
- package/dist/client/assets/{SetupWizardModal-Dv0rX2_o.js → SetupWizardModal-BcIGBBpA.js} +1 -1
- package/dist/client/assets/{SkillMultiselect-CSkXQzdv.js → SkillMultiselect-DPARHJeQ.js} +1 -1
- package/dist/client/assets/{SkillsView-2srXMOzj.js → SkillsView-Da_d_HPu.js} +1 -1
- package/dist/client/assets/{TodoView-CxPPIvw2.js → TodoView-5rAeqYtV.js} +1 -1
- package/dist/client/assets/{folder-open-FA1PwpXV.js → folder-open-CgjcFqww.js} +1 -1
- package/dist/client/assets/{index-CEavim6l.js → index-DoQ5ALYY.js} +25 -25
- package/dist/client/assets/{list-checks-6EktkUso.js → list-checks-C9YWtF7h.js} +1 -1
- package/dist/client/assets/{star-B6Th07jw.js → star-4nUh67-U.js} +1 -1
- package/dist/client/assets/{upload-BJwuErhV.js → upload-CEt5-Bnq.js} +1 -1
- package/dist/client/assets/{users-BrnPTF8H.js → users-4I0JDmgO.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/version.json +1 -1
- package/dist/extension.js +518 -51
- package/dist/pi-claude-cli/index.ts +2 -2
- package/dist/pi-claude-cli/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/AgentDetailView-C2Iik3Qf.js +0 -18
- package/dist/client/assets/SettingsModal-Zo5qDGOq.js +0 -31
package/dist/bin.js
CHANGED
|
@@ -77,6 +77,7 @@ var init_settings_schema = __esm({
|
|
|
77
77
|
favoriteModels: void 0,
|
|
78
78
|
openrouterModelSync: true,
|
|
79
79
|
updateCheckEnabled: true,
|
|
80
|
+
fnBinaryCheckEnabled: true,
|
|
80
81
|
updateCheckFrequency: "daily",
|
|
81
82
|
showGitHubStarButton: true,
|
|
82
83
|
modelOnboardingComplete: void 0,
|
|
@@ -3292,6 +3293,12 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3292
3293
|
if (!inMemory && !isAbsolute(fusionDir)) {
|
|
3293
3294
|
throw new Error(`[fusion] Database constructor requires an absolute fusionDir path, got: ${fusionDir}`);
|
|
3294
3295
|
}
|
|
3296
|
+
if (!inMemory && /\.fusion[\\/]\.fusion(?:[\\/]|$)/.test(fusionDir)) {
|
|
3297
|
+
throw new Error(
|
|
3298
|
+
`[fusion] Refusing to open Database at nested .fusion/.fusion path: ${fusionDir}
|
|
3299
|
+
This means a caller passed a .fusion directory where a project root was expected. Audit the call site for an extra \`join(rootDir, '.fusion')\` step.`
|
|
3300
|
+
);
|
|
3301
|
+
}
|
|
3295
3302
|
if (!inMemory && !existsSync(fusionDir)) {
|
|
3296
3303
|
mkdirSync(fusionDir, { recursive: true });
|
|
3297
3304
|
}
|
|
@@ -6018,6 +6025,7 @@ var init_agent_store = __esm({
|
|
|
6018
6025
|
};
|
|
6019
6026
|
const line = JSON.stringify(safeEntry) + "\n";
|
|
6020
6027
|
await appendFile(this.runLogPath(agentId, runId), line, "utf-8");
|
|
6028
|
+
this.emit("run:log", agentId, runId, safeEntry);
|
|
6021
6029
|
}
|
|
6022
6030
|
/**
|
|
6023
6031
|
* Read all log entries for a given run from its JSONL file.
|
|
@@ -36344,12 +36352,16 @@ var init_gh_cli = __esm({
|
|
|
36344
36352
|
|
|
36345
36353
|
// ../core/src/fn-binary.ts
|
|
36346
36354
|
import { spawn as spawn2 } from "node:child_process";
|
|
36347
|
-
import { platform as platform2 } from "node:os";
|
|
36355
|
+
import { platform as platform2, tmpdir as tmpdir2 } from "node:os";
|
|
36348
36356
|
function runProbe(command, args, timeoutMs) {
|
|
36349
36357
|
return new Promise((resolve42) => {
|
|
36350
36358
|
let stdout = "";
|
|
36351
36359
|
let stderr = "";
|
|
36352
|
-
const child = spawn2(command, args, {
|
|
36360
|
+
const child = spawn2(command, args, {
|
|
36361
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
36362
|
+
shell: false,
|
|
36363
|
+
cwd: tmpdir2()
|
|
36364
|
+
});
|
|
36353
36365
|
const timer = setTimeout(() => {
|
|
36354
36366
|
try {
|
|
36355
36367
|
child.kill("SIGKILL");
|
|
@@ -37667,7 +37679,7 @@ async function syncBackupAutomation(automationStore, settings) {
|
|
|
37667
37679
|
if (!AutomationStore2.isValidCron(schedule)) {
|
|
37668
37680
|
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
37669
37681
|
}
|
|
37670
|
-
const command = "
|
|
37682
|
+
const command = "fn backup --create";
|
|
37671
37683
|
if (existingSchedule) {
|
|
37672
37684
|
return await automationStore.updateSchedule(existingSchedule.id, {
|
|
37673
37685
|
scheduleType: "custom",
|
|
@@ -37700,7 +37712,7 @@ async function syncBackupRoutine(routineStore, settings) {
|
|
|
37700
37712
|
if (!RoutineStore2.isValidCron(schedule)) {
|
|
37701
37713
|
throw new Error(`Invalid backup schedule: ${schedule}`);
|
|
37702
37714
|
}
|
|
37703
|
-
const command = "
|
|
37715
|
+
const command = "fn backup --create";
|
|
37704
37716
|
const input = {
|
|
37705
37717
|
name: BACKUP_SCHEDULE_NAME,
|
|
37706
37718
|
description: "Automatic database backup based on project settings",
|
|
@@ -49575,7 +49587,7 @@ var require_dist3 = __commonJS({
|
|
|
49575
49587
|
|
|
49576
49588
|
// ../core/src/agent-companies-parser.ts
|
|
49577
49589
|
import { existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync, statSync as statSync4 } from "node:fs";
|
|
49578
|
-
import { tmpdir as
|
|
49590
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
49579
49591
|
import { join as join20, resolve as resolve9 } from "node:path";
|
|
49580
49592
|
function slugifyAgentReference(value) {
|
|
49581
49593
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -49898,7 +49910,7 @@ async function extractTarArchive(archivePath, outputDir) {
|
|
|
49898
49910
|
}
|
|
49899
49911
|
async function parseCompanyArchive(archivePath) {
|
|
49900
49912
|
const resolvedArchivePath = resolve9(archivePath);
|
|
49901
|
-
const tempDir = mkdtempSync(join20(
|
|
49913
|
+
const tempDir = mkdtempSync(join20(tmpdir3(), "agent-companies-"));
|
|
49902
49914
|
try {
|
|
49903
49915
|
if (resolvedArchivePath.endsWith(".tar.gz") || resolvedArchivePath.endsWith(".tgz")) {
|
|
49904
49916
|
await extractTarArchive(resolvedArchivePath, tempDir);
|
|
@@ -65767,6 +65779,20 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65767
65779
|
);
|
|
65768
65780
|
this.activeStepExecutors.delete(task.id);
|
|
65769
65781
|
}
|
|
65782
|
+
if (this.activeWorkflowStepSessions.has(task.id)) {
|
|
65783
|
+
executorLog.log(`${task.id} moved from in-progress to ${to} \u2014 terminating workflow step session`);
|
|
65784
|
+
this.pausedAborted.add(task.id);
|
|
65785
|
+
this.options.stuckTaskDetector?.untrackTask(task.id);
|
|
65786
|
+
const workflowSession = this.activeWorkflowStepSessions.get(task.id);
|
|
65787
|
+
const sessionWithAbort = workflowSession;
|
|
65788
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65789
|
+
void sessionWithAbort.abort().catch((err) => {
|
|
65790
|
+
executorLog.warn(`Failed to abort workflow step session for ${task.id}: ${err}`);
|
|
65791
|
+
});
|
|
65792
|
+
}
|
|
65793
|
+
workflowSession.dispose();
|
|
65794
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
65795
|
+
}
|
|
65770
65796
|
this.disposeSubagentsForTask(task.id, `parent moved from in-progress to ${to}`);
|
|
65771
65797
|
this.loopRecoveryState.delete(task.id);
|
|
65772
65798
|
this.spawnedAgents.delete(task.id);
|
|
@@ -65799,8 +65825,42 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65799
65825
|
this.disposeSubagentsForTask(task.id, "task paused");
|
|
65800
65826
|
return;
|
|
65801
65827
|
}
|
|
65802
|
-
if (
|
|
65828
|
+
if (task.paused && this.activeWorkflowStepSessions.has(task.id)) {
|
|
65829
|
+
executorLog.log(`Pausing ${task.id} \u2014 terminating workflow step session`);
|
|
65830
|
+
this.pausedAborted.add(task.id);
|
|
65831
|
+
this.options.stuckTaskDetector?.untrackTask(task.id);
|
|
65832
|
+
const workflowSession = this.activeWorkflowStepSessions.get(task.id);
|
|
65833
|
+
const sessionWithAbort = workflowSession;
|
|
65834
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65835
|
+
await sessionWithAbort.abort().catch(
|
|
65836
|
+
(err) => executorLog.warn(`Failed to abort workflow step session for pause ${task.id}: ${err}`)
|
|
65837
|
+
);
|
|
65838
|
+
}
|
|
65839
|
+
workflowSession.dispose();
|
|
65840
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
65841
|
+
this.loopRecoveryState.delete(task.id);
|
|
65842
|
+
this.spawnedAgents.delete(task.id);
|
|
65843
|
+
this.stuckAborted.delete(task.id);
|
|
65844
|
+
this.disposeSubagentsForTask(task.id, "task paused");
|
|
65845
|
+
return;
|
|
65846
|
+
}
|
|
65847
|
+
if (!task.paused && task.column === "in-progress" && !this.activeSessions.has(task.id) && !this.activeStepExecutors.has(task.id) && !this.activeWorkflowStepSessions.has(task.id)) {
|
|
65803
65848
|
if (!this.executing.has(task.id) && !this.resumingUnpaused.has(task.id) && !this.recoveringCompleted.has(task.id)) {
|
|
65849
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
65850
|
+
if (pauseLabel) {
|
|
65851
|
+
executorLog.log(`Skipping unpause resume for ${task.id} \u2014 ${pauseLabel} active`);
|
|
65852
|
+
return;
|
|
65853
|
+
}
|
|
65854
|
+
if (this.isTaskWorkComplete(task) && !task.mergeDetails) {
|
|
65855
|
+
this.recoveringCompleted.add(task.id);
|
|
65856
|
+
executorLog.log(`${task.id} unpaused with completed work and no session \u2014 recovering directly to in-review`);
|
|
65857
|
+
void this.recoverCompletedTask(task).catch(
|
|
65858
|
+
(err) => executorLog.error(`Failed to recover completed unpaused task ${task.id}:`, err)
|
|
65859
|
+
).finally(() => {
|
|
65860
|
+
this.recoveringCompleted.delete(task.id);
|
|
65861
|
+
});
|
|
65862
|
+
return;
|
|
65863
|
+
}
|
|
65804
65864
|
this.resumingUnpaused.add(task.id);
|
|
65805
65865
|
executorLog.log(`Unpaused ${task.id} in-progress with no session \u2014 resuming execution`);
|
|
65806
65866
|
try {
|
|
@@ -65919,6 +65979,22 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65919
65979
|
this.spawnedAgents.delete(taskId);
|
|
65920
65980
|
this.stuckAborted.delete(taskId);
|
|
65921
65981
|
}
|
|
65982
|
+
for (const [taskId, workflowSession] of this.activeWorkflowStepSessions) {
|
|
65983
|
+
executorLog.log(`Global pause \u2014 terminating workflow step session for ${taskId}`);
|
|
65984
|
+
this.pausedAborted.add(taskId);
|
|
65985
|
+
this.options.stuckTaskDetector?.untrackTask(taskId);
|
|
65986
|
+
const sessionWithAbort = workflowSession;
|
|
65987
|
+
if (typeof sessionWithAbort.abort === "function") {
|
|
65988
|
+
void sessionWithAbort.abort().catch((err) => {
|
|
65989
|
+
executorLog.warn(`Failed to abort workflow step session for ${taskId}: ${err}`);
|
|
65990
|
+
});
|
|
65991
|
+
}
|
|
65992
|
+
workflowSession.dispose();
|
|
65993
|
+
this.activeWorkflowStepSessions.delete(taskId);
|
|
65994
|
+
this.loopRecoveryState.delete(taskId);
|
|
65995
|
+
this.spawnedAgents.delete(taskId);
|
|
65996
|
+
this.stuckAborted.delete(taskId);
|
|
65997
|
+
}
|
|
65922
65998
|
}
|
|
65923
65999
|
});
|
|
65924
66000
|
}
|
|
@@ -65936,6 +66012,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65936
66012
|
activeSessions = /* @__PURE__ */ new Map();
|
|
65937
66013
|
/** Active step-session executors per task (mutually exclusive with activeSessions). */
|
|
65938
66014
|
activeStepExecutors = /* @__PURE__ */ new Map();
|
|
66015
|
+
/** Active pre-merge workflow step sessions per task. */
|
|
66016
|
+
activeWorkflowStepSessions = /* @__PURE__ */ new Map();
|
|
65939
66017
|
/**
|
|
65940
66018
|
* Reviewer subagent sessions per task. Reviewers (`reviewer.ts`) create their
|
|
65941
66019
|
* own AgentSessions that aren't part of `activeSessions`/`activeStepExecutors`,
|
|
@@ -65982,6 +66060,69 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
65982
66060
|
await this.store.mergeTask(taskId);
|
|
65983
66061
|
return "merged";
|
|
65984
66062
|
}
|
|
66063
|
+
async getExecutionPauseLabel() {
|
|
66064
|
+
const settings = await this.store.getSettings();
|
|
66065
|
+
if (settings.globalPause) return "global pause";
|
|
66066
|
+
if (settings.enginePaused) return "engine pause";
|
|
66067
|
+
return null;
|
|
66068
|
+
}
|
|
66069
|
+
async shouldDeferCompletionForGlobalPause(taskId, context) {
|
|
66070
|
+
const settings = await this.store.getSettings();
|
|
66071
|
+
if (!settings.globalPause) {
|
|
66072
|
+
return false;
|
|
66073
|
+
}
|
|
66074
|
+
this.clearCompletedTaskWatchdog(taskId);
|
|
66075
|
+
executorLog.log(`${taskId}: completion handoff deferred \u2014 global pause active (${context})`);
|
|
66076
|
+
await this.store.logEntry(
|
|
66077
|
+
taskId,
|
|
66078
|
+
`Completion handoff deferred \u2014 global pause active (${context})`,
|
|
66079
|
+
void 0,
|
|
66080
|
+
this.currentRunContext
|
|
66081
|
+
).catch(() => void 0);
|
|
66082
|
+
return true;
|
|
66083
|
+
}
|
|
66084
|
+
async shouldDeferWorkflowStepCompletion(taskId, context) {
|
|
66085
|
+
let latestTask = null;
|
|
66086
|
+
try {
|
|
66087
|
+
latestTask = await this.store.getTask(taskId);
|
|
66088
|
+
} catch {
|
|
66089
|
+
latestTask = null;
|
|
66090
|
+
}
|
|
66091
|
+
if (latestTask?.paused || this.pausedAborted.has(taskId)) {
|
|
66092
|
+
this.clearCompletedTaskWatchdog(taskId);
|
|
66093
|
+
executorLog.log(`${taskId}: completion handoff deferred \u2014 task paused (${context})`);
|
|
66094
|
+
await this.store.logEntry(
|
|
66095
|
+
taskId,
|
|
66096
|
+
`Completion handoff deferred \u2014 task paused (${context})`,
|
|
66097
|
+
void 0,
|
|
66098
|
+
this.currentRunContext
|
|
66099
|
+
).catch(() => void 0);
|
|
66100
|
+
return true;
|
|
66101
|
+
}
|
|
66102
|
+
return this.shouldDeferCompletionForGlobalPause(taskId, context);
|
|
66103
|
+
}
|
|
66104
|
+
async parkTaskAfterWorkflowStepPause(taskId) {
|
|
66105
|
+
let latestTask = null;
|
|
66106
|
+
try {
|
|
66107
|
+
latestTask = await this.store.getTask(taskId);
|
|
66108
|
+
} catch {
|
|
66109
|
+
latestTask = null;
|
|
66110
|
+
}
|
|
66111
|
+
if (!latestTask?.paused) {
|
|
66112
|
+
return false;
|
|
66113
|
+
}
|
|
66114
|
+
executorLog.log(`${taskId}: workflow step interrupted by task pause \u2014 moving to todo`);
|
|
66115
|
+
await this.store.logEntry(
|
|
66116
|
+
taskId,
|
|
66117
|
+
"Execution paused during pre-merge workflow step \u2014 moved to todo",
|
|
66118
|
+
void 0,
|
|
66119
|
+
this.currentRunContext
|
|
66120
|
+
).catch(() => void 0);
|
|
66121
|
+
if (latestTask.column === "in-progress") {
|
|
66122
|
+
await this.store.moveTask(taskId, "todo", { preserveResumeState: true });
|
|
66123
|
+
}
|
|
66124
|
+
return true;
|
|
66125
|
+
}
|
|
65985
66126
|
/** Child agent sessions keyed by agent ID. Used for termination. */
|
|
65986
66127
|
childSessions = /* @__PURE__ */ new Map();
|
|
65987
66128
|
/** Total count of currently spawned agents (across all parents). */
|
|
@@ -66176,11 +66317,15 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66176
66317
|
this.clearCompletedTaskWatchdog(taskId);
|
|
66177
66318
|
const handle = setTimeout(async () => {
|
|
66178
66319
|
this.completedTaskWatchdogs.delete(taskId);
|
|
66179
|
-
if (this.recoveringCompleted.has(taskId) || this.executing.has(taskId) || this.activeSessions.has(taskId) || this.activeStepExecutors.has(taskId) || this.resumingUnpaused.has(taskId)) {
|
|
66320
|
+
if (this.recoveringCompleted.has(taskId) || this.executing.has(taskId) || this.activeSessions.has(taskId) || this.activeStepExecutors.has(taskId) || this.activeWorkflowStepSessions.has(taskId) || this.resumingUnpaused.has(taskId)) {
|
|
66180
66321
|
return;
|
|
66181
66322
|
}
|
|
66182
66323
|
this.recoveringCompleted.add(taskId);
|
|
66183
66324
|
try {
|
|
66325
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
66326
|
+
if (pauseLabel) {
|
|
66327
|
+
return;
|
|
66328
|
+
}
|
|
66184
66329
|
let currentTask = null;
|
|
66185
66330
|
try {
|
|
66186
66331
|
currentTask = await this.store.getTask(taskId);
|
|
@@ -66226,6 +66371,11 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66226
66371
|
* stuck.
|
|
66227
66372
|
*/
|
|
66228
66373
|
async performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState = true) {
|
|
66374
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
66375
|
+
if (pauseLabel) {
|
|
66376
|
+
executorLog.log(`${taskId}: workflow rerun deferred \u2014 ${pauseLabel} active`);
|
|
66377
|
+
return "deferred-paused";
|
|
66378
|
+
}
|
|
66229
66379
|
if (this.workflowRerunPending.has(taskId)) {
|
|
66230
66380
|
executorLog.warn(`${taskId}: workflow rerun bounce already in flight \u2014 skipping re-entry`);
|
|
66231
66381
|
return "skipped-pending";
|
|
@@ -66236,6 +66386,10 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66236
66386
|
if (!latestTask) {
|
|
66237
66387
|
throw new Error("task missing during workflow rerun bounce");
|
|
66238
66388
|
}
|
|
66389
|
+
if (latestTask.paused) {
|
|
66390
|
+
executorLog.log(`${taskId}: workflow rerun deferred \u2014 task is paused`);
|
|
66391
|
+
return "deferred-paused";
|
|
66392
|
+
}
|
|
66239
66393
|
if (latestTask.column === "in-progress") {
|
|
66240
66394
|
const originalExecutionStartedAt = latestTask.executionStartedAt;
|
|
66241
66395
|
if (preserveResumeState) {
|
|
@@ -66247,11 +66401,21 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66247
66401
|
worktree: worktreePath,
|
|
66248
66402
|
executionStartedAt: originalExecutionStartedAt ?? null
|
|
66249
66403
|
});
|
|
66404
|
+
const pauseLabelAfterTodo = await this.getExecutionPauseLabel();
|
|
66405
|
+
if (pauseLabelAfterTodo) {
|
|
66406
|
+
executorLog.log(`${taskId}: workflow rerun parked in todo \u2014 ${pauseLabelAfterTodo} became active during bounce`);
|
|
66407
|
+
return "deferred-paused";
|
|
66408
|
+
}
|
|
66250
66409
|
await this.store.moveTask(taskId, "in-progress");
|
|
66251
66410
|
return "bounced";
|
|
66252
66411
|
}
|
|
66253
66412
|
if (latestTask.column === "todo") {
|
|
66254
66413
|
await this.store.updateTask(taskId, { worktree: worktreePath });
|
|
66414
|
+
const pauseLabelBeforeResume = await this.getExecutionPauseLabel();
|
|
66415
|
+
if (pauseLabelBeforeResume) {
|
|
66416
|
+
executorLog.log(`${taskId}: workflow rerun parked in todo \u2014 ${pauseLabelBeforeResume} became active before resume`);
|
|
66417
|
+
return "deferred-paused";
|
|
66418
|
+
}
|
|
66255
66419
|
await this.store.moveTask(taskId, "in-progress");
|
|
66256
66420
|
return "bounced";
|
|
66257
66421
|
}
|
|
@@ -66267,8 +66431,10 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66267
66431
|
const outcome = await this.performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState);
|
|
66268
66432
|
if (outcome === "bounced") {
|
|
66269
66433
|
executorLog.log(successMessage);
|
|
66270
|
-
} else {
|
|
66434
|
+
} else if (outcome === "skipped-pending") {
|
|
66271
66435
|
executorLog.warn(`${taskId}: rerun bounce skipped \u2014 another bounce already in flight`);
|
|
66436
|
+
} else {
|
|
66437
|
+
executorLog.log(`${taskId}: rerun bounce deferred while pause is active`);
|
|
66272
66438
|
}
|
|
66273
66439
|
} catch (err) {
|
|
66274
66440
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -66277,6 +66443,11 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66277
66443
|
}, 0);
|
|
66278
66444
|
const watchdog = setTimeout(async () => {
|
|
66279
66445
|
this.workflowRerunWatchdogs.delete(taskId);
|
|
66446
|
+
const pauseLabel = await this.getExecutionPauseLabel();
|
|
66447
|
+
if (pauseLabel) {
|
|
66448
|
+
executorLog.log(`${taskId}: workflow rerun watchdog skipped \u2014 ${pauseLabel} active`);
|
|
66449
|
+
return;
|
|
66450
|
+
}
|
|
66280
66451
|
let currentTask = null;
|
|
66281
66452
|
try {
|
|
66282
66453
|
currentTask = await this.store.getTask(taskId);
|
|
@@ -66299,7 +66470,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66299
66470
|
const outcome = await this.performWorkflowRerunBounce(taskId, worktreePath, preserveResumeState);
|
|
66300
66471
|
if (outcome === "bounced") {
|
|
66301
66472
|
executorLog.warn(`${taskId}: workflow rerun watchdog retry succeeded`);
|
|
66302
|
-
} else {
|
|
66473
|
+
} else if (outcome === "skipped-pending") {
|
|
66303
66474
|
executorLog.error(
|
|
66304
66475
|
`${taskId}: workflow rerun watchdog retry skipped \u2014 original bounce still in flight after ${WORKFLOW_RERUN_WATCHDOG_MS / 1e3}s; task may be stuck`
|
|
66305
66476
|
);
|
|
@@ -66307,6 +66478,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66307
66478
|
taskId,
|
|
66308
66479
|
`Workflow rerun watchdog retry skipped \u2014 original bounce still in flight after ${WORKFLOW_RERUN_WATCHDOG_MS / 1e3}s; task may be stuck`
|
|
66309
66480
|
).catch(() => void 0);
|
|
66481
|
+
} else {
|
|
66482
|
+
executorLog.log(`${taskId}: workflow rerun watchdog retry deferred while pause is active`);
|
|
66310
66483
|
}
|
|
66311
66484
|
} catch (err) {
|
|
66312
66485
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -66429,11 +66602,17 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66429
66602
|
*/
|
|
66430
66603
|
async recoverCompletedTask(task) {
|
|
66431
66604
|
try {
|
|
66432
|
-
if (this.executing.has(task.id) || this.activeSessions.has(task.id) || this.activeStepExecutors.has(task.id) || this.resumingUnpaused.has(task.id)) {
|
|
66605
|
+
if (this.executing.has(task.id) || this.activeSessions.has(task.id) || this.activeStepExecutors.has(task.id) || this.activeWorkflowStepSessions.has(task.id) || this.resumingUnpaused.has(task.id)) {
|
|
66433
66606
|
executorLog.log(`${task.id}: skipping recoverCompletedTask \u2014 task has active execution in flight`);
|
|
66434
66607
|
return false;
|
|
66435
66608
|
}
|
|
66436
66609
|
const settings = await this.store.getSettings();
|
|
66610
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
66611
|
+
executorLog.log(
|
|
66612
|
+
`${task.id}: skipping recoverCompletedTask \u2014 ${settings.globalPause ? "global pause" : "engine pause"} active`
|
|
66613
|
+
);
|
|
66614
|
+
return false;
|
|
66615
|
+
}
|
|
66437
66616
|
if (task.worktree && existsSync27(task.worktree)) {
|
|
66438
66617
|
const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
|
|
66439
66618
|
if (modifiedFiles.length > 0) {
|
|
@@ -66441,7 +66620,16 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66441
66620
|
executorLog.log(`${task.id}: recovered ${modifiedFiles.length} modified files`);
|
|
66442
66621
|
}
|
|
66443
66622
|
if (task.executionMode !== "fast") {
|
|
66623
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps during completed-task recovery")) {
|
|
66624
|
+
return false;
|
|
66625
|
+
}
|
|
66444
66626
|
const workflowResult = await this.runWorkflowSteps(task, task.worktree, settings);
|
|
66627
|
+
if (workflowResult === "deferred-paused") {
|
|
66628
|
+
if (this.pausedAborted.has(task.id)) {
|
|
66629
|
+
this.pausedAborted.delete(task.id);
|
|
66630
|
+
}
|
|
66631
|
+
return false;
|
|
66632
|
+
}
|
|
66445
66633
|
if (!workflowResult.allPassed) {
|
|
66446
66634
|
await this.sendTaskBackForFix(task, task.worktree, workflowResult.feedback, workflowResult.stepName || "Unknown", "Workflow step failed during recovery", false);
|
|
66447
66635
|
return true;
|
|
@@ -66450,6 +66638,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66450
66638
|
executorLog.log(`${task.id}: fast mode \u2014 skipping workflow steps on auto-recovery`);
|
|
66451
66639
|
}
|
|
66452
66640
|
}
|
|
66641
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition during completed-task recovery")) {
|
|
66642
|
+
return false;
|
|
66643
|
+
}
|
|
66453
66644
|
await this.persistTokenUsage(task.id);
|
|
66454
66645
|
await this.store.moveTask(task.id, "in-review");
|
|
66455
66646
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -66515,6 +66706,13 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66515
66706
|
* directly to in-review without spawning a new agent session.
|
|
66516
66707
|
*/
|
|
66517
66708
|
async resumeOrphaned() {
|
|
66709
|
+
const settings = await this.store.getSettings();
|
|
66710
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
66711
|
+
executorLog.log(
|
|
66712
|
+
`resumeOrphaned skipped \u2014 ${settings.globalPause ? "global pause" : "engine pause"} is active`
|
|
66713
|
+
);
|
|
66714
|
+
return;
|
|
66715
|
+
}
|
|
66518
66716
|
const tasks = await this.store.listTasks({ slim: true, column: "in-progress" });
|
|
66519
66717
|
const inProgress = tasks.filter(
|
|
66520
66718
|
(t) => t.column === "in-progress" && !this.executing.has(t.id) && !t.paused
|
|
@@ -66953,8 +67151,21 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66953
67151
|
await audit.filesystem({ type: "file:capture-modified", target: task.id, metadata: { files: modifiedFiles } });
|
|
66954
67152
|
}
|
|
66955
67153
|
this.scheduleCompletedTaskWatchdog(task.id, "step-session completion");
|
|
67154
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after step-session completion")) {
|
|
67155
|
+
return;
|
|
67156
|
+
}
|
|
66956
67157
|
if (executionMode !== "fast") {
|
|
66957
67158
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
67159
|
+
if (workflowResult === "deferred-paused") {
|
|
67160
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
67161
|
+
this.pausedAborted.delete(task.id);
|
|
67162
|
+
return;
|
|
67163
|
+
}
|
|
67164
|
+
if (this.pausedAborted.has(task.id)) {
|
|
67165
|
+
this.pausedAborted.delete(task.id);
|
|
67166
|
+
}
|
|
67167
|
+
return;
|
|
67168
|
+
}
|
|
66958
67169
|
if (!workflowResult.allPassed) {
|
|
66959
67170
|
if (workflowResult.revisionRequested) {
|
|
66960
67171
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -66972,6 +67183,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66972
67183
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
66973
67184
|
}
|
|
66974
67185
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
67186
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after step-session completion")) {
|
|
67187
|
+
return;
|
|
67188
|
+
}
|
|
66975
67189
|
await this.store.moveTask(task.id, "in-review");
|
|
66976
67190
|
this.clearCompletedTaskWatchdog(task.id);
|
|
66977
67191
|
await audit.database({ type: "task:move", target: task.id, metadata: { to: "in-review" } });
|
|
@@ -67324,6 +67538,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67324
67538
|
this.pausedAborted.delete(task.id);
|
|
67325
67539
|
wasPaused = true;
|
|
67326
67540
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
67541
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "paused after completion")) {
|
|
67542
|
+
return;
|
|
67543
|
+
}
|
|
67327
67544
|
executorLog.log(`${task.id} paused after completion (graceful session exit) \u2014 finalizing to in-review`);
|
|
67328
67545
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review");
|
|
67329
67546
|
await this.persistTokenUsage(task.id);
|
|
@@ -67360,8 +67577,23 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67360
67577
|
executorLog.log(`${task.id}: captured ${modifiedFiles.length} modified files`);
|
|
67361
67578
|
}
|
|
67362
67579
|
this.scheduleCompletedTaskWatchdog(task.id, "task completion");
|
|
67580
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after task completion")) {
|
|
67581
|
+
return;
|
|
67582
|
+
}
|
|
67363
67583
|
if (executionMode !== "fast") {
|
|
67364
67584
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
67585
|
+
if (workflowResult === "deferred-paused") {
|
|
67586
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
67587
|
+
this.pausedAborted.delete(task.id);
|
|
67588
|
+
wasPaused = true;
|
|
67589
|
+
return;
|
|
67590
|
+
}
|
|
67591
|
+
if (this.pausedAborted.has(task.id)) {
|
|
67592
|
+
this.pausedAborted.delete(task.id);
|
|
67593
|
+
wasPaused = true;
|
|
67594
|
+
}
|
|
67595
|
+
return;
|
|
67596
|
+
}
|
|
67365
67597
|
if (!workflowResult.allPassed) {
|
|
67366
67598
|
if (workflowResult.revisionRequested) {
|
|
67367
67599
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -67379,6 +67611,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67379
67611
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
67380
67612
|
}
|
|
67381
67613
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
67614
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after task completion")) {
|
|
67615
|
+
return;
|
|
67616
|
+
}
|
|
67382
67617
|
await this.persistTokenUsage(task.id);
|
|
67383
67618
|
await this.store.moveTask(task.id, "in-review");
|
|
67384
67619
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -67503,8 +67738,23 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67503
67738
|
executorLog.log(`${task.id}: captured ${modifiedFiles.length} modified files`);
|
|
67504
67739
|
}
|
|
67505
67740
|
this.scheduleCompletedTaskWatchdog(task.id, "task completion retry");
|
|
67741
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after task completion retry")) {
|
|
67742
|
+
return;
|
|
67743
|
+
}
|
|
67506
67744
|
if (executionMode !== "fast") {
|
|
67507
67745
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
67746
|
+
if (workflowResult === "deferred-paused") {
|
|
67747
|
+
if (await this.parkTaskAfterWorkflowStepPause(task.id)) {
|
|
67748
|
+
this.pausedAborted.delete(task.id);
|
|
67749
|
+
wasPaused = true;
|
|
67750
|
+
return;
|
|
67751
|
+
}
|
|
67752
|
+
if (this.pausedAborted.has(task.id)) {
|
|
67753
|
+
this.pausedAborted.delete(task.id);
|
|
67754
|
+
wasPaused = true;
|
|
67755
|
+
}
|
|
67756
|
+
return;
|
|
67757
|
+
}
|
|
67508
67758
|
if (!workflowResult.allPassed) {
|
|
67509
67759
|
if (workflowResult.revisionRequested) {
|
|
67510
67760
|
await this.handleWorkflowRevisionRequest(task, worktreePath, workflowResult.feedback, workflowResult.stepName);
|
|
@@ -67518,6 +67768,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67518
67768
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
67519
67769
|
}
|
|
67520
67770
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
67771
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before in-review transition after task completion retry")) {
|
|
67772
|
+
return;
|
|
67773
|
+
}
|
|
67521
67774
|
await this.persistTokenUsage(task.id);
|
|
67522
67775
|
await this.store.moveTask(task.id, "in-review");
|
|
67523
67776
|
this.clearCompletedTaskWatchdog(task.id);
|
|
@@ -67610,6 +67863,9 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67610
67863
|
} else if (this.pausedAborted.has(task.id)) {
|
|
67611
67864
|
this.pausedAborted.delete(task.id);
|
|
67612
67865
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
67866
|
+
if (await this.shouldDeferCompletionForGlobalPause(task.id, "paused after completion")) {
|
|
67867
|
+
return;
|
|
67868
|
+
}
|
|
67613
67869
|
executorLog.log(`${task.id} paused after completion \u2014 finalizing to in-review`);
|
|
67614
67870
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review", void 0, this.currentRunContext);
|
|
67615
67871
|
await this.persistTokenUsage(task.id);
|
|
@@ -67968,22 +68224,28 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
67968
68224
|
if (params.summary) {
|
|
67969
68225
|
await store.updateTask(taskId, { summary: params.summary });
|
|
67970
68226
|
}
|
|
67971
|
-
await store.
|
|
68227
|
+
const settings = await store.getSettings();
|
|
68228
|
+
const hardPauseActive = Boolean(task.paused || settings.globalPause);
|
|
68229
|
+
if (hardPauseActive) {
|
|
68230
|
+
await store.updateTask(taskId, { status: null });
|
|
68231
|
+
} else {
|
|
68232
|
+
await store.updateTask(taskId, { paused: false, status: null });
|
|
68233
|
+
}
|
|
67972
68234
|
await store.logEntry(taskId, "Task marked done by agent");
|
|
67973
68235
|
const latestTask = await store.getTask(taskId);
|
|
67974
68236
|
let latestColumn = latestTask.column;
|
|
67975
68237
|
if (latestColumn === "todo") {
|
|
67976
68238
|
await store.logEntry(
|
|
67977
68239
|
taskId,
|
|
67978
|
-
"fn_task_done called while task was in todo \u2014 promoting to in-progress before completion handoff"
|
|
68240
|
+
hardPauseActive ? "fn_task_done called while task was in todo during pause \u2014 promoting to in-progress for deferred completion handoff" : "fn_task_done called while task was in todo \u2014 promoting to in-progress before completion handoff"
|
|
67979
68241
|
);
|
|
67980
68242
|
await store.moveTask(taskId, "in-progress");
|
|
67981
68243
|
latestColumn = "in-progress";
|
|
67982
68244
|
}
|
|
67983
|
-
if (latestColumn === "in-progress") {
|
|
68245
|
+
if (latestColumn === "in-progress" && !hardPauseActive) {
|
|
67984
68246
|
this.scheduleCompletedTaskWatchdog(taskId, "fn_task_done");
|
|
67985
68247
|
}
|
|
67986
|
-
const successMessage = params.summary ? "Task marked complete with summary. All steps done. Moving to in-review." : "Task marked complete. All steps done. Moving to in-review.";
|
|
68248
|
+
const successMessage = hardPauseActive ? "Task marked complete. Completion handoff deferred until pause is cleared." : params.summary ? "Task marked complete with summary. All steps done. Moving to in-review." : "Task marked complete. All steps done. Moving to in-review.";
|
|
67987
68249
|
return {
|
|
67988
68250
|
content: [{ type: "text", text: successMessage }],
|
|
67989
68251
|
details: {}
|
|
@@ -68558,6 +68820,9 @@ ${failureFeedback}
|
|
|
68558
68820
|
await this.store.updateTask(task.id, { workflowStepResults: results });
|
|
68559
68821
|
continue;
|
|
68560
68822
|
}
|
|
68823
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `before workflow step '${ws.name}'`)) {
|
|
68824
|
+
return "deferred-paused";
|
|
68825
|
+
}
|
|
68561
68826
|
await this.store.logEntry(task.id, `[pre-merge] Starting workflow step: ${ws.name} (${stepMode} mode)`);
|
|
68562
68827
|
executorLog.log(`${task.id} \u2014 [pre-merge] running workflow step: ${ws.name} (${stepMode} mode)`);
|
|
68563
68828
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -68572,6 +68837,9 @@ ${failureFeedback}
|
|
|
68572
68837
|
await this.store.updateTask(task.id, { workflowStepResults: results });
|
|
68573
68838
|
try {
|
|
68574
68839
|
const result = stepMode === "script" ? await this.executeScriptWorkflowStep(task, ws, worktreePath, settings) : await this.executeWorkflowStep(task, ws, worktreePath, settings);
|
|
68840
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `workflow step '${ws.name}'`)) {
|
|
68841
|
+
return "deferred-paused";
|
|
68842
|
+
}
|
|
68575
68843
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
68576
68844
|
if (result.success) {
|
|
68577
68845
|
await this.store.logEntry(task.id, `[timing] Workflow step '${ws.name}' completed in ${Date.now() - stepStartedAtMs}ms`);
|
|
@@ -68637,6 +68905,9 @@ ${failureFeedback}
|
|
|
68637
68905
|
};
|
|
68638
68906
|
}
|
|
68639
68907
|
} catch (err) {
|
|
68908
|
+
if (await this.shouldDeferWorkflowStepCompletion(task.id, `workflow step '${ws.name}'`)) {
|
|
68909
|
+
return "deferred-paused";
|
|
68910
|
+
}
|
|
68640
68911
|
const { message: errorMessage, detail: errorDetail, stack: errorStack } = formatError(err);
|
|
68641
68912
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
68642
68913
|
await this.store.logEntry(
|
|
@@ -68802,6 +69073,7 @@ and show an appropriate message to the user.\`
|
|
|
68802
69073
|
task.id,
|
|
68803
69074
|
`Workflow step '${workflowStep.name}' using model: ${describeModel(session)}${useOverride && attemptLabel === "primary" ? " (workflow step override)" : ""}${attemptLabel === "fallback" ? " (fallback after timeout)" : ""}`
|
|
68804
69075
|
);
|
|
69076
|
+
this.activeWorkflowStepSessions.set(task.id, session);
|
|
68805
69077
|
let output = "";
|
|
68806
69078
|
session.subscribe((event) => {
|
|
68807
69079
|
if (event.type === "message_update") {
|
|
@@ -68874,6 +69146,10 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
68874
69146
|
return { success: false, error: errorMessage };
|
|
68875
69147
|
} finally {
|
|
68876
69148
|
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
69149
|
+
const activeWorkflowStepSession = this.activeWorkflowStepSessions.get(task.id);
|
|
69150
|
+
if (activeWorkflowStepSession === session) {
|
|
69151
|
+
this.activeWorkflowStepSessions.delete(task.id);
|
|
69152
|
+
}
|
|
68877
69153
|
void timedOut;
|
|
68878
69154
|
}
|
|
68879
69155
|
};
|
|
@@ -70617,6 +70893,15 @@ var init_scheduler = __esm({
|
|
|
70617
70893
|
schedulerLog.log(`Task ${task.id} is paused \u2014 skipping dispatch`);
|
|
70618
70894
|
continue;
|
|
70619
70895
|
}
|
|
70896
|
+
const latestSettings = await this.store.getSettings();
|
|
70897
|
+
if (latestSettings.globalPause) {
|
|
70898
|
+
schedulerLog.log(`Task ${task.id} dispatch aborted \u2014 globalPause became active mid-pass`);
|
|
70899
|
+
continue;
|
|
70900
|
+
}
|
|
70901
|
+
if (latestSettings.enginePaused) {
|
|
70902
|
+
schedulerLog.log(`Task ${task.id} dispatch aborted \u2014 enginePaused became active mid-pass`);
|
|
70903
|
+
continue;
|
|
70904
|
+
}
|
|
70620
70905
|
let effectiveNode = resolveEffectiveNode(freshTask, settings);
|
|
70621
70906
|
schedulerLog.log(`Task ${task.id} routed to node=${effectiveNode.nodeId ?? "local"} (source=${effectiveNode.source})`);
|
|
70622
70907
|
if (effectiveNode.nodeId !== void 0 && this.options.nodeHealthMonitor) {
|
|
@@ -75754,6 +76039,36 @@ function execCommand(command, options) {
|
|
|
75754
76039
|
});
|
|
75755
76040
|
});
|
|
75756
76041
|
}
|
|
76042
|
+
function isInProcessBackupCommand(command) {
|
|
76043
|
+
if (!command) return false;
|
|
76044
|
+
const trimmed = command.trim();
|
|
76045
|
+
if (!trimmed) return false;
|
|
76046
|
+
if (SHELL_METACHARACTERS_REGEX.test(trimmed)) return false;
|
|
76047
|
+
const tokens = trimmed.split(/\s+/).map((tok) => tok.toLowerCase());
|
|
76048
|
+
let cursor = 0;
|
|
76049
|
+
if (tokens[cursor] === "npx") {
|
|
76050
|
+
cursor += 1;
|
|
76051
|
+
while (cursor < tokens.length) {
|
|
76052
|
+
const tok = tokens[cursor];
|
|
76053
|
+
if (tok === void 0 || !tok.startsWith("-")) break;
|
|
76054
|
+
const takesValue = (tok === "-p" || tok === "--package") && cursor + 1 < tokens.length && tokens[cursor + 1] !== void 0 && !tokens[cursor + 1].startsWith("-");
|
|
76055
|
+
cursor += takesValue ? 2 : 1;
|
|
76056
|
+
}
|
|
76057
|
+
}
|
|
76058
|
+
const binary = tokens[cursor];
|
|
76059
|
+
if (!binary || !FUSION_BINARY_TOKENS.has(binary)) return false;
|
|
76060
|
+
cursor += 1;
|
|
76061
|
+
if (tokens[cursor] !== "backup") return false;
|
|
76062
|
+
cursor += 1;
|
|
76063
|
+
if (tokens[cursor] !== "--create") return false;
|
|
76064
|
+
cursor += 1;
|
|
76065
|
+
for (; cursor < tokens.length; cursor += 1) {
|
|
76066
|
+
const tok = tokens[cursor];
|
|
76067
|
+
if (!tok) continue;
|
|
76068
|
+
if (!tok.startsWith("-")) return false;
|
|
76069
|
+
}
|
|
76070
|
+
return true;
|
|
76071
|
+
}
|
|
75757
76072
|
async function createAiPromptExecutor(cwd) {
|
|
75758
76073
|
const disposeLog = createLogger2("cron-runner");
|
|
75759
76074
|
return async (prompt, modelProvider, modelId) => {
|
|
@@ -75793,7 +76108,7 @@ function truncateOutput(stdout, stderr) {
|
|
|
75793
76108
|
}
|
|
75794
76109
|
return combined;
|
|
75795
76110
|
}
|
|
75796
|
-
var log14, DEFAULT_TIMEOUT_MS6, MAX_BUFFER, MAX_OUTPUT_LENGTH, DEFAULT_POLL_INTERVAL_MS, MIN_POLL_INTERVAL_MS, CronRunner, AI_AUTOMATION_SYSTEM_PROMPT;
|
|
76111
|
+
var log14, FUSION_BINARY_TOKENS, SHELL_METACHARACTERS_REGEX, DEFAULT_TIMEOUT_MS6, MAX_BUFFER, MAX_OUTPUT_LENGTH, DEFAULT_POLL_INTERVAL_MS, MIN_POLL_INTERVAL_MS, CronRunner, AI_AUTOMATION_SYSTEM_PROMPT;
|
|
75797
76112
|
var init_cron_runner = __esm({
|
|
75798
76113
|
"../engine/src/cron-runner.ts"() {
|
|
75799
76114
|
"use strict";
|
|
@@ -75802,6 +76117,14 @@ var init_cron_runner = __esm({
|
|
|
75802
76117
|
init_shell_utils();
|
|
75803
76118
|
init_pi();
|
|
75804
76119
|
log14 = createLogger2("cron-runner");
|
|
76120
|
+
FUSION_BINARY_TOKENS = /* @__PURE__ */ new Set([
|
|
76121
|
+
"fn",
|
|
76122
|
+
"fusion",
|
|
76123
|
+
"runfusion",
|
|
76124
|
+
"runfusion.ai",
|
|
76125
|
+
"@runfusion/fusion"
|
|
76126
|
+
]);
|
|
76127
|
+
SHELL_METACHARACTERS_REGEX = /[&|;<>`$()]/;
|
|
75805
76128
|
DEFAULT_TIMEOUT_MS6 = 5 * 60 * 1e3;
|
|
75806
76129
|
MAX_BUFFER = 1024 * 1024;
|
|
75807
76130
|
MAX_OUTPUT_LENGTH = 10 * 1024;
|
|
@@ -75942,6 +76265,9 @@ var init_cron_runner = __esm({
|
|
|
75942
76265
|
*/
|
|
75943
76266
|
async executeLegacyCommand(schedule, startedAt) {
|
|
75944
76267
|
log14.log(`Executing ${schedule.name} (${schedule.id}): ${schedule.command}`);
|
|
76268
|
+
if (isInProcessBackupCommand(schedule.command)) {
|
|
76269
|
+
return this.executeBackupInProcess(schedule, startedAt);
|
|
76270
|
+
}
|
|
75945
76271
|
try {
|
|
75946
76272
|
const timeoutMs = schedule.timeoutMs ?? DEFAULT_TIMEOUT_MS6;
|
|
75947
76273
|
const { stdout, stderr } = await execCommand(schedule.command, {
|
|
@@ -75972,6 +76298,47 @@ var init_cron_runner = __esm({
|
|
|
75972
76298
|
};
|
|
75973
76299
|
}
|
|
75974
76300
|
}
|
|
76301
|
+
/**
|
|
76302
|
+
* Run an auto-backup schedule in-process via the engine's open TaskStore,
|
|
76303
|
+
* bypassing the shell-out that would otherwise invoke an outdated fusion
|
|
76304
|
+
* binary on PATH. See `isInProcessBackupCommand` for the matching contract.
|
|
76305
|
+
*/
|
|
76306
|
+
async executeBackupInProcess(schedule, startedAt) {
|
|
76307
|
+
const action = await this.runBackupActionInProcess();
|
|
76308
|
+
if (action.success) {
|
|
76309
|
+
log14.log(`\u2713 ${schedule.name} completed in-process`);
|
|
76310
|
+
} else {
|
|
76311
|
+
log14.warn(`\u2717 ${schedule.name} in-process backup ${action.error ? `threw: ${action.error}` : `reported failure: ${action.output}`}`);
|
|
76312
|
+
}
|
|
76313
|
+
return {
|
|
76314
|
+
success: action.success,
|
|
76315
|
+
output: action.output,
|
|
76316
|
+
error: action.error,
|
|
76317
|
+
startedAt,
|
|
76318
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76319
|
+
};
|
|
76320
|
+
}
|
|
76321
|
+
/**
|
|
76322
|
+
* Shared in-process backup execution used by both the legacy-command path
|
|
76323
|
+
* and the command-step path. Returns the success/output/error tuple in
|
|
76324
|
+
* a shape that callers can wrap into either a run or a step result.
|
|
76325
|
+
*/
|
|
76326
|
+
async runBackupActionInProcess() {
|
|
76327
|
+
try {
|
|
76328
|
+
const { runBackupCommand: runBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
76329
|
+
const fusionDir = this.store.getFusionDir();
|
|
76330
|
+
const settings = await this.store.getSettings();
|
|
76331
|
+
const result = await runBackupCommand2(fusionDir, settings);
|
|
76332
|
+
return {
|
|
76333
|
+
success: result.success,
|
|
76334
|
+
output: truncateOutput(result.output ?? "", ""),
|
|
76335
|
+
error: result.success ? void 0 : result.output
|
|
76336
|
+
};
|
|
76337
|
+
} catch (err) {
|
|
76338
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76339
|
+
return { success: false, output: "", error: message };
|
|
76340
|
+
}
|
|
76341
|
+
}
|
|
75975
76342
|
/**
|
|
75976
76343
|
* Execute multiple steps sequentially.
|
|
75977
76344
|
* Aggregates per-step results into an overall AutomationRunResult.
|
|
@@ -76060,6 +76427,19 @@ var init_cron_runner = __esm({
|
|
|
76060
76427
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76061
76428
|
};
|
|
76062
76429
|
}
|
|
76430
|
+
if (isInProcessBackupCommand(step.command)) {
|
|
76431
|
+
const action = await this.runBackupActionInProcess();
|
|
76432
|
+
return {
|
|
76433
|
+
stepId: step.id,
|
|
76434
|
+
stepName: step.name,
|
|
76435
|
+
stepIndex,
|
|
76436
|
+
success: action.success,
|
|
76437
|
+
output: action.output,
|
|
76438
|
+
error: action.error,
|
|
76439
|
+
startedAt,
|
|
76440
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76441
|
+
};
|
|
76442
|
+
}
|
|
76063
76443
|
try {
|
|
76064
76444
|
const { stdout, stderr } = await execCommand(step.command, {
|
|
76065
76445
|
timeout: timeoutMs,
|
|
@@ -76251,6 +76631,7 @@ var init_routine_runner = __esm({
|
|
|
76251
76631
|
"../engine/src/routine-runner.ts"() {
|
|
76252
76632
|
"use strict";
|
|
76253
76633
|
import_cron_parser4 = __toESM(require_dist2(), 1);
|
|
76634
|
+
init_cron_runner();
|
|
76254
76635
|
init_logger2();
|
|
76255
76636
|
init_shell_utils();
|
|
76256
76637
|
log15 = createLogger2("routine-runner");
|
|
@@ -76408,6 +76789,30 @@ var init_routine_runner = __esm({
|
|
|
76408
76789
|
return this.executeCommand(routine.command ?? "", routine.timeoutMs, startedAt);
|
|
76409
76790
|
}
|
|
76410
76791
|
async executeCommand(command, timeoutMs, startedAt) {
|
|
76792
|
+
if (isInProcessBackupCommand(command) && this.options.taskStore) {
|
|
76793
|
+
try {
|
|
76794
|
+
const { runBackupCommand: runBackupCommand2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
76795
|
+
const fusionDir = this.options.taskStore.getFusionDir();
|
|
76796
|
+
const settings = await this.options.taskStore.getSettings();
|
|
76797
|
+
const result = await runBackupCommand2(fusionDir, settings);
|
|
76798
|
+
return {
|
|
76799
|
+
success: result.success,
|
|
76800
|
+
output: truncateOutput2(result.output ?? "", ""),
|
|
76801
|
+
error: result.success ? void 0 : result.output,
|
|
76802
|
+
startedAt,
|
|
76803
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76804
|
+
};
|
|
76805
|
+
} catch (err) {
|
|
76806
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76807
|
+
return {
|
|
76808
|
+
success: false,
|
|
76809
|
+
output: "",
|
|
76810
|
+
error: message,
|
|
76811
|
+
startedAt,
|
|
76812
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76813
|
+
};
|
|
76814
|
+
}
|
|
76815
|
+
}
|
|
76411
76816
|
try {
|
|
76412
76817
|
const { stdout, stderr } = await execAsync6(command, {
|
|
76413
76818
|
timeout: timeoutMs ?? DEFAULT_TIMEOUT_MS7,
|
|
@@ -77199,6 +77604,13 @@ var init_self_healing = __esm({
|
|
|
77199
77604
|
* stale in-progress/planning tasks that no longer have a live worker.
|
|
77200
77605
|
*/
|
|
77201
77606
|
async runStartupRecovery() {
|
|
77607
|
+
const settings = await this.store.getSettings();
|
|
77608
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
77609
|
+
log16.log(
|
|
77610
|
+
`Startup recovery skipped \u2014 ${settings.globalPause ? "global pause" : "engine pause"} is active`
|
|
77611
|
+
);
|
|
77612
|
+
return;
|
|
77613
|
+
}
|
|
77202
77614
|
const steps = [
|
|
77203
77615
|
{ name: "no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures().then(() => void 0) },
|
|
77204
77616
|
{ name: "completed-tasks", fn: () => this.recoverCompletedTasks().then(() => void 0) },
|
|
@@ -77549,27 +77961,34 @@ var init_self_healing = __esm({
|
|
|
77549
77961
|
log16.error(`Maintenance batch 1 step "${fn.name}" failed: ${stepErr instanceof Error ? stepErr.message : String(stepErr)}`);
|
|
77550
77962
|
}
|
|
77551
77963
|
}
|
|
77552
|
-
const
|
|
77553
|
-
|
|
77554
|
-
|
|
77555
|
-
|
|
77556
|
-
|
|
77557
|
-
|
|
77558
|
-
|
|
77559
|
-
|
|
77560
|
-
|
|
77561
|
-
|
|
77562
|
-
|
|
77563
|
-
|
|
77564
|
-
|
|
77565
|
-
|
|
77566
|
-
|
|
77567
|
-
|
|
77568
|
-
|
|
77569
|
-
|
|
77570
|
-
|
|
77571
|
-
|
|
77572
|
-
|
|
77964
|
+
const recoverySettings = await this.store.getSettings();
|
|
77965
|
+
if (recoverySettings.globalPause || recoverySettings.enginePaused) {
|
|
77966
|
+
log16.log(
|
|
77967
|
+
`Maintenance batch 2 skipped \u2014 ${recoverySettings.globalPause ? "global pause" : "engine pause"} is active`
|
|
77968
|
+
);
|
|
77969
|
+
} else {
|
|
77970
|
+
const batch2Fns = [
|
|
77971
|
+
{ name: "recover-completed-tasks", fn: () => this.recoverCompletedTasks() },
|
|
77972
|
+
{ name: "recover-stale-incomplete-review", fn: () => this.recoverStaleIncompleteReviewTasks() },
|
|
77973
|
+
{ name: "recover-failed-pre-merge-steps", fn: () => this.recoverReviewTasksWithFailedPreMergeSteps() },
|
|
77974
|
+
{ name: "recover-interrupted-merging", fn: () => this.recoverInterruptedMergingTasks() },
|
|
77975
|
+
{ name: "recover-mergeable-review", fn: () => this.recoverMergeableReviewTasks() },
|
|
77976
|
+
{ name: "recover-merged-review", fn: () => this.recoverMergedReviewTasks() },
|
|
77977
|
+
{ name: "recover-misclassified-failures", fn: () => this.recoverMisclassifiedFailures() },
|
|
77978
|
+
{ name: "recover-no-progress-no-task-done", fn: () => this.recoverNoProgressNoTaskDoneFailures() },
|
|
77979
|
+
{ name: "recover-partial-progress-no-task-done", fn: () => this.recoverPartialProgressNoTaskDoneFailures() },
|
|
77980
|
+
{ name: "recover-orphaned-executions", fn: () => this.recoverOrphanedExecutions() },
|
|
77981
|
+
{ name: "recover-approved-triage", fn: () => this.recoverApprovedTriageTasks() },
|
|
77982
|
+
{ name: "recover-orphaned-planning", fn: () => this.recoverOrphanedPlanningTasks() },
|
|
77983
|
+
{ name: "recover-ghost-review", fn: () => this.recoverGhostReviewTasks() }
|
|
77984
|
+
];
|
|
77985
|
+
for (const fn of batch2Fns) {
|
|
77986
|
+
try {
|
|
77987
|
+
await fn.fn();
|
|
77988
|
+
log16.log(`Maintenance batch 2 step "${fn.name}" succeeded`);
|
|
77989
|
+
} catch (stepErr) {
|
|
77990
|
+
log16.error(`Maintenance batch 2 step "${fn.name}" failed: ${stepErr instanceof Error ? stepErr.message : String(stepErr)}`);
|
|
77991
|
+
}
|
|
77573
77992
|
}
|
|
77574
77993
|
}
|
|
77575
77994
|
const batch3Fns = [
|
|
@@ -79302,6 +79721,10 @@ var init_in_process_runtime = __esm({
|
|
|
79302
79721
|
* before `start()` via `setMergeEnqueuer`.
|
|
79303
79722
|
*/
|
|
79304
79723
|
mergeEnqueuer;
|
|
79724
|
+
/** Tracks whether startup recovery was intentionally deferred due to pause state. */
|
|
79725
|
+
startupRecoveryDeferred = false;
|
|
79726
|
+
/** Prevent duplicate unpause recovery dispatches from racing each other. */
|
|
79727
|
+
resumeAfterUnpauseRunning = false;
|
|
79305
79728
|
/**
|
|
79306
79729
|
* Start the runtime and initialize all subsystems.
|
|
79307
79730
|
*
|
|
@@ -79335,7 +79758,7 @@ var init_in_process_runtime = __esm({
|
|
|
79335
79758
|
runtimeLog.log(`TaskStore initialized for project ${this.config.projectId}`);
|
|
79336
79759
|
}
|
|
79337
79760
|
this.messageStore = new MessageStoreClass(this.taskStore.getDatabase());
|
|
79338
|
-
this.pluginStore = new PluginStoreClass(this.
|
|
79761
|
+
this.pluginStore = new PluginStoreClass(this.config.workingDirectory);
|
|
79339
79762
|
await this.pluginStore.init();
|
|
79340
79763
|
this.pluginLoader = new PluginLoaderClass({
|
|
79341
79764
|
pluginStore: this.pluginStore,
|
|
@@ -79727,11 +80150,16 @@ var init_in_process_runtime = __esm({
|
|
|
79727
80150
|
this.selfHealingManager.start();
|
|
79728
80151
|
this.stuckTaskDetector.start();
|
|
79729
80152
|
this.setupEventForwarding();
|
|
79730
|
-
await this.
|
|
79731
|
-
|
|
79732
|
-
|
|
79733
|
-
runtimeLog.
|
|
79734
|
-
|
|
80153
|
+
const startupSettings = await this.taskStore.getSettings();
|
|
80154
|
+
if (startupSettings.globalPause || startupSettings.enginePaused) {
|
|
80155
|
+
this.startupRecoveryDeferred = true;
|
|
80156
|
+
runtimeLog.log(
|
|
80157
|
+
`Startup recovery deferred \u2014 ${startupSettings.globalPause ? "global pause" : "engine pause"} is active`
|
|
80158
|
+
);
|
|
80159
|
+
} else {
|
|
80160
|
+
this.startupRecoveryDeferred = false;
|
|
80161
|
+
await this.resumeStartupRecoverySequence();
|
|
80162
|
+
}
|
|
79735
80163
|
this.scheduler.start();
|
|
79736
80164
|
this.triageProcessor?.start();
|
|
79737
80165
|
this.missionExecutionLoop = missionExecutionLoop;
|
|
@@ -79897,6 +80325,45 @@ var init_in_process_runtime = __esm({
|
|
|
79897
80325
|
setMergeEnqueuer(enqueueMerge) {
|
|
79898
80326
|
this.mergeEnqueuer = enqueueMerge;
|
|
79899
80327
|
}
|
|
80328
|
+
/**
|
|
80329
|
+
* Resume executor/self-healing activity after an unpause transition.
|
|
80330
|
+
*
|
|
80331
|
+
* When startup recovery had been deferred, this replays the original startup
|
|
80332
|
+
* ordering so orphan resume and self-healing cannot race each other.
|
|
80333
|
+
*/
|
|
80334
|
+
async resumeAfterUnpause() {
|
|
80335
|
+
if (!this.taskStore || !this.executor || !this.selfHealingManager) {
|
|
80336
|
+
return;
|
|
80337
|
+
}
|
|
80338
|
+
if (this.resumeAfterUnpauseRunning) {
|
|
80339
|
+
return;
|
|
80340
|
+
}
|
|
80341
|
+
this.resumeAfterUnpauseRunning = true;
|
|
80342
|
+
try {
|
|
80343
|
+
const settings = await this.taskStore.getSettings();
|
|
80344
|
+
if (settings.globalPause || settings.enginePaused) {
|
|
80345
|
+
runtimeLog.log(
|
|
80346
|
+
`Unpause recovery still blocked \u2014 ${settings.globalPause ? "global pause" : "engine pause"} remains active`
|
|
80347
|
+
);
|
|
80348
|
+
return;
|
|
80349
|
+
}
|
|
80350
|
+
if (this.startupRecoveryDeferred) {
|
|
80351
|
+
await this.resumeStartupRecoverySequence();
|
|
80352
|
+
this.startupRecoveryDeferred = false;
|
|
80353
|
+
return;
|
|
80354
|
+
}
|
|
80355
|
+
await this.executor.resumeOrphaned();
|
|
80356
|
+
} finally {
|
|
80357
|
+
this.resumeAfterUnpauseRunning = false;
|
|
80358
|
+
}
|
|
80359
|
+
}
|
|
80360
|
+
async resumeStartupRecoverySequence() {
|
|
80361
|
+
await this.selfHealingManager.recoverNoProgressNoTaskDoneFailures();
|
|
80362
|
+
await this.executor.resumeOrphaned();
|
|
80363
|
+
void this.selfHealingManager.runStartupRecovery().catch((err) => {
|
|
80364
|
+
runtimeLog.error("Self-healing startup recovery failed:", err);
|
|
80365
|
+
});
|
|
80366
|
+
}
|
|
79900
80367
|
/**
|
|
79901
80368
|
* Get the project's TaskStore instance.
|
|
79902
80369
|
* @throws Error if runtime has not been started
|
|
@@ -83736,13 +84203,13 @@ ${detail}`
|
|
|
83736
84203
|
if (prev.globalPause && !s.globalPause) {
|
|
83737
84204
|
runtimeLog.log("Global unpause \u2014 resuming agentic activity");
|
|
83738
84205
|
try {
|
|
83739
|
-
const
|
|
83740
|
-
|
|
83741
|
-
(err) => runtimeLog.error("Failed to resume
|
|
84206
|
+
const runtime = this.runtime;
|
|
84207
|
+
runtime.resumeAfterUnpause?.().catch(
|
|
84208
|
+
(err) => runtimeLog.error("Failed to resume agentic activity on unpause:", err)
|
|
83742
84209
|
);
|
|
83743
84210
|
} catch (err) {
|
|
83744
84211
|
runtimeLog.warn(
|
|
83745
|
-
`Global unpause: failed to dispatch
|
|
84212
|
+
`Global unpause: failed to dispatch resumeAfterUnpause: ${err instanceof Error ? err.message : String(err)}`
|
|
83746
84213
|
);
|
|
83747
84214
|
}
|
|
83748
84215
|
if (s.autoMerge) {
|
|
@@ -83766,13 +84233,13 @@ ${detail}`
|
|
|
83766
84233
|
if (prev.enginePaused && !s.enginePaused) {
|
|
83767
84234
|
runtimeLog.log("Engine unpaused \u2014 resuming agentic activity");
|
|
83768
84235
|
try {
|
|
83769
|
-
const
|
|
83770
|
-
|
|
83771
|
-
(err) => runtimeLog.error("Failed to resume
|
|
84236
|
+
const runtime = this.runtime;
|
|
84237
|
+
runtime.resumeAfterUnpause?.().catch(
|
|
84238
|
+
(err) => runtimeLog.error("Failed to resume agentic activity on engine unpause:", err)
|
|
83772
84239
|
);
|
|
83773
84240
|
} catch (err) {
|
|
83774
84241
|
runtimeLog.warn(
|
|
83775
|
-
`Engine unpause: failed to dispatch
|
|
84242
|
+
`Engine unpause: failed to dispatch resumeAfterUnpause: ${err instanceof Error ? err.message : String(err)}`
|
|
83776
84243
|
);
|
|
83777
84244
|
}
|
|
83778
84245
|
if (s.autoMerge) {
|
|
@@ -136670,7 +137137,7 @@ Rules:
|
|
|
136670
137137
|
// ../dashboard/src/routes/register-agent-import-export-generation-routes.ts
|
|
136671
137138
|
import { createWriteStream } from "node:fs";
|
|
136672
137139
|
import * as fsPromises2 from "node:fs/promises";
|
|
136673
|
-
import { tmpdir as
|
|
137140
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
136674
137141
|
import { join as join45, resolve as resolve22 } from "node:path";
|
|
136675
137142
|
import { Readable } from "node:stream";
|
|
136676
137143
|
import { pipeline as streamPipeline } from "node:stream/promises";
|
|
@@ -136712,7 +137179,7 @@ function registerAgentImportExportRoutes(ctx) {
|
|
|
136712
137179
|
} else if (typeof outputDir === "string") {
|
|
136713
137180
|
throw badRequest("outputDir cannot be empty");
|
|
136714
137181
|
} else {
|
|
136715
|
-
resolvedOutputDir = await mkdtemp(join45(
|
|
137182
|
+
resolvedOutputDir = await mkdtemp(join45(tmpdir4(), "fusion-agent-export-"));
|
|
136716
137183
|
}
|
|
136717
137184
|
const result = await exportAgentsToDirectory2(agentsToExport, resolvedOutputDir, {
|
|
136718
137185
|
companyName: typeof companyName === "string" ? companyName : void 0,
|
|
@@ -137027,7 +137494,7 @@ ${body}`;
|
|
|
137027
137494
|
const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/main.tar.gz`;
|
|
137028
137495
|
let tempDir = null;
|
|
137029
137496
|
try {
|
|
137030
|
-
tempDir = await mkdtemp(join45(
|
|
137497
|
+
tempDir = await mkdtemp(join45(tmpdir4(), `fn-agent-import-${importCompanySlug}-`));
|
|
137031
137498
|
const archivePath = join45(tempDir, "archive.tar.gz");
|
|
137032
137499
|
const downloadController = new AbortController();
|
|
137033
137500
|
const downloadTimeout = setTimeout(() => downloadController.abort(), 3e4);
|
|
@@ -141554,6 +142021,21 @@ ${stderr}`;
|
|
|
141554
142021
|
});
|
|
141555
142022
|
});
|
|
141556
142023
|
}
|
|
142024
|
+
function buildSkippedStatusPayload(expectedVersion) {
|
|
142025
|
+
return {
|
|
142026
|
+
binary: {
|
|
142027
|
+
installed: false,
|
|
142028
|
+
invocation: FN_INSTALL_NPM
|
|
142029
|
+
},
|
|
142030
|
+
expectedVersion,
|
|
142031
|
+
state: "skipped",
|
|
142032
|
+
install: {
|
|
142033
|
+
npm: FN_INSTALL_NPM,
|
|
142034
|
+
curl: FN_INSTALL_CURL,
|
|
142035
|
+
package: FN_NPM_PACKAGE
|
|
142036
|
+
}
|
|
142037
|
+
};
|
|
142038
|
+
}
|
|
141557
142039
|
var INSTALL_TIMEOUT_MS, MAX_OUTPUT_BYTES2, registerFnBinaryRoutes;
|
|
141558
142040
|
var init_register_fn_binary_routes = __esm({
|
|
141559
142041
|
"../dashboard/src/routes/register-fn-binary-routes.ts"() {
|
|
@@ -141564,11 +142046,23 @@ var init_register_fn_binary_routes = __esm({
|
|
|
141564
142046
|
INSTALL_TIMEOUT_MS = 18e4;
|
|
141565
142047
|
MAX_OUTPUT_BYTES2 = 64 * 1024;
|
|
141566
142048
|
registerFnBinaryRoutes = (ctx) => {
|
|
141567
|
-
const { router, rethrowAsApiError: rethrowAsApiError8 } = ctx;
|
|
142049
|
+
const { router, rethrowAsApiError: rethrowAsApiError8, store } = ctx;
|
|
142050
|
+
async function isCheckEnabled() {
|
|
142051
|
+
try {
|
|
142052
|
+
const settings = await store.getSettings();
|
|
142053
|
+
return settings.fnBinaryCheckEnabled !== false;
|
|
142054
|
+
} catch {
|
|
142055
|
+
return true;
|
|
142056
|
+
}
|
|
142057
|
+
}
|
|
141568
142058
|
router.get("/system/fn-binary/status", async (_req, res) => {
|
|
141569
142059
|
try {
|
|
141570
|
-
const binary = await detectFnBinary();
|
|
141571
142060
|
const expectedVersion = getCliPackageVersion();
|
|
142061
|
+
if (!await isCheckEnabled()) {
|
|
142062
|
+
res.json(buildSkippedStatusPayload(expectedVersion));
|
|
142063
|
+
return;
|
|
142064
|
+
}
|
|
142065
|
+
const binary = await detectFnBinary();
|
|
141572
142066
|
res.json(buildStatusPayload(binary, expectedVersion));
|
|
141573
142067
|
} catch (err) {
|
|
141574
142068
|
if (err instanceof ApiError) throw err;
|
|
@@ -141577,6 +142071,13 @@ var init_register_fn_binary_routes = __esm({
|
|
|
141577
142071
|
});
|
|
141578
142072
|
router.post("/system/fn-binary/install", async (_req, res) => {
|
|
141579
142073
|
try {
|
|
142074
|
+
if (!await isCheckEnabled()) {
|
|
142075
|
+
throw new ApiError(
|
|
142076
|
+
409,
|
|
142077
|
+
"fn-binary checks are disabled in global settings (fnBinaryCheckEnabled=false). Re-enable them to install via the dashboard.",
|
|
142078
|
+
{ code: "FN_BINARY_CHECK_DISABLED" }
|
|
142079
|
+
);
|
|
142080
|
+
}
|
|
141580
142081
|
const installResult = await runNpmInstall();
|
|
141581
142082
|
const binary = await detectFnBinary();
|
|
141582
142083
|
const expectedVersion = getCliPackageVersion();
|
|
@@ -155723,6 +156224,43 @@ data: ${JSON.stringify(entry)}
|
|
|
155723
156224
|
scopedStore.off("agent:log", onAgentLog);
|
|
155724
156225
|
});
|
|
155725
156226
|
});
|
|
156227
|
+
app.get("/api/agents/:id/runs/:runId/logs/stream", async (req, res) => {
|
|
156228
|
+
const agentId = req.params.id;
|
|
156229
|
+
const runId = req.params.runId;
|
|
156230
|
+
const projectId = typeof req.query.projectId === "string" ? req.query.projectId : void 0;
|
|
156231
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
156232
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
156233
|
+
res.setHeader("Connection", "keep-alive");
|
|
156234
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
156235
|
+
res.flushHeaders();
|
|
156236
|
+
res.write(": connected\n\n");
|
|
156237
|
+
const engineManager = options?.engineManager;
|
|
156238
|
+
const engine2 = engineManager && projectId ? engineManager.getEngine(projectId) : options?.engine;
|
|
156239
|
+
const agentStore = engine2?.getAgentStore();
|
|
156240
|
+
if (!agentStore) {
|
|
156241
|
+
res.write(`event: error
|
|
156242
|
+
data: ${JSON.stringify({ message: "No active engine for project" })}
|
|
156243
|
+
|
|
156244
|
+
`);
|
|
156245
|
+
res.end();
|
|
156246
|
+
return;
|
|
156247
|
+
}
|
|
156248
|
+
const onRunLog = (eventAgentId, eventRunId, entry) => {
|
|
156249
|
+
if (eventAgentId !== agentId || eventRunId !== runId) return;
|
|
156250
|
+
res.write(`event: agent:log
|
|
156251
|
+
data: ${JSON.stringify(entry)}
|
|
156252
|
+
|
|
156253
|
+
`);
|
|
156254
|
+
};
|
|
156255
|
+
agentStore.on("run:log", onRunLog);
|
|
156256
|
+
const heartbeat = setInterval(() => {
|
|
156257
|
+
res.write(": heartbeat\n\n");
|
|
156258
|
+
}, 3e4);
|
|
156259
|
+
req.on("close", () => {
|
|
156260
|
+
clearInterval(heartbeat);
|
|
156261
|
+
agentStore.off("run:log", onRunLog);
|
|
156262
|
+
});
|
|
156263
|
+
});
|
|
155726
156264
|
app.get("/api/terminal/sessions/:id/stream", rateLimit(RATE_LIMITS.sse), (req, res) => {
|
|
155727
156265
|
const sessionId = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
|
|
155728
156266
|
res.setHeader("Content-Type", "text/event-stream");
|
|
@@ -158348,7 +158886,7 @@ var app_exports = {};
|
|
|
158348
158886
|
__export(app_exports, {
|
|
158349
158887
|
DashboardApp: () => DashboardApp
|
|
158350
158888
|
});
|
|
158351
|
-
import { useState as useState2, useSyncExternalStore, useCallback as useCallback2, useEffect as useEffect2 } from "react";
|
|
158889
|
+
import { useState as useState2, useSyncExternalStore, useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
|
|
158352
158890
|
import { Box, Text, useInput, useApp, useStdout } from "ink";
|
|
158353
158891
|
import Spinner from "ink-spinner";
|
|
158354
158892
|
import TextInput from "ink-text-input";
|
|
@@ -159130,7 +159668,8 @@ function formatLogTime(iso) {
|
|
|
159130
159668
|
function TaskDetailScreen({
|
|
159131
159669
|
task,
|
|
159132
159670
|
projectPath,
|
|
159133
|
-
interactiveData
|
|
159671
|
+
interactiveData,
|
|
159672
|
+
controller
|
|
159134
159673
|
}) {
|
|
159135
159674
|
const { stdout } = useStdout();
|
|
159136
159675
|
const cols = stdout?.columns ?? 80;
|
|
@@ -159195,6 +159734,27 @@ function TaskDetailScreen({
|
|
|
159195
159734
|
useEffect2(() => {
|
|
159196
159735
|
if (autoFollow) setLogScrollOffset(0);
|
|
159197
159736
|
}, [autoFollow, logCount]);
|
|
159737
|
+
const WHEEL_STEP = 3;
|
|
159738
|
+
const logCountRef = useRef(logCount);
|
|
159739
|
+
const logPaneRowsRef = useRef(logPaneRows);
|
|
159740
|
+
logCountRef.current = logCount;
|
|
159741
|
+
logPaneRowsRef.current = logPaneRows;
|
|
159742
|
+
useEffect2(() => {
|
|
159743
|
+
return controller.onWheel((dir2) => {
|
|
159744
|
+
const maxOffset = Math.max(0, logCountRef.current - logPaneRowsRef.current);
|
|
159745
|
+
if (maxOffset === 0) return;
|
|
159746
|
+
if (dir2 === "up") {
|
|
159747
|
+
setAutoFollow(false);
|
|
159748
|
+
setLogScrollOffset((o) => Math.min(maxOffset, o + WHEEL_STEP));
|
|
159749
|
+
} else {
|
|
159750
|
+
setLogScrollOffset((o) => {
|
|
159751
|
+
const next = Math.max(0, o - WHEEL_STEP);
|
|
159752
|
+
if (next === 0) setAutoFollow(true);
|
|
159753
|
+
return next;
|
|
159754
|
+
});
|
|
159755
|
+
}
|
|
159756
|
+
});
|
|
159757
|
+
}, [controller]);
|
|
159198
159758
|
useInput((input, key) => {
|
|
159199
159759
|
if (detail && detail !== "unavailable" && detail.recentLogs.length > 0) {
|
|
159200
159760
|
const maxOffset = Math.max(0, detail.recentLogs.length - logPaneRows);
|
|
@@ -159565,7 +160125,8 @@ function BoardView({ state, controller }) {
|
|
|
159565
160125
|
{
|
|
159566
160126
|
task: selectedTask,
|
|
159567
160127
|
projectPath: selectedProject?.path ?? null,
|
|
159568
|
-
interactiveData: state.interactiveData
|
|
160128
|
+
interactiveData: state.interactiveData,
|
|
160129
|
+
controller
|
|
159569
160130
|
}
|
|
159570
160131
|
) }) : tasksState.loading ? /* @__PURE__ */ jsxs(Box, { justifyContent: "center", alignItems: "center", flexGrow: 1, gap: 1, children: [
|
|
159571
160132
|
/* @__PURE__ */ jsx(Text, { color: "white", children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
|
|
@@ -160387,7 +160948,7 @@ function PushModal({
|
|
|
160387
160948
|
}
|
|
160388
160949
|
);
|
|
160389
160950
|
}
|
|
160390
|
-
function GitView({ state }) {
|
|
160951
|
+
function GitView({ state, controller }) {
|
|
160391
160952
|
const { stdout } = useStdout();
|
|
160392
160953
|
const cols = stdout?.columns ?? 80;
|
|
160393
160954
|
const data = state.interactiveData;
|
|
@@ -160575,6 +161136,23 @@ function GitView({ state }) {
|
|
|
160575
161136
|
}
|
|
160576
161137
|
}
|
|
160577
161138
|
});
|
|
161139
|
+
const gitWheelRef = useRef({ activePane, commits, branches, worktrees });
|
|
161140
|
+
gitWheelRef.current = { activePane, commits, branches, worktrees };
|
|
161141
|
+
useEffect2(() => {
|
|
161142
|
+
if (state.interactiveView !== "git") return;
|
|
161143
|
+
return controller.onWheel((dir2) => {
|
|
161144
|
+
const { activePane: pane, commits: cs, branches: bs, worktrees: ws } = gitWheelRef.current;
|
|
161145
|
+
const STEP = 3;
|
|
161146
|
+
const delta = dir2 === "up" ? -STEP : STEP;
|
|
161147
|
+
if (pane === "commits") {
|
|
161148
|
+
setCommitIndex((i) => Math.max(0, Math.min(cs.length - 1, i + delta)));
|
|
161149
|
+
} else if (pane === "branches") {
|
|
161150
|
+
setBranchIndex((i) => Math.max(0, Math.min(bs.length - 1, i + delta)));
|
|
161151
|
+
} else if (pane === "worktrees") {
|
|
161152
|
+
setWorktreeIndex((i) => Math.max(0, Math.min(ws.length - 1, i + delta)));
|
|
161153
|
+
}
|
|
161154
|
+
});
|
|
161155
|
+
}, [controller, state.interactiveView]);
|
|
160578
161156
|
const isNarrow = cols < NARROW_THRESHOLD;
|
|
160579
161157
|
const leftWidth = Math.max(24, Math.floor(cols * 0.35));
|
|
160580
161158
|
const rightWidth = cols - leftWidth - 1;
|
|
@@ -160916,7 +161494,7 @@ function entriesToNodes(entries, depth) {
|
|
|
160916
161494
|
const files = filtered.filter((e) => !e.isDirectory).sort((a, b) => a.name.localeCompare(b.name));
|
|
160917
161495
|
return [...dirs, ...files].map((e) => ({ entry: e, depth, expanded: false, children: void 0 }));
|
|
160918
161496
|
}
|
|
160919
|
-
function FilesView({ state }) {
|
|
161497
|
+
function FilesView({ state, controller }) {
|
|
160920
161498
|
const { stdout } = useStdout();
|
|
160921
161499
|
const cols = stdout?.columns ?? 80;
|
|
160922
161500
|
const data = state.interactiveData;
|
|
@@ -161124,6 +161702,23 @@ function FilesView({ state }) {
|
|
|
161124
161702
|
}
|
|
161125
161703
|
}
|
|
161126
161704
|
}, { isActive: state.interactiveView === "files" });
|
|
161705
|
+
const filesWheelRef = useRef({ focusedPane, flatNodes, previewResult, previewHeight });
|
|
161706
|
+
filesWheelRef.current = { focusedPane, flatNodes, previewResult, previewHeight };
|
|
161707
|
+
useEffect2(() => {
|
|
161708
|
+
if (state.interactiveView !== "files") return;
|
|
161709
|
+
return controller.onWheel((dir2) => {
|
|
161710
|
+
const { focusedPane: pane, flatNodes: nodes, previewResult: pr, previewHeight: ph } = filesWheelRef.current;
|
|
161711
|
+
const STEP = 3;
|
|
161712
|
+
const delta = dir2 === "up" ? -STEP : STEP;
|
|
161713
|
+
if (pane === "tree") {
|
|
161714
|
+
setSelectedIndex((i) => Math.max(0, Math.min(nodes.length - 1, i + delta)));
|
|
161715
|
+
} else {
|
|
161716
|
+
const lineCount = pr?.lineCount ?? 0;
|
|
161717
|
+
const maxScroll = Math.max(0, lineCount - ph);
|
|
161718
|
+
setPreviewScroll((s) => Math.max(0, Math.min(maxScroll, s + delta)));
|
|
161719
|
+
}
|
|
161720
|
+
});
|
|
161721
|
+
}, [controller, state.interactiveView]);
|
|
161127
161722
|
const isNarrow = cols < NARROW_THRESHOLD;
|
|
161128
161723
|
const treeWidth = isNarrow ? Math.max(20, cols - 2) : Math.max(20, Math.floor(cols * 0.38));
|
|
161129
161724
|
const previewEntry = selectedNode && !selectedNode.entry.isDirectory ? selectedNode.entry : null;
|
|
@@ -161287,8 +161882,8 @@ function InteractiveMode({ state, controller }) {
|
|
|
161287
161882
|
state.interactiveView === "board" && /* @__PURE__ */ jsx(BoardView, { state, controller }),
|
|
161288
161883
|
state.interactiveView === "agents" && /* @__PURE__ */ jsx(AgentsView, { state }),
|
|
161289
161884
|
state.interactiveView === "settings" && /* @__PURE__ */ jsx(SettingsInteractiveView, { state, controller }),
|
|
161290
|
-
state.interactiveView === "git" && /* @__PURE__ */ jsx(GitView, { state }),
|
|
161291
|
-
state.interactiveView === "files" && /* @__PURE__ */ jsx(FilesView, { state })
|
|
161885
|
+
state.interactiveView === "git" && /* @__PURE__ */ jsx(GitView, { state, controller }),
|
|
161886
|
+
state.interactiveView === "files" && /* @__PURE__ */ jsx(FilesView, { state, controller })
|
|
161292
161887
|
] })
|
|
161293
161888
|
] });
|
|
161294
161889
|
}
|
|
@@ -161319,6 +161914,23 @@ function DashboardApp({ controller }) {
|
|
|
161319
161914
|
useCallback2((cb) => controller.subscribe(cb), [controller]),
|
|
161320
161915
|
useCallback2(() => controller.getSnapshot(), [controller])
|
|
161321
161916
|
);
|
|
161917
|
+
const wheelStateRef = useRef(state);
|
|
161918
|
+
wheelStateRef.current = state;
|
|
161919
|
+
useEffect2(() => {
|
|
161920
|
+
return controller.onWheel((dir2) => {
|
|
161921
|
+
const s = wheelStateRef.current;
|
|
161922
|
+
if (s.activeSection !== "logs") return;
|
|
161923
|
+
const filtered = controller.getFilteredLogEntries();
|
|
161924
|
+
if (filtered.length === 0) return;
|
|
161925
|
+
const WHEEL_STEP = 3;
|
|
161926
|
+
const cur = s.selectedLogIndex;
|
|
161927
|
+
if (dir2 === "up") {
|
|
161928
|
+
controller.setSelectedLogIndex(Math.max(0, cur - WHEEL_STEP));
|
|
161929
|
+
} else {
|
|
161930
|
+
controller.setSelectedLogIndex(Math.min(filtered.length - 1, cur + WHEEL_STEP));
|
|
161931
|
+
}
|
|
161932
|
+
});
|
|
161933
|
+
}, [controller]);
|
|
161322
161934
|
const [qrOverlay, setQrOverlay] = useState2(null);
|
|
161323
161935
|
useInput((input, key) => {
|
|
161324
161936
|
if ((input === "q" || input === "Q") && !key.ctrl || key.ctrl && input === "c") {
|
|
@@ -161701,6 +162313,15 @@ var init_controller = __esm({
|
|
|
161701
162313
|
// no remote API is wired up).
|
|
161702
162314
|
remoteStatus = null;
|
|
161703
162315
|
remoteStatusTimer = null;
|
|
162316
|
+
// Mouse-wheel handling. We enable xterm SGR mouse mode in start() so the
|
|
162317
|
+
// terminal sends button reports for wheel up/down (buttons 64/65). A
|
|
162318
|
+
// parallel `data` listener parses those reports and dispatches to wheel
|
|
162319
|
+
// handlers. Ink's own keypress parser ignores SGR mouse sequences so
|
|
162320
|
+
// long as the full sequence (including the leading ESC) arrives in one
|
|
162321
|
+
// chunk — which it does once raw mode is enabled before mouse mode is
|
|
162322
|
+
// requested. (See ink#222 / @zenobius/ink-mouse for prior art.)
|
|
162323
|
+
wheelHandlers = /* @__PURE__ */ new Set();
|
|
162324
|
+
mouseStdinListener = null;
|
|
161704
162325
|
constructor() {
|
|
161705
162326
|
this.logBuffer = new LogRingBuffer();
|
|
161706
162327
|
}
|
|
@@ -161709,6 +162330,15 @@ var init_controller = __esm({
|
|
|
161709
162330
|
this.subscribers.add(callback);
|
|
161710
162331
|
return () => this.subscribers.delete(callback);
|
|
161711
162332
|
}
|
|
162333
|
+
/**
|
|
162334
|
+
* Subscribe to mouse-wheel events. Direction is "up" (scroll back/older
|
|
162335
|
+
* content) or "down" (scroll forward/newer content). Only fires while the
|
|
162336
|
+
* dashboard is running and the terminal supports xterm mouse reporting.
|
|
162337
|
+
*/
|
|
162338
|
+
onWheel(handler) {
|
|
162339
|
+
this.wheelHandlers.add(handler);
|
|
162340
|
+
return () => this.wheelHandlers.delete(handler);
|
|
162341
|
+
}
|
|
161712
162342
|
getSnapshot() {
|
|
161713
162343
|
if (this.cachedSnapshot) return this.cachedSnapshot;
|
|
161714
162344
|
this.cachedSnapshot = {
|
|
@@ -162093,6 +162723,10 @@ var init_controller = __esm({
|
|
|
162093
162723
|
this.inkInstance = render(
|
|
162094
162724
|
createElement(DashboardApp2, { controller: this })
|
|
162095
162725
|
);
|
|
162726
|
+
if (process.stdin?.isTTY) {
|
|
162727
|
+
process.stdout.write("\x1B[?1000h\x1B[?1006h");
|
|
162728
|
+
this.installMouseListener();
|
|
162729
|
+
}
|
|
162096
162730
|
this.resizeListener = () => {
|
|
162097
162731
|
if (this.resizeDebounceTimer) clearTimeout(this.resizeDebounceTimer);
|
|
162098
162732
|
this.resizeDebounceTimer = setTimeout(() => {
|
|
@@ -162199,10 +162833,48 @@ var init_controller = __esm({
|
|
|
162199
162833
|
this.inkInstance = null;
|
|
162200
162834
|
}
|
|
162201
162835
|
if (process.stdout?.isTTY && typeof process.stdout.write === "function") {
|
|
162836
|
+
this.uninstallMouseListener();
|
|
162837
|
+
process.stdout.write("\x1B[?1006l\x1B[?1000l");
|
|
162202
162838
|
process.stdout.write("\x1B[?1049l");
|
|
162203
162839
|
}
|
|
162204
162840
|
}
|
|
162205
162841
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
162842
|
+
// Attach a parallel `data` listener that decodes xterm SGR mouse
|
|
162843
|
+
// sequences and dispatches wheel events. Ink's own listener is also
|
|
162844
|
+
// attached; SGR sequences arrive as a single chunk that Ink's keypress
|
|
162845
|
+
// parser silently ignores, so we don't need to (and shouldn't) strip
|
|
162846
|
+
// them from the stream.
|
|
162847
|
+
installMouseListener() {
|
|
162848
|
+
if (this.mouseStdinListener) return;
|
|
162849
|
+
const mouseRe = /\x1b\[<(\d+);\d+;\d+[Mm]/g;
|
|
162850
|
+
const listener = (chunk) => {
|
|
162851
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
162852
|
+
if (text.indexOf("\x1B[<") === -1) return;
|
|
162853
|
+
mouseRe.lastIndex = 0;
|
|
162854
|
+
let m;
|
|
162855
|
+
while ((m = mouseRe.exec(text)) !== null) {
|
|
162856
|
+
const btn = Number.parseInt(m[1] ?? "", 10);
|
|
162857
|
+
if (btn === 64) this.dispatchWheel("up");
|
|
162858
|
+
else if (btn === 65) this.dispatchWheel("down");
|
|
162859
|
+
}
|
|
162860
|
+
};
|
|
162861
|
+
this.mouseStdinListener = listener;
|
|
162862
|
+
process.stdin.on("data", listener);
|
|
162863
|
+
}
|
|
162864
|
+
uninstallMouseListener() {
|
|
162865
|
+
if (!this.mouseStdinListener) return;
|
|
162866
|
+
process.stdin.off("data", this.mouseStdinListener);
|
|
162867
|
+
this.mouseStdinListener = null;
|
|
162868
|
+
}
|
|
162869
|
+
dispatchWheel(direction) {
|
|
162870
|
+
for (const handler of this.wheelHandlers) {
|
|
162871
|
+
try {
|
|
162872
|
+
handler(direction);
|
|
162873
|
+
} catch (err) {
|
|
162874
|
+
tuiDebug2("wheel-handler-error", { err: String(err) });
|
|
162875
|
+
}
|
|
162876
|
+
}
|
|
162877
|
+
}
|
|
162206
162878
|
clampSelectedLogIndex(entries) {
|
|
162207
162879
|
if (entries.length === 0) {
|
|
162208
162880
|
this.selectedLogIndex = 0;
|
|
@@ -170806,7 +171478,7 @@ __export(native_patch_exports, {
|
|
|
170806
171478
|
});
|
|
170807
171479
|
import { join as join68, basename as basename21, dirname as dirname28 } from "node:path";
|
|
170808
171480
|
import { existsSync as existsSync51, copyFileSync, mkdirSync as mkdirSync12, symlinkSync as symlinkSync2, rmSync as rmSync5, lstatSync as lstatSync3, readlinkSync as readlinkSync2 } from "node:fs";
|
|
170809
|
-
import { tmpdir as
|
|
171481
|
+
import { tmpdir as tmpdir5 } from "node:os";
|
|
170810
171482
|
function findStagedNativeDir2() {
|
|
170811
171483
|
const platform4 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
|
|
170812
171484
|
const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
|
|
@@ -170851,7 +171523,7 @@ function setupNativeResolution() {
|
|
|
170851
171523
|
process.env.NODE_PTY_SPAWN_HELPER_DIR = nativeDir;
|
|
170852
171524
|
}
|
|
170853
171525
|
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
|
|
170854
|
-
const tmpRoot = join68(
|
|
171526
|
+
const tmpRoot = join68(tmpdir5(), `fn-bunfs-${process.pid}`);
|
|
170855
171527
|
const fnDir = join68(tmpRoot, "fn");
|
|
170856
171528
|
const prebuildsDir = join68(fnDir, "prebuilds");
|
|
170857
171529
|
const platformDir = join68(prebuildsDir, basename21(nativeDir));
|
|
@@ -170939,7 +171611,7 @@ var init_native_patch = __esm({
|
|
|
170939
171611
|
import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, readFileSync as readFileSync24, symlinkSync as symlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
170940
171612
|
import { createRequire as createRequire7 } from "node:module";
|
|
170941
171613
|
import { join as join69, dirname as dirname29, resolve as resolve41 } from "node:path";
|
|
170942
|
-
import { tmpdir as
|
|
171614
|
+
import { tmpdir as tmpdir6 } from "node:os";
|
|
170943
171615
|
import { performance as performance3 } from "node:perf_hooks";
|
|
170944
171616
|
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
170945
171617
|
var isBunBinary3 = typeof Bun !== "undefined" && !!Bun.embeddedFiles;
|
|
@@ -170947,7 +171619,7 @@ function configurePiPackage() {
|
|
|
170947
171619
|
if (process.env.PI_PACKAGE_DIR) {
|
|
170948
171620
|
return;
|
|
170949
171621
|
}
|
|
170950
|
-
const tmp = mkdtempSync2(join69(
|
|
171622
|
+
const tmp = mkdtempSync2(join69(tmpdir6(), "fn-pkg-"));
|
|
170951
171623
|
let packageJson = {
|
|
170952
171624
|
name: "pi",
|
|
170953
171625
|
version: "0.1.0",
|