runcastle 1.2.2 → 1.2.4
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/bin/runcastle.js +346 -204
- package/drizzle/0019_freezing_natasha_romanoff.sql +2 -0
- package/drizzle/meta/0019_snapshot.json +860 -0
- package/drizzle/meta/_journal.json +7 -0
- package/index.js +341 -199
- package/package.json +1 -1
- package/web/assets/index-CzDDtq7w.js +142 -0
- package/web/index.html +1 -1
- package/web/assets/index-Co7aViQi.js +0 -142
package/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(exports_paths, {
|
|
|
25
25
|
projectWorktreesDir: () => projectWorktreesDir,
|
|
26
26
|
prodDataDir: () => prodDataDir,
|
|
27
27
|
logsDir: () => logsDir,
|
|
28
|
+
installIdPath: () => installIdPath,
|
|
28
29
|
featureDocsRel: () => featureDocsRel,
|
|
29
30
|
envPath: () => envPath,
|
|
30
31
|
devDataDir: () => devDataDir,
|
|
@@ -63,6 +64,9 @@ function configPath() {
|
|
|
63
64
|
function envPath() {
|
|
64
65
|
return join(dataDir(), ".env");
|
|
65
66
|
}
|
|
67
|
+
function installIdPath() {
|
|
68
|
+
return join(dataDir(), "install-id");
|
|
69
|
+
}
|
|
66
70
|
function logsDir() {
|
|
67
71
|
return join(dataDir(), "logs");
|
|
68
72
|
}
|
|
@@ -245,7 +249,7 @@ var init_ids = () => {};
|
|
|
245
249
|
|
|
246
250
|
// ../core/src/schemas.ts
|
|
247
251
|
import * as z2 from "zod";
|
|
248
|
-
var Phase, TicketStatus, SessionKind, RunStatus, FeatureStatus, SessionStatus, TicketInput, Ticket, WaypointType, WaypointStatus, WaypointDisposition, WaypointInput, Waypoint, TestNoteStatus, TestNote, PREPARED_KEYS, PreparedKey, DRIVE_LOOP_KEYS, FindingSource, PROJECT_NAME_MAX = 80, ProjectName, Project, ProjectFinding, Feature, SessionRow, Run, SettingSource, SettingScope, SettingField, SettingsView, SettingsUpdateInput, EventRow;
|
|
252
|
+
var Phase, TicketStatus, SessionKind, SessionPurpose, MergeBranchPair, RunStatus, FeatureStatus, SessionStatus, TicketInput, Ticket, WaypointType, WaypointStatus, WaypointDisposition, WaypointInput, Waypoint, TestNoteStatus, TestNote, PREPARED_KEYS, PreparedKey, DRIVE_LOOP_KEYS, FindingSource, PROJECT_NAME_MAX = 80, ProjectName, Project, ProjectFinding, Feature, SessionRow, Run, SettingSource, SettingScope, SettingField, SettingsView, SettingsUpdateInput, EventRow;
|
|
249
253
|
var init_schemas = __esm(() => {
|
|
250
254
|
Phase = z2.enum([
|
|
251
255
|
"ideation",
|
|
@@ -265,6 +269,11 @@ var init_schemas = __esm(() => {
|
|
|
265
269
|
"prepare",
|
|
266
270
|
"project"
|
|
267
271
|
]);
|
|
272
|
+
SessionPurpose = z2.enum(["resolve-conflict"]);
|
|
273
|
+
MergeBranchPair = z2.object({
|
|
274
|
+
mergeFrom: z2.string(),
|
|
275
|
+
mergeInto: z2.string()
|
|
276
|
+
});
|
|
268
277
|
RunStatus = z2.enum(["running", "succeeded", "failed", "cancelled"]);
|
|
269
278
|
FeatureStatus = z2.enum(["active", "shipped", "archived"]);
|
|
270
279
|
SessionStatus = z2.enum(["launching", "live", "ended"]);
|
|
@@ -381,6 +390,8 @@ var init_schemas = __esm(() => {
|
|
|
381
390
|
featureId: z2.string().optional(),
|
|
382
391
|
projectId: z2.string().optional(),
|
|
383
392
|
kind: SessionKind,
|
|
393
|
+
purpose: SessionPurpose.optional(),
|
|
394
|
+
purposeData: MergeBranchPair.optional(),
|
|
384
395
|
ccSessionId: z2.string().optional(),
|
|
385
396
|
transcriptPath: z2.string().optional(),
|
|
386
397
|
status: SessionStatus,
|
|
@@ -619,6 +630,8 @@ var init_db_schema = __esm(() => {
|
|
|
619
630
|
projectId: text("project_id"),
|
|
620
631
|
lap: integer("lap").notNull().default(1),
|
|
621
632
|
kind: text("kind").notNull().$type(),
|
|
633
|
+
purpose: text("purpose").$type(),
|
|
634
|
+
purposeData: text("purpose_data", { mode: "json" }).$type(),
|
|
622
635
|
ccSessionId: text("cc_session_id"),
|
|
623
636
|
transcriptPath: text("transcript_path"),
|
|
624
637
|
status: text("status").notNull().$type(),
|
|
@@ -913,6 +926,23 @@ var init_resolve_executable = __esm(() => {
|
|
|
913
926
|
};
|
|
914
927
|
});
|
|
915
928
|
|
|
929
|
+
// src/pty/kill-tree.ts
|
|
930
|
+
import { execFile } from "child_process";
|
|
931
|
+
import { promisify } from "util";
|
|
932
|
+
async function killProcessTree(pid) {
|
|
933
|
+
try {
|
|
934
|
+
if (process.platform === "win32") {
|
|
935
|
+
await execFileAsync("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true });
|
|
936
|
+
} else {
|
|
937
|
+
process.kill(-pid, "SIGTERM");
|
|
938
|
+
}
|
|
939
|
+
} catch {}
|
|
940
|
+
}
|
|
941
|
+
var execFileAsync;
|
|
942
|
+
var init_kill_tree = __esm(() => {
|
|
943
|
+
execFileAsync = promisify(execFile);
|
|
944
|
+
});
|
|
945
|
+
|
|
916
946
|
// src/pty/pty-sidecar.ts
|
|
917
947
|
import { spawn } from "child_process";
|
|
918
948
|
import { createRequire } from "module";
|
|
@@ -1094,6 +1124,9 @@ ${why.replace(/\n/g, `\r
|
|
|
1094
1124
|
}
|
|
1095
1125
|
}, 500);
|
|
1096
1126
|
},
|
|
1127
|
+
killTree() {
|
|
1128
|
+
return killProcessTree(process.platform === "win32" ? child.pid ?? pid : pid);
|
|
1129
|
+
},
|
|
1097
1130
|
get pid() {
|
|
1098
1131
|
return pid;
|
|
1099
1132
|
},
|
|
@@ -1106,6 +1139,7 @@ var HOST_PATH;
|
|
|
1106
1139
|
var init_pty_sidecar = __esm(() => {
|
|
1107
1140
|
init_asset_paths();
|
|
1108
1141
|
init_resolve_executable();
|
|
1142
|
+
init_kill_tree();
|
|
1109
1143
|
HOST_PATH = resolveAsset(ASSET_ENV.ptyHost, fileURLToPath(new URL("./pty-host.cjs", import.meta.url)));
|
|
1110
1144
|
});
|
|
1111
1145
|
|
|
@@ -1192,6 +1226,9 @@ function createNativePtySession(cmd, args, opts) {
|
|
|
1192
1226
|
proc.kill();
|
|
1193
1227
|
} catch {}
|
|
1194
1228
|
},
|
|
1229
|
+
killTree() {
|
|
1230
|
+
return killProcessTree(proc.pid);
|
|
1231
|
+
},
|
|
1195
1232
|
get pid() {
|
|
1196
1233
|
return proc.pid;
|
|
1197
1234
|
},
|
|
@@ -1203,6 +1240,7 @@ function createNativePtySession(cmd, args, opts) {
|
|
|
1203
1240
|
var cached = null, BACKEND_LOGGED;
|
|
1204
1241
|
var init_pty = __esm(() => {
|
|
1205
1242
|
init_resolve_executable();
|
|
1243
|
+
init_kill_tree();
|
|
1206
1244
|
init_pty_sidecar();
|
|
1207
1245
|
BACKEND_LOGGED = Symbol.for("runcastle.pty.backend.logged");
|
|
1208
1246
|
});
|
|
@@ -1540,6 +1578,8 @@ function rowToSession(row) {
|
|
|
1540
1578
|
featureId: row.featureId ?? undefined,
|
|
1541
1579
|
projectId: row.projectId ?? undefined,
|
|
1542
1580
|
kind: row.kind,
|
|
1581
|
+
purpose: row.purpose ?? undefined,
|
|
1582
|
+
purposeData: row.purposeData ?? undefined,
|
|
1543
1583
|
ccSessionId: row.ccSessionId ?? undefined,
|
|
1544
1584
|
transcriptPath: row.transcriptPath ?? undefined,
|
|
1545
1585
|
status: row.status,
|
|
@@ -1795,7 +1835,6 @@ var init_waypoints = __esm(() => {
|
|
|
1795
1835
|
});
|
|
1796
1836
|
|
|
1797
1837
|
// src/pty/dev-pane.ts
|
|
1798
|
-
import { execFileSync } from "child_process";
|
|
1799
1838
|
function drivePaneId(ownerId) {
|
|
1800
1839
|
return `${DRIVE_PREFIX}${ownerId}`;
|
|
1801
1840
|
}
|
|
@@ -1855,24 +1894,24 @@ function devPaneLive(paneId) {
|
|
|
1855
1894
|
const entry = ptyRegistry().get(paneId);
|
|
1856
1895
|
return !!entry && !entry.exited;
|
|
1857
1896
|
}
|
|
1858
|
-
function
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1897
|
+
function killTreeBounded(pty) {
|
|
1898
|
+
return new Promise((resolve4) => {
|
|
1899
|
+
const timer = setTimeout(resolve4, KILL_TREE_TIMEOUT_MS);
|
|
1900
|
+
pty.killTree().catch(() => {}).then(() => {
|
|
1901
|
+
clearTimeout(timer);
|
|
1902
|
+
resolve4();
|
|
1903
|
+
});
|
|
1904
|
+
});
|
|
1866
1905
|
}
|
|
1867
|
-
function stopDevPane(paneId) {
|
|
1906
|
+
async function stopDevPane(paneId) {
|
|
1868
1907
|
const reg = ptyRegistry();
|
|
1869
1908
|
const entry = reg.get(paneId);
|
|
1870
1909
|
if (entry && !entry.exited)
|
|
1871
|
-
|
|
1910
|
+
await killTreeBounded(entry.pty);
|
|
1872
1911
|
reg.kill(paneId);
|
|
1873
1912
|
reg.remove(paneId);
|
|
1874
1913
|
}
|
|
1875
|
-
var DRIVE_PREFIX = "drive:", SNIFF_WINDOW = 8192;
|
|
1914
|
+
var DRIVE_PREFIX = "drive:", SNIFF_WINDOW = 8192, KILL_TREE_TIMEOUT_MS = 5000;
|
|
1876
1915
|
var init_dev_pane = __esm(() => {
|
|
1877
1916
|
init_events();
|
|
1878
1917
|
init_registry();
|
|
@@ -2801,7 +2840,7 @@ async function testDrive(ctx, project, feature, action) {
|
|
|
2801
2840
|
const detachedWorktree2 = testDriveState.detachedWorktree;
|
|
2802
2841
|
const devPaneId = testDriveState.devPaneId;
|
|
2803
2842
|
if (devPaneId)
|
|
2804
|
-
stopDevPane(devPaneId);
|
|
2843
|
+
await stopDevPane(devPaneId);
|
|
2805
2844
|
const teardown = await runDriveHookStep(ctx, scope, project.repoPath, "teardown", project.driveStopCommand, driveEnvFor(ctx, project, { slug: feature.slug, branch }, scope).env);
|
|
2806
2845
|
const carriedChanges = await dirtyPaths(g);
|
|
2807
2846
|
await g.checkout(previousBranch2);
|
|
@@ -2941,7 +2980,7 @@ async function startDryRun(ctx, project) {
|
|
|
2941
2980
|
async function stopDryRun(ctx, project, state) {
|
|
2942
2981
|
const scope = { projectId: project.id };
|
|
2943
2982
|
if (state.devPaneId)
|
|
2944
|
-
stopDevPane(state.devPaneId);
|
|
2983
|
+
await stopDevPane(state.devPaneId);
|
|
2945
2984
|
const teardown = await runDriveHookStep(ctx, scope, project.repoPath, "teardown", project.driveStopCommand, state.env);
|
|
2946
2985
|
state.observed.teardownOk = teardown?.result.ok;
|
|
2947
2986
|
testDriveState = undefined;
|
|
@@ -3156,10 +3195,24 @@ async function conflictedFiles(g) {
|
|
|
3156
3195
|
return [];
|
|
3157
3196
|
}
|
|
3158
3197
|
}
|
|
3198
|
+
async function mergeInProgressAt(worktreePath) {
|
|
3199
|
+
try {
|
|
3200
|
+
return await mergeInProgress(git(worktreePath));
|
|
3201
|
+
} catch {
|
|
3202
|
+
return false;
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
async function isAncestor(repoPath, ancestor, descendant) {
|
|
3206
|
+
try {
|
|
3207
|
+
const out = await git(repoPath).raw(["rev-list", "--count", ancestor, `^${descendant}`]);
|
|
3208
|
+
return out.trim() === "0";
|
|
3209
|
+
} catch {
|
|
3210
|
+
return false;
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3159
3213
|
async function mergeInProgress(g) {
|
|
3160
3214
|
try {
|
|
3161
|
-
await g.raw(["rev-parse", "--verify", "--quiet", "MERGE_HEAD"]);
|
|
3162
|
-
return true;
|
|
3215
|
+
return (await g.raw(["rev-parse", "--verify", "--quiet", "MERGE_HEAD"])).trim() !== "";
|
|
3163
3216
|
} catch {
|
|
3164
3217
|
return false;
|
|
3165
3218
|
}
|
|
@@ -3189,6 +3242,8 @@ function createSessionRow(ctx, input) {
|
|
|
3189
3242
|
projectId: input.projectId ?? null,
|
|
3190
3243
|
lap: input.featureId ? getFeatureRow(ctx, input.featureId).lap : 1,
|
|
3191
3244
|
kind: input.kind,
|
|
3245
|
+
purpose: input.purpose ?? null,
|
|
3246
|
+
purposeData: input.purposeData ?? null,
|
|
3192
3247
|
ccSessionId: null,
|
|
3193
3248
|
transcriptPath: null,
|
|
3194
3249
|
status: "launching",
|
|
@@ -3436,7 +3491,7 @@ function landProjectSession(ctx, session) {
|
|
|
3436
3491
|
data: { sessionId: session.id, branch: PROJECT_BRANCH, ...data }
|
|
3437
3492
|
});
|
|
3438
3493
|
};
|
|
3439
|
-
landProjectBranch(project).then((res) => {
|
|
3494
|
+
const landing = landProjectBranch(project).then((res) => {
|
|
3440
3495
|
if (!res)
|
|
3441
3496
|
return;
|
|
3442
3497
|
if (res.landed) {
|
|
@@ -3449,6 +3504,8 @@ function landProjectSession(ctx, session) {
|
|
|
3449
3504
|
}).catch((e) => {
|
|
3450
3505
|
report("project.land_failed", `could not land the project session's work onto ${project.mainBranch}: ${e instanceof Error ? e.message : String(e)} \u2014 ${kept}`, { error: e instanceof Error ? e.message : String(e) });
|
|
3451
3506
|
});
|
|
3507
|
+
inFlightLandings.add(landing);
|
|
3508
|
+
landing.finally(() => inFlightLandings.delete(landing));
|
|
3452
3509
|
}
|
|
3453
3510
|
function markSessionEnded(ctx, id) {
|
|
3454
3511
|
const existing = getSessionRow(ctx, id);
|
|
@@ -3477,7 +3534,7 @@ function mostRecentLiveSession(ctx) {
|
|
|
3477
3534
|
const row = ctx.db.select().from(sessions).where(eq5(sessions.status, "live")).orderBy(desc3(sql2`rowid`)).limit(1).get();
|
|
3478
3535
|
return row ? rowToSession(row) : null;
|
|
3479
3536
|
}
|
|
3480
|
-
var KICKOFF_DELAY_MS = 1500, KICKOFF_SUBMIT_DELAY_MS = 350, KICKOFF_CONFIRM_MS = 12000, KICKOFF_MAX_ATTEMPTS = 3, CLEAR_INPUT = "\x15", SESSION_READY_TIMEOUT_MS = 25000, CONVERGE_KICKOFF_LINE, KICKOFF_LINES, RESUME_KICKOFF_PREFIX, pendingKickoffOverrides, deliveries, MATCH_PREFIX = 40, landedProjectSessions;
|
|
3537
|
+
var KICKOFF_DELAY_MS = 1500, KICKOFF_SUBMIT_DELAY_MS = 350, KICKOFF_CONFIRM_MS = 12000, KICKOFF_MAX_ATTEMPTS = 3, CLEAR_INPUT = "\x15", SESSION_READY_TIMEOUT_MS = 25000, CONVERGE_KICKOFF_LINE, KICKOFF_LINES, RESUME_KICKOFF_PREFIX, pendingKickoffOverrides, deliveries, MATCH_PREFIX = 40, landedProjectSessions, inFlightLandings;
|
|
3481
3538
|
var init_sessions = __esm(() => {
|
|
3482
3539
|
init_src();
|
|
3483
3540
|
init_schema();
|
|
@@ -3500,6 +3557,7 @@ var init_sessions = __esm(() => {
|
|
|
3500
3557
|
pendingKickoffOverrides = new Map;
|
|
3501
3558
|
deliveries = new Map;
|
|
3502
3559
|
landedProjectSessions = new Set;
|
|
3560
|
+
inFlightLandings = new Set;
|
|
3503
3561
|
});
|
|
3504
3562
|
|
|
3505
3563
|
// src/launcher/reconcile.ts
|
|
@@ -6630,6 +6688,8 @@ function guardsEdits(kind) {
|
|
|
6630
6688
|
function evaluateEditGuard(input) {
|
|
6631
6689
|
if (!guardsEdits(input.kind))
|
|
6632
6690
|
return null;
|
|
6691
|
+
if (input.purpose === "resolve-conflict" && input.mergeInProgress)
|
|
6692
|
+
return null;
|
|
6633
6693
|
if (!input.toolName || !EDIT_TOOLS.includes(input.toolName))
|
|
6634
6694
|
return null;
|
|
6635
6695
|
if (!input.filePath)
|
|
@@ -6806,22 +6866,40 @@ function handleUserPrompt(ctx, sessionId, feature, payload) {
|
|
|
6806
6866
|
}
|
|
6807
6867
|
};
|
|
6808
6868
|
}
|
|
6809
|
-
function handleSessionEnd(ctx,
|
|
6810
|
-
markSessionEnded(ctx,
|
|
6811
|
-
releaseForSession(ctx,
|
|
6869
|
+
async function handleSessionEnd(ctx, session, feature) {
|
|
6870
|
+
markSessionEnded(ctx, session.id);
|
|
6871
|
+
releaseForSession(ctx, session.id);
|
|
6812
6872
|
emit(ctx, feature.id, {
|
|
6813
6873
|
type: "session.ended",
|
|
6814
6874
|
message: "session ended",
|
|
6815
|
-
data: { sessionId }
|
|
6875
|
+
data: { sessionId: session.id }
|
|
6816
6876
|
});
|
|
6877
|
+
await noteResolvedMerge(ctx, session, feature);
|
|
6817
6878
|
return {};
|
|
6818
6879
|
}
|
|
6819
|
-
function
|
|
6880
|
+
async function noteResolvedMerge(ctx, session, feature) {
|
|
6881
|
+
const pair = session.purpose === "resolve-conflict" ? session.purposeData : undefined;
|
|
6882
|
+
if (!pair)
|
|
6883
|
+
return;
|
|
6884
|
+
try {
|
|
6885
|
+
if (!await isAncestor(session.worktreePath, pair.mergeFrom, pair.mergeInto))
|
|
6886
|
+
return;
|
|
6887
|
+
emit(ctx, feature.id, {
|
|
6888
|
+
type: "merge.resolved",
|
|
6889
|
+
message: `merge conflict resolved \u2014 ${pair.mergeFrom} is in ${pair.mergeInto}`,
|
|
6890
|
+
data: { sessionId: session.id, ...pair }
|
|
6891
|
+
});
|
|
6892
|
+
} catch {}
|
|
6893
|
+
}
|
|
6894
|
+
async function handlePreToolUse(session, feature, payload) {
|
|
6820
6895
|
const toolName = typeof payload?.tool_name === "string" ? payload.tool_name : undefined;
|
|
6821
6896
|
const toolInput = payload?.tool_input ?? {};
|
|
6822
6897
|
const path = typeof toolInput.file_path === "string" ? toolInput.file_path : typeof toolInput.notebook_path === "string" ? toolInput.notebook_path : undefined;
|
|
6898
|
+
const mergeInProgress2 = session.purpose === "resolve-conflict" ? await mergeInProgressAt(session.worktreePath) : false;
|
|
6823
6899
|
const denial = evaluateEditGuard({
|
|
6824
6900
|
kind: session.kind,
|
|
6901
|
+
purpose: session.purpose,
|
|
6902
|
+
mergeInProgress: mergeInProgress2,
|
|
6825
6903
|
worktreePath: session.worktreePath,
|
|
6826
6904
|
toolName,
|
|
6827
6905
|
filePath: path,
|
|
@@ -6850,6 +6928,7 @@ var init_hooks = __esm(() => {
|
|
|
6850
6928
|
init_edit_guard();
|
|
6851
6929
|
init_sessions();
|
|
6852
6930
|
init_events();
|
|
6931
|
+
init_git();
|
|
6853
6932
|
init_prep();
|
|
6854
6933
|
init_repo();
|
|
6855
6934
|
init_tickets();
|
|
@@ -6887,7 +6966,7 @@ var init_hooks = __esm(() => {
|
|
|
6887
6966
|
case "session-end":
|
|
6888
6967
|
return c.json(handleProjectScopedSessionEnd(ctx, session));
|
|
6889
6968
|
case "pre-tool":
|
|
6890
|
-
return c.json(handlePreToolUse(session, undefined, body.payload));
|
|
6969
|
+
return c.json(await handlePreToolUse(session, undefined, body.payload));
|
|
6891
6970
|
default:
|
|
6892
6971
|
return c.json({});
|
|
6893
6972
|
}
|
|
@@ -6901,9 +6980,9 @@ var init_hooks = __esm(() => {
|
|
|
6901
6980
|
case "user-prompt":
|
|
6902
6981
|
return c.json(handleUserPrompt(ctx, sessionId, feature, body.payload));
|
|
6903
6982
|
case "session-end":
|
|
6904
|
-
return c.json(handleSessionEnd(ctx,
|
|
6983
|
+
return c.json(await handleSessionEnd(ctx, session, feature));
|
|
6905
6984
|
case "pre-tool":
|
|
6906
|
-
return c.json(handlePreToolUse(session, feature, body.payload));
|
|
6985
|
+
return c.json(await handlePreToolUse(session, feature, body.payload));
|
|
6907
6986
|
default:
|
|
6908
6987
|
return c.json({});
|
|
6909
6988
|
}
|
|
@@ -7045,6 +7124,166 @@ var init_web = __esm(() => {
|
|
|
7045
7124
|
};
|
|
7046
7125
|
});
|
|
7047
7126
|
|
|
7127
|
+
// src/version.ts
|
|
7128
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
7129
|
+
import { dirname as dirname5, join as join12 } from "path";
|
|
7130
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
7131
|
+
function runcastleVersion() {
|
|
7132
|
+
if (cached2)
|
|
7133
|
+
return cached2;
|
|
7134
|
+
const here = dirname5(fileURLToPath4(import.meta.url));
|
|
7135
|
+
for (const pkgPath of [join12(here, "..", "package.json"), join12(here, "package.json")]) {
|
|
7136
|
+
try {
|
|
7137
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
7138
|
+
if (typeof pkg.version === "string") {
|
|
7139
|
+
cached2 = pkg.version;
|
|
7140
|
+
return cached2;
|
|
7141
|
+
}
|
|
7142
|
+
} catch {}
|
|
7143
|
+
}
|
|
7144
|
+
cached2 = UNKNOWN_VERSION;
|
|
7145
|
+
return cached2;
|
|
7146
|
+
}
|
|
7147
|
+
var cached2, UNKNOWN_VERSION = "0.0.0";
|
|
7148
|
+
var init_version = () => {};
|
|
7149
|
+
|
|
7150
|
+
// src/services/install-id.ts
|
|
7151
|
+
import { randomUUID } from "crypto";
|
|
7152
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
|
|
7153
|
+
import { dirname as dirname6 } from "path";
|
|
7154
|
+
function getInstallId() {
|
|
7155
|
+
const file = installIdPath();
|
|
7156
|
+
try {
|
|
7157
|
+
if (existsSync13(file)) {
|
|
7158
|
+
const existing = readFileSync7(file, "utf8").trim();
|
|
7159
|
+
if (UUID_RE.test(existing))
|
|
7160
|
+
return existing;
|
|
7161
|
+
}
|
|
7162
|
+
const id = randomUUID();
|
|
7163
|
+
mkdirSync6(dirname6(file), { recursive: true });
|
|
7164
|
+
writeFileSync2(file, `${id}
|
|
7165
|
+
`);
|
|
7166
|
+
return id;
|
|
7167
|
+
} catch {
|
|
7168
|
+
return randomUUID();
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
var UUID_RE;
|
|
7172
|
+
var init_install_id = __esm(() => {
|
|
7173
|
+
init_paths();
|
|
7174
|
+
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
7175
|
+
});
|
|
7176
|
+
|
|
7177
|
+
// src/services/update-check.ts
|
|
7178
|
+
function parse(version) {
|
|
7179
|
+
const cleaned = version.trim().replace(/^v/, "");
|
|
7180
|
+
const [core = "", pre = ""] = cleaned.split("-", 2);
|
|
7181
|
+
const release2 = core.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
7182
|
+
return { release: release2, pre: pre ? pre.split(".") : [] };
|
|
7183
|
+
}
|
|
7184
|
+
function compareSemver(a, b) {
|
|
7185
|
+
const pa = parse(a);
|
|
7186
|
+
const pb = parse(b);
|
|
7187
|
+
const len = Math.max(pa.release.length, pb.release.length);
|
|
7188
|
+
for (let i = 0;i < len; i++) {
|
|
7189
|
+
const diff = (pa.release[i] ?? 0) - (pb.release[i] ?? 0);
|
|
7190
|
+
if (diff !== 0)
|
|
7191
|
+
return diff;
|
|
7192
|
+
}
|
|
7193
|
+
if (pa.pre.length === 0 && pb.pre.length === 0)
|
|
7194
|
+
return 0;
|
|
7195
|
+
if (pa.pre.length === 0)
|
|
7196
|
+
return 1;
|
|
7197
|
+
if (pb.pre.length === 0)
|
|
7198
|
+
return -1;
|
|
7199
|
+
const preLen = Math.max(pa.pre.length, pb.pre.length);
|
|
7200
|
+
for (let i = 0;i < preLen; i++) {
|
|
7201
|
+
const x = pa.pre[i];
|
|
7202
|
+
const y = pb.pre[i];
|
|
7203
|
+
if (x === undefined)
|
|
7204
|
+
return -1;
|
|
7205
|
+
if (y === undefined)
|
|
7206
|
+
return 1;
|
|
7207
|
+
const nx = Number(x);
|
|
7208
|
+
const ny = Number(y);
|
|
7209
|
+
const bothNumeric = !Number.isNaN(nx) && !Number.isNaN(ny);
|
|
7210
|
+
const cmp = bothNumeric ? nx - ny : x < y ? -1 : x > y ? 1 : 0;
|
|
7211
|
+
if (cmp !== 0)
|
|
7212
|
+
return cmp;
|
|
7213
|
+
}
|
|
7214
|
+
return 0;
|
|
7215
|
+
}
|
|
7216
|
+
function doNotTrack(env) {
|
|
7217
|
+
const raw = env.DO_NOT_TRACK;
|
|
7218
|
+
return raw !== undefined && raw !== "" && raw !== "0";
|
|
7219
|
+
}
|
|
7220
|
+
async function latestFromNpm(fetchImpl) {
|
|
7221
|
+
try {
|
|
7222
|
+
const res = await fetchImpl(NPM_LATEST_URL);
|
|
7223
|
+
if (!res.ok)
|
|
7224
|
+
return null;
|
|
7225
|
+
const body = await res.json();
|
|
7226
|
+
return typeof body.version === "string" ? body.version : null;
|
|
7227
|
+
} catch {
|
|
7228
|
+
return null;
|
|
7229
|
+
}
|
|
7230
|
+
}
|
|
7231
|
+
async function latestFromPing(fetchImpl, current2) {
|
|
7232
|
+
try {
|
|
7233
|
+
const res = await fetchImpl(PING_URL, {
|
|
7234
|
+
method: "POST",
|
|
7235
|
+
headers: { "content-type": "application/json" },
|
|
7236
|
+
body: JSON.stringify({
|
|
7237
|
+
installId: getInstallId(),
|
|
7238
|
+
version: current2,
|
|
7239
|
+
platform: process.platform
|
|
7240
|
+
})
|
|
7241
|
+
});
|
|
7242
|
+
if (!res.ok)
|
|
7243
|
+
return null;
|
|
7244
|
+
const body = await res.json();
|
|
7245
|
+
return typeof body.latest === "string" ? body.latest : null;
|
|
7246
|
+
} catch {
|
|
7247
|
+
return null;
|
|
7248
|
+
}
|
|
7249
|
+
}
|
|
7250
|
+
async function checkForUpdate(opts) {
|
|
7251
|
+
const { current: current2 } = opts;
|
|
7252
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
7253
|
+
const env = opts.env ?? process.env;
|
|
7254
|
+
if (current2 === UNKNOWN_VERSION)
|
|
7255
|
+
return NO_UPDATE(current2);
|
|
7256
|
+
const latest = doNotTrack(env) ? await latestFromNpm(fetchImpl) : await latestFromPing(fetchImpl, current2) ?? await latestFromNpm(fetchImpl);
|
|
7257
|
+
if (!latest)
|
|
7258
|
+
return NO_UPDATE(current2);
|
|
7259
|
+
return {
|
|
7260
|
+
current: current2,
|
|
7261
|
+
latest,
|
|
7262
|
+
updateAvailable: compareSemver(latest, current2) > 0,
|
|
7263
|
+
command: UPDATE_COMMAND
|
|
7264
|
+
};
|
|
7265
|
+
}
|
|
7266
|
+
async function getUpdateInfo(current2, fetchImpl) {
|
|
7267
|
+
if (cache)
|
|
7268
|
+
return cache;
|
|
7269
|
+
const info = await checkForUpdate({ current: current2, fetchImpl });
|
|
7270
|
+
if (info.latest !== null)
|
|
7271
|
+
cache = info;
|
|
7272
|
+
return info;
|
|
7273
|
+
}
|
|
7274
|
+
var PACKAGE_NAME = "runcastle", UPDATE_COMMAND, PING_URL = "https://ping.runcastle.dev/ping", NPM_LATEST_URL, NO_UPDATE = (current2) => ({
|
|
7275
|
+
current: current2,
|
|
7276
|
+
latest: null,
|
|
7277
|
+
updateAvailable: false,
|
|
7278
|
+
command: UPDATE_COMMAND
|
|
7279
|
+
}), cache;
|
|
7280
|
+
var init_update_check = __esm(() => {
|
|
7281
|
+
init_version();
|
|
7282
|
+
init_install_id();
|
|
7283
|
+
UPDATE_COMMAND = `bun add -g ${PACKAGE_NAME}@latest`;
|
|
7284
|
+
NPM_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
7285
|
+
});
|
|
7286
|
+
|
|
7048
7287
|
// src/trpc/context.ts
|
|
7049
7288
|
import { initTRPC } from "@trpc/server";
|
|
7050
7289
|
var t, errorMapping, router, publicProcedure, createCallerFactory;
|
|
@@ -7093,11 +7332,11 @@ var init_events2 = __esm(() => {
|
|
|
7093
7332
|
});
|
|
7094
7333
|
|
|
7095
7334
|
// src/launcher/artifacts.ts
|
|
7096
|
-
import { mkdirSync as
|
|
7097
|
-
import { fileURLToPath as
|
|
7098
|
-
import { join as
|
|
7335
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
7336
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7337
|
+
import { join as join13 } from "path";
|
|
7099
7338
|
function hookClientPath() {
|
|
7100
|
-
return resolveAsset(ASSET_ENV.hookClient,
|
|
7339
|
+
return resolveAsset(ASSET_ENV.hookClient, fileURLToPath5(new URL("./hook-client.ts", import.meta.url)));
|
|
7101
7340
|
}
|
|
7102
7341
|
function serverUrlFor(config2) {
|
|
7103
7342
|
return `http://localhost:${config2.serverPort}`;
|
|
@@ -7519,16 +7758,16 @@ function renderMcpConfig(session, config2) {
|
|
|
7519
7758
|
async function writeSessionArtifacts(input) {
|
|
7520
7759
|
const { session, feature, config: config2, waypoint, prepare, projectBrief, lap } = input;
|
|
7521
7760
|
const dir = sessionDir(session.id);
|
|
7522
|
-
|
|
7523
|
-
const systemPromptPath =
|
|
7524
|
-
const settingsPath =
|
|
7525
|
-
const mcpConfigPath =
|
|
7761
|
+
mkdirSync7(dir, { recursive: true });
|
|
7762
|
+
const systemPromptPath = join13(dir, "system-prompt.md");
|
|
7763
|
+
const settingsPath = join13(dir, "settings.json");
|
|
7764
|
+
const mcpConfigPath = join13(dir, "mcp.json");
|
|
7526
7765
|
const systemPrompt = feature ? renderSystemPrompt(feature, session.kind, waypoint, lap) : prepare ? renderPreparePrompt(prepare) : projectBrief ? renderProjectPrompt(projectBrief) : (() => {
|
|
7527
7766
|
throw new Error(`session ${session.id} has no feature and no project-session brief`);
|
|
7528
7767
|
})();
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7768
|
+
writeFileSync3(systemPromptPath, systemPrompt, "utf8");
|
|
7769
|
+
writeFileSync3(settingsPath, JSON.stringify(renderSettings(hookClientPath(), session.kind), null, 2), "utf8");
|
|
7770
|
+
writeFileSync3(mcpConfigPath, JSON.stringify(renderMcpConfig(session, config2), null, 2), "utf8");
|
|
7532
7771
|
return { systemPromptPath, settingsPath, mcpConfigPath };
|
|
7533
7772
|
}
|
|
7534
7773
|
var RUNCASTLE_MCP_ALLOW_RULES, SESSION_BASH_READ_RULES, SESSION_BASH_WRITE_RULES, SESSION_BASH_ALLOW_RULES, SESSION_START_SOURCES;
|
|
@@ -7572,9 +7811,9 @@ var init_artifacts = __esm(() => {
|
|
|
7572
7811
|
});
|
|
7573
7812
|
|
|
7574
7813
|
// src/launcher/launcher.ts
|
|
7575
|
-
import { existsSync as
|
|
7576
|
-
import { dirname as
|
|
7577
|
-
import { fileURLToPath as
|
|
7814
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7815
|
+
import { dirname as dirname7, join as join14, resolve as resolve9 } from "path";
|
|
7816
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
7578
7817
|
import { and as and9, eq as eq11 } from "drizzle-orm";
|
|
7579
7818
|
function ptyExitMessage(exitCode) {
|
|
7580
7819
|
const label = typeof exitCode === "number" ? String(exitCode) : "unknown";
|
|
@@ -7606,23 +7845,23 @@ function resolveClaudeExecutable() {
|
|
|
7606
7845
|
function claudeSpawnTarget(claudeArgs) {
|
|
7607
7846
|
return spawnTargetFor(resolveClaudeExecutable(), claudeArgs);
|
|
7608
7847
|
}
|
|
7609
|
-
function resolvePluginDir(fromDir =
|
|
7610
|
-
const rel =
|
|
7848
|
+
function resolvePluginDir(fromDir = dirname7(fileURLToPath6(import.meta.url))) {
|
|
7849
|
+
const rel = join14("packages", "skills", "packs", "runcastle");
|
|
7611
7850
|
const override = process.env[SKILLS_DIR_ENV];
|
|
7612
7851
|
if (override) {
|
|
7613
|
-
const dir2 =
|
|
7614
|
-
if (
|
|
7852
|
+
const dir2 = join14(resolve9(override), "packs", "runcastle");
|
|
7853
|
+
if (existsSync14(dir2))
|
|
7615
7854
|
return dir2;
|
|
7616
7855
|
throw new Error(`${SKILLS_DIR_ENV}=${override} has no plugin dir at ${dir2}`);
|
|
7617
7856
|
}
|
|
7618
7857
|
const searched = [];
|
|
7619
7858
|
let dir = fromDir;
|
|
7620
7859
|
for (let i = 0;i < 8; i += 1) {
|
|
7621
|
-
const candidate =
|
|
7860
|
+
const candidate = join14(dir, rel);
|
|
7622
7861
|
searched.push(candidate);
|
|
7623
|
-
if (
|
|
7862
|
+
if (existsSync14(candidate))
|
|
7624
7863
|
return candidate;
|
|
7625
|
-
const parent =
|
|
7864
|
+
const parent = dirname7(dir);
|
|
7626
7865
|
if (parent === dir)
|
|
7627
7866
|
break;
|
|
7628
7867
|
dir = parent;
|
|
@@ -7691,6 +7930,8 @@ async function launchSession(ctx, input, opts = {}) {
|
|
|
7691
7930
|
const session = createSessionRow(ctx, {
|
|
7692
7931
|
featureId: feature.id,
|
|
7693
7932
|
kind: input.kind,
|
|
7933
|
+
purpose: input.purpose,
|
|
7934
|
+
purposeData: input.purposeData,
|
|
7694
7935
|
worktreePath
|
|
7695
7936
|
});
|
|
7696
7937
|
const plan = planKickoff({ kind: input.kind, lap: feature.lap, kickoffLine: input.kickoffLine });
|
|
@@ -8138,7 +8379,9 @@ var init_feature = __esm(() => {
|
|
|
8138
8379
|
launchSession: publicProcedure.input(z6.object({
|
|
8139
8380
|
featureId: z6.string(),
|
|
8140
8381
|
kind: SessionKind,
|
|
8141
|
-
kickoffLine: z6.string().min(1).optional()
|
|
8382
|
+
kickoffLine: z6.string().min(1).optional(),
|
|
8383
|
+
purpose: SessionPurpose.optional(),
|
|
8384
|
+
purposeData: MergeBranchPair.optional()
|
|
8142
8385
|
})).mutation(({ ctx, input }) => launchSession(ctx, input)),
|
|
8143
8386
|
workWaypoint: publicProcedure.input(z6.object({ featureId: z6.string(), waypointId: z6.string(), endLive: z6.boolean().optional() })).mutation(({ ctx, input }) => workWaypoint(ctx, input)),
|
|
8144
8387
|
converge: publicProcedure.input(z6.object({ featureId: z6.string(), overrideReason: z6.string().min(1).optional() })).mutation(({ ctx, input }) => converge(ctx, { featureId: input.featureId, overrideReason: input.overrideReason })),
|
|
@@ -8189,9 +8432,9 @@ var init_feature = __esm(() => {
|
|
|
8189
8432
|
});
|
|
8190
8433
|
|
|
8191
8434
|
// src/services/fsbrowse.ts
|
|
8192
|
-
import { existsSync as
|
|
8435
|
+
import { existsSync as existsSync15, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
8193
8436
|
import { homedir as homedir2 } from "os";
|
|
8194
|
-
import { basename, dirname as
|
|
8437
|
+
import { basename, dirname as dirname8, isAbsolute as isAbsolute2, join as join15, resolve as resolve10, sep as sep3 } from "path";
|
|
8195
8438
|
function expandPath(input, home = homedir2()) {
|
|
8196
8439
|
const trimmed = input.trim();
|
|
8197
8440
|
if (trimmed === "")
|
|
@@ -8200,12 +8443,12 @@ function expandPath(input, home = homedir2()) {
|
|
|
8200
8443
|
if (out === "~")
|
|
8201
8444
|
out = home;
|
|
8202
8445
|
else if (out.startsWith("~/") || out.startsWith("~\\"))
|
|
8203
|
-
out =
|
|
8446
|
+
out = join15(home, out.slice(2));
|
|
8204
8447
|
return resolve10(out);
|
|
8205
8448
|
}
|
|
8206
8449
|
function looksLikeRepo(dir) {
|
|
8207
8450
|
try {
|
|
8208
|
-
return
|
|
8451
|
+
return existsSync15(join15(dir, ".git"));
|
|
8209
8452
|
} catch {
|
|
8210
8453
|
return false;
|
|
8211
8454
|
}
|
|
@@ -8216,20 +8459,20 @@ function listRoots(home = homedir2()) {
|
|
|
8216
8459
|
for (let code = 67;code <= 90; code++) {
|
|
8217
8460
|
const drive = `${String.fromCharCode(code)}:${sep3}`;
|
|
8218
8461
|
try {
|
|
8219
|
-
if (
|
|
8462
|
+
if (existsSync15(drive))
|
|
8220
8463
|
roots.push({ label: drive.replace(/\\$/, ""), path: drive, kind: "drive" });
|
|
8221
8464
|
} catch {}
|
|
8222
8465
|
}
|
|
8223
8466
|
} else {
|
|
8224
8467
|
roots.push({ label: "/", path: "/", kind: "drive" });
|
|
8225
8468
|
}
|
|
8226
|
-
if (
|
|
8469
|
+
if (existsSync15(home))
|
|
8227
8470
|
roots.push({ label: "Home", path: home, kind: "home" });
|
|
8228
8471
|
const seen = new Set(roots.map(pathKey));
|
|
8229
8472
|
for (const name of COMMON_DIRS) {
|
|
8230
|
-
const candidate =
|
|
8473
|
+
const candidate = join15(home, name);
|
|
8231
8474
|
const key = pathKey({ path: candidate });
|
|
8232
|
-
if (seen.has(key) || !
|
|
8475
|
+
if (seen.has(key) || !existsSync15(candidate))
|
|
8233
8476
|
continue;
|
|
8234
8477
|
seen.add(key);
|
|
8235
8478
|
roots.push({ label: name, path: candidate, kind: "common" });
|
|
@@ -8244,7 +8487,7 @@ function crumbsFor(dir) {
|
|
|
8244
8487
|
let cur = dir;
|
|
8245
8488
|
for (;; ) {
|
|
8246
8489
|
out.unshift({ name: basename(cur) || cur, path: cur });
|
|
8247
|
-
const parent =
|
|
8490
|
+
const parent = dirname8(cur);
|
|
8248
8491
|
if (parent === cur)
|
|
8249
8492
|
break;
|
|
8250
8493
|
cur = parent;
|
|
@@ -8252,7 +8495,7 @@ function crumbsFor(dir) {
|
|
|
8252
8495
|
return out;
|
|
8253
8496
|
}
|
|
8254
8497
|
function parentOf(dir) {
|
|
8255
|
-
const parent =
|
|
8498
|
+
const parent = dirname8(dir);
|
|
8256
8499
|
return parent === dir ? null : parent;
|
|
8257
8500
|
}
|
|
8258
8501
|
function browseDir(input, showHidden = false, home = homedir2()) {
|
|
@@ -8260,13 +8503,13 @@ function browseDir(input, showHidden = false, home = homedir2()) {
|
|
|
8260
8503
|
if (!isAbsolute2(target)) {
|
|
8261
8504
|
throw new InvalidInputError(`path is not absolute: ${target}`);
|
|
8262
8505
|
}
|
|
8263
|
-
if (!
|
|
8506
|
+
if (!existsSync15(target)) {
|
|
8264
8507
|
throw new InvalidInputError(`path does not exist: ${target}`);
|
|
8265
8508
|
}
|
|
8266
8509
|
let dir = target;
|
|
8267
8510
|
try {
|
|
8268
8511
|
if (!statSync2(target).isDirectory())
|
|
8269
|
-
dir =
|
|
8512
|
+
dir = dirname8(target);
|
|
8270
8513
|
} catch (e) {
|
|
8271
8514
|
throw new InvalidInputError(`cannot read path: ${target} (${errMsg4(e)})`);
|
|
8272
8515
|
}
|
|
@@ -8288,7 +8531,7 @@ function browseDir(input, showHidden = false, home = homedir2()) {
|
|
|
8288
8531
|
continue;
|
|
8289
8532
|
if (!showHidden && name.startsWith("."))
|
|
8290
8533
|
continue;
|
|
8291
|
-
const path =
|
|
8534
|
+
const path = join15(dir, name);
|
|
8292
8535
|
const isSymlink = dirent.isSymbolicLink();
|
|
8293
8536
|
if (isSymlink && !showHidden)
|
|
8294
8537
|
continue;
|
|
@@ -8325,8 +8568,8 @@ var init_fsbrowse = __esm(() => {
|
|
|
8325
8568
|
});
|
|
8326
8569
|
|
|
8327
8570
|
// src/services/projects.ts
|
|
8328
|
-
import { existsSync as
|
|
8329
|
-
import { basename as basename2, join as
|
|
8571
|
+
import { existsSync as existsSync16 } from "fs";
|
|
8572
|
+
import { basename as basename2, join as join16 } from "path";
|
|
8330
8573
|
import { eq as eq12, isNull } from "drizzle-orm";
|
|
8331
8574
|
function listProjects(ctx) {
|
|
8332
8575
|
return ctx.db.select().from(projects).where(isNull(projects.closedAt)).all().map(rowToProject);
|
|
@@ -8407,7 +8650,7 @@ async function assertRepoTolerant(ctx, repoPath) {
|
|
|
8407
8650
|
} catch (e) {
|
|
8408
8651
|
if (!isNotImplemented(e))
|
|
8409
8652
|
throw e;
|
|
8410
|
-
if (!
|
|
8653
|
+
if (!existsSync16(join16(repoPath, ".git"))) {
|
|
8411
8654
|
throw new InvalidInputError(`not a git repository: ${repoPath}`);
|
|
8412
8655
|
}
|
|
8413
8656
|
}
|
|
@@ -8480,15 +8723,15 @@ var init_run = __esm(() => {
|
|
|
8480
8723
|
});
|
|
8481
8724
|
|
|
8482
8725
|
// src/services/settings.ts
|
|
8483
|
-
import { existsSync as
|
|
8484
|
-
import { dirname as
|
|
8726
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
8727
|
+
import { dirname as dirname9 } from "path";
|
|
8485
8728
|
import * as z9 from "zod";
|
|
8486
8729
|
import { eq as eq13 } from "drizzle-orm";
|
|
8487
8730
|
function readRawConfig(configFile) {
|
|
8488
|
-
if (!
|
|
8731
|
+
if (!existsSync17(configFile))
|
|
8489
8732
|
return {};
|
|
8490
8733
|
try {
|
|
8491
|
-
const parsed = JSON.parse(
|
|
8734
|
+
const parsed = JSON.parse(readFileSync8(configFile, "utf8"));
|
|
8492
8735
|
if (typeof parsed !== "object" || parsed === null)
|
|
8493
8736
|
return {};
|
|
8494
8737
|
return foldLegacyModelConfig(parsed);
|
|
@@ -8627,8 +8870,8 @@ function updateSettings(ctx, input, io = {}) {
|
|
|
8627
8870
|
function writeGlobal(configFile, configKey, value) {
|
|
8628
8871
|
const raw = readRawConfig(configFile);
|
|
8629
8872
|
raw[configKey] = value;
|
|
8630
|
-
|
|
8631
|
-
|
|
8873
|
+
mkdirSync8(dirname9(configFile), { recursive: true });
|
|
8874
|
+
writeFileSync4(configFile, `${JSON.stringify(raw, null, 2)}
|
|
8632
8875
|
`);
|
|
8633
8876
|
}
|
|
8634
8877
|
function updateStepModel(ctx, input, configFile, io) {
|
|
@@ -8666,8 +8909,8 @@ function writeStepModel(configFile, step, value) {
|
|
|
8666
8909
|
else
|
|
8667
8910
|
stepModels[step] = value;
|
|
8668
8911
|
raw.stepModels = stepModels;
|
|
8669
|
-
|
|
8670
|
-
|
|
8912
|
+
mkdirSync8(dirname9(configFile), { recursive: true });
|
|
8913
|
+
writeFileSync4(configFile, `${JSON.stringify(raw, null, 2)}
|
|
8671
8914
|
`);
|
|
8672
8915
|
}
|
|
8673
8916
|
function field(view, key) {
|
|
@@ -9044,9 +9287,9 @@ var init_system_exec = __esm(() => {
|
|
|
9044
9287
|
});
|
|
9045
9288
|
|
|
9046
9289
|
// src/services/setup.ts
|
|
9047
|
-
import { copyFileSync, existsSync as
|
|
9048
|
-
import { dirname as
|
|
9049
|
-
import { fileURLToPath as
|
|
9290
|
+
import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync9, readdirSync as readdirSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
9291
|
+
import { dirname as dirname10, join as join17 } from "path";
|
|
9292
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
9050
9293
|
async function writeGitIdentity(exec, identity) {
|
|
9051
9294
|
const name = identity.name.trim();
|
|
9052
9295
|
const email = identity.email.trim();
|
|
@@ -9121,16 +9364,16 @@ async function resolveRuntime(exec, preferred) {
|
|
|
9121
9364
|
function resolveSandcastleBin() {
|
|
9122
9365
|
try {
|
|
9123
9366
|
const mainUrl = import.meta.resolve("@ai-hero/sandcastle");
|
|
9124
|
-
let dir =
|
|
9367
|
+
let dir = dirname10(fileURLToPath7(mainUrl));
|
|
9125
9368
|
for (let hops = 0;hops < 6; hops++) {
|
|
9126
|
-
const manifestPath =
|
|
9127
|
-
if (
|
|
9128
|
-
const manifest = JSON.parse(
|
|
9369
|
+
const manifestPath = join17(dir, "package.json");
|
|
9370
|
+
if (existsSync18(manifestPath)) {
|
|
9371
|
+
const manifest = JSON.parse(readFileSync9(manifestPath, "utf8"));
|
|
9129
9372
|
if (manifest.name === "@ai-hero/sandcastle" && manifest.bin?.sandcastle) {
|
|
9130
|
-
return
|
|
9373
|
+
return join17(dir, manifest.bin.sandcastle);
|
|
9131
9374
|
}
|
|
9132
9375
|
}
|
|
9133
|
-
const parent =
|
|
9376
|
+
const parent = dirname10(dir);
|
|
9134
9377
|
if (parent === dir)
|
|
9135
9378
|
break;
|
|
9136
9379
|
dir = parent;
|
|
@@ -9152,21 +9395,21 @@ function terminalSpec(kind, opts) {
|
|
|
9152
9395
|
};
|
|
9153
9396
|
}
|
|
9154
9397
|
function sandcastleTemplateDir() {
|
|
9155
|
-
return resolveAsset(ASSET_ENV.sandcastleTemplate,
|
|
9398
|
+
return resolveAsset(ASSET_ENV.sandcastleTemplate, fileURLToPath7(new URL("../assets/sandcastle", import.meta.url)));
|
|
9156
9399
|
}
|
|
9157
9400
|
function scaffoldSandcastleConfig(templateDir, targetDir) {
|
|
9158
|
-
const dir =
|
|
9159
|
-
if (
|
|
9401
|
+
const dir = join17(targetDir, ".sandcastle");
|
|
9402
|
+
if (existsSync18(dir))
|
|
9160
9403
|
return { scaffolded: false, dir };
|
|
9161
|
-
|
|
9404
|
+
mkdirSync9(dir, { recursive: true });
|
|
9162
9405
|
for (const name of readdirSync6(templateDir)) {
|
|
9163
|
-
copyFileSync(
|
|
9406
|
+
copyFileSync(join17(templateDir, name), join17(dir, name));
|
|
9164
9407
|
}
|
|
9165
9408
|
return { scaffolded: true, dir };
|
|
9166
9409
|
}
|
|
9167
9410
|
function prepareSandboxBuildContext() {
|
|
9168
9411
|
const target = sandboxBuildDir();
|
|
9169
|
-
|
|
9412
|
+
mkdirSync9(target, { recursive: true });
|
|
9170
9413
|
scaffoldSandcastleConfig(sandcastleTemplateDir(), target);
|
|
9171
9414
|
return target;
|
|
9172
9415
|
}
|
|
@@ -9202,10 +9445,10 @@ function createTokenVerifier(exec) {
|
|
|
9202
9445
|
function fileAfkTokenIo(exec) {
|
|
9203
9446
|
const path = envPath();
|
|
9204
9447
|
return {
|
|
9205
|
-
read: () =>
|
|
9448
|
+
read: () => existsSync18(path) ? readFileSync9(path, "utf8") : "",
|
|
9206
9449
|
write: (content) => {
|
|
9207
|
-
|
|
9208
|
-
|
|
9450
|
+
mkdirSync9(dirname10(path), { recursive: true });
|
|
9451
|
+
writeFileSync5(path, content, "utf8");
|
|
9209
9452
|
},
|
|
9210
9453
|
verify: createTokenVerifier(exec)
|
|
9211
9454
|
};
|
|
@@ -9258,110 +9501,6 @@ var init_setup2 = __esm(() => {
|
|
|
9258
9501
|
});
|
|
9259
9502
|
});
|
|
9260
9503
|
|
|
9261
|
-
// src/version.ts
|
|
9262
|
-
import { readFileSync as readFileSync8 } from "fs";
|
|
9263
|
-
import { dirname as dirname9, join as join17 } from "path";
|
|
9264
|
-
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
9265
|
-
function runcastleVersion() {
|
|
9266
|
-
if (cached2)
|
|
9267
|
-
return cached2;
|
|
9268
|
-
const here = dirname9(fileURLToPath7(import.meta.url));
|
|
9269
|
-
for (const pkgPath of [join17(here, "..", "package.json"), join17(here, "package.json")]) {
|
|
9270
|
-
try {
|
|
9271
|
-
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
9272
|
-
if (typeof pkg.version === "string") {
|
|
9273
|
-
cached2 = pkg.version;
|
|
9274
|
-
return cached2;
|
|
9275
|
-
}
|
|
9276
|
-
} catch {}
|
|
9277
|
-
}
|
|
9278
|
-
cached2 = UNKNOWN_VERSION;
|
|
9279
|
-
return cached2;
|
|
9280
|
-
}
|
|
9281
|
-
var cached2, UNKNOWN_VERSION = "0.0.0";
|
|
9282
|
-
var init_version = () => {};
|
|
9283
|
-
|
|
9284
|
-
// src/services/update-check.ts
|
|
9285
|
-
function parse(version) {
|
|
9286
|
-
const cleaned = version.trim().replace(/^v/, "");
|
|
9287
|
-
const [core = "", pre = ""] = cleaned.split("-", 2);
|
|
9288
|
-
const release2 = core.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
9289
|
-
return { release: release2, pre: pre ? pre.split(".") : [] };
|
|
9290
|
-
}
|
|
9291
|
-
function compareSemver(a, b) {
|
|
9292
|
-
const pa = parse(a);
|
|
9293
|
-
const pb = parse(b);
|
|
9294
|
-
const len = Math.max(pa.release.length, pb.release.length);
|
|
9295
|
-
for (let i = 0;i < len; i++) {
|
|
9296
|
-
const diff = (pa.release[i] ?? 0) - (pb.release[i] ?? 0);
|
|
9297
|
-
if (diff !== 0)
|
|
9298
|
-
return diff;
|
|
9299
|
-
}
|
|
9300
|
-
if (pa.pre.length === 0 && pb.pre.length === 0)
|
|
9301
|
-
return 0;
|
|
9302
|
-
if (pa.pre.length === 0)
|
|
9303
|
-
return 1;
|
|
9304
|
-
if (pb.pre.length === 0)
|
|
9305
|
-
return -1;
|
|
9306
|
-
const preLen = Math.max(pa.pre.length, pb.pre.length);
|
|
9307
|
-
for (let i = 0;i < preLen; i++) {
|
|
9308
|
-
const x = pa.pre[i];
|
|
9309
|
-
const y = pb.pre[i];
|
|
9310
|
-
if (x === undefined)
|
|
9311
|
-
return -1;
|
|
9312
|
-
if (y === undefined)
|
|
9313
|
-
return 1;
|
|
9314
|
-
const nx = Number(x);
|
|
9315
|
-
const ny = Number(y);
|
|
9316
|
-
const bothNumeric = !Number.isNaN(nx) && !Number.isNaN(ny);
|
|
9317
|
-
const cmp = bothNumeric ? nx - ny : x < y ? -1 : x > y ? 1 : 0;
|
|
9318
|
-
if (cmp !== 0)
|
|
9319
|
-
return cmp;
|
|
9320
|
-
}
|
|
9321
|
-
return 0;
|
|
9322
|
-
}
|
|
9323
|
-
async function checkForUpdate(opts) {
|
|
9324
|
-
const { current: current2 } = opts;
|
|
9325
|
-
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
9326
|
-
if (current2 === UNKNOWN_VERSION)
|
|
9327
|
-
return NO_UPDATE(current2);
|
|
9328
|
-
try {
|
|
9329
|
-
const res = await fetchImpl(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
|
|
9330
|
-
if (!res.ok)
|
|
9331
|
-
return NO_UPDATE(current2);
|
|
9332
|
-
const body = await res.json();
|
|
9333
|
-
const latest = typeof body.version === "string" ? body.version : null;
|
|
9334
|
-
if (!latest)
|
|
9335
|
-
return NO_UPDATE(current2);
|
|
9336
|
-
return {
|
|
9337
|
-
current: current2,
|
|
9338
|
-
latest,
|
|
9339
|
-
updateAvailable: compareSemver(latest, current2) > 0,
|
|
9340
|
-
command: UPDATE_COMMAND
|
|
9341
|
-
};
|
|
9342
|
-
} catch {
|
|
9343
|
-
return NO_UPDATE(current2);
|
|
9344
|
-
}
|
|
9345
|
-
}
|
|
9346
|
-
async function getUpdateInfo(current2, fetchImpl) {
|
|
9347
|
-
if (cache)
|
|
9348
|
-
return cache;
|
|
9349
|
-
const info = await checkForUpdate({ current: current2, fetchImpl });
|
|
9350
|
-
if (info.latest !== null)
|
|
9351
|
-
cache = info;
|
|
9352
|
-
return info;
|
|
9353
|
-
}
|
|
9354
|
-
var PACKAGE_NAME = "runcastle", UPDATE_COMMAND, NO_UPDATE = (current2) => ({
|
|
9355
|
-
current: current2,
|
|
9356
|
-
latest: null,
|
|
9357
|
-
updateAvailable: false,
|
|
9358
|
-
command: UPDATE_COMMAND
|
|
9359
|
-
}), cache;
|
|
9360
|
-
var init_update_check = __esm(() => {
|
|
9361
|
-
init_version();
|
|
9362
|
-
UPDATE_COMMAND = `bun add -g ${PACKAGE_NAME}@latest`;
|
|
9363
|
-
});
|
|
9364
|
-
|
|
9365
9504
|
// src/trpc/routers/system.ts
|
|
9366
9505
|
var systemRouter;
|
|
9367
9506
|
var init_system = __esm(() => {
|
|
@@ -9375,8 +9514,8 @@ var init_system = __esm(() => {
|
|
|
9375
9514
|
});
|
|
9376
9515
|
|
|
9377
9516
|
// src/services/test-notes.ts
|
|
9378
|
-
import { mkdirSync as
|
|
9379
|
-
import { dirname as
|
|
9517
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
9518
|
+
import { dirname as dirname11 } from "path";
|
|
9380
9519
|
import { asc as asc4, eq as eq14 } from "drizzle-orm";
|
|
9381
9520
|
function rowToNote(row) {
|
|
9382
9521
|
return {
|
|
@@ -9521,8 +9660,8 @@ function renderTestNotes(ctx, feature) {
|
|
|
9521
9660
|
lines.push(noteLine(ctx, note));
|
|
9522
9661
|
}
|
|
9523
9662
|
const path = featureDocPath(projectForFeature(ctx, feature), feature, "test-notes.md");
|
|
9524
|
-
|
|
9525
|
-
|
|
9663
|
+
mkdirSync10(dirname11(path), { recursive: true });
|
|
9664
|
+
writeFileSync6(path, `${lines.join(`
|
|
9526
9665
|
`)}
|
|
9527
9666
|
`, "utf8");
|
|
9528
9667
|
}
|
|
@@ -9721,6 +9860,7 @@ async function startServer() {
|
|
|
9721
9860
|
websocket: terminalWebSocket
|
|
9722
9861
|
});
|
|
9723
9862
|
console.log(`runcastle${process.env.RUNCASTLE_DEV ? " [dev]" : ""} server listening on ` + `http://localhost:${config2.serverPort} \u2014 data dir: ${dataDir()}`);
|
|
9863
|
+
getUpdateInfo(runcastleVersion()).catch(() => {});
|
|
9724
9864
|
const SHUTDOWN_KEY = Symbol.for("runcastle.shutdown.wired");
|
|
9725
9865
|
const g = globalThis;
|
|
9726
9866
|
if (!g[SHUTDOWN_KEY]) {
|
|
@@ -9746,7 +9886,9 @@ var init_src2 = __esm(() => {
|
|
|
9746
9886
|
init_hooks();
|
|
9747
9887
|
init_stream();
|
|
9748
9888
|
init_web();
|
|
9889
|
+
init_update_check();
|
|
9749
9890
|
init_router();
|
|
9891
|
+
init_version();
|
|
9750
9892
|
init_reconcile_runs();
|
|
9751
9893
|
});
|
|
9752
9894
|
init_src2();
|