runcastle 1.2.3 → 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 +303 -188
- 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 +298 -183
- package/package.json +1 -1
- package/web/assets/{index-BhOja4Fc.js → index-CzDDtq7w.js} +38 -38
- package/web/index.html +1 -1
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(),
|
|
@@ -1565,6 +1578,8 @@ function rowToSession(row) {
|
|
|
1565
1578
|
featureId: row.featureId ?? undefined,
|
|
1566
1579
|
projectId: row.projectId ?? undefined,
|
|
1567
1580
|
kind: row.kind,
|
|
1581
|
+
purpose: row.purpose ?? undefined,
|
|
1582
|
+
purposeData: row.purposeData ?? undefined,
|
|
1568
1583
|
ccSessionId: row.ccSessionId ?? undefined,
|
|
1569
1584
|
transcriptPath: row.transcriptPath ?? undefined,
|
|
1570
1585
|
status: row.status,
|
|
@@ -3180,10 +3195,24 @@ async function conflictedFiles(g) {
|
|
|
3180
3195
|
return [];
|
|
3181
3196
|
}
|
|
3182
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
|
+
}
|
|
3183
3213
|
async function mergeInProgress(g) {
|
|
3184
3214
|
try {
|
|
3185
|
-
await g.raw(["rev-parse", "--verify", "--quiet", "MERGE_HEAD"]);
|
|
3186
|
-
return true;
|
|
3215
|
+
return (await g.raw(["rev-parse", "--verify", "--quiet", "MERGE_HEAD"])).trim() !== "";
|
|
3187
3216
|
} catch {
|
|
3188
3217
|
return false;
|
|
3189
3218
|
}
|
|
@@ -3213,6 +3242,8 @@ function createSessionRow(ctx, input) {
|
|
|
3213
3242
|
projectId: input.projectId ?? null,
|
|
3214
3243
|
lap: input.featureId ? getFeatureRow(ctx, input.featureId).lap : 1,
|
|
3215
3244
|
kind: input.kind,
|
|
3245
|
+
purpose: input.purpose ?? null,
|
|
3246
|
+
purposeData: input.purposeData ?? null,
|
|
3216
3247
|
ccSessionId: null,
|
|
3217
3248
|
transcriptPath: null,
|
|
3218
3249
|
status: "launching",
|
|
@@ -6657,6 +6688,8 @@ function guardsEdits(kind) {
|
|
|
6657
6688
|
function evaluateEditGuard(input) {
|
|
6658
6689
|
if (!guardsEdits(input.kind))
|
|
6659
6690
|
return null;
|
|
6691
|
+
if (input.purpose === "resolve-conflict" && input.mergeInProgress)
|
|
6692
|
+
return null;
|
|
6660
6693
|
if (!input.toolName || !EDIT_TOOLS.includes(input.toolName))
|
|
6661
6694
|
return null;
|
|
6662
6695
|
if (!input.filePath)
|
|
@@ -6833,22 +6866,40 @@ function handleUserPrompt(ctx, sessionId, feature, payload) {
|
|
|
6833
6866
|
}
|
|
6834
6867
|
};
|
|
6835
6868
|
}
|
|
6836
|
-
function handleSessionEnd(ctx,
|
|
6837
|
-
markSessionEnded(ctx,
|
|
6838
|
-
releaseForSession(ctx,
|
|
6869
|
+
async function handleSessionEnd(ctx, session, feature) {
|
|
6870
|
+
markSessionEnded(ctx, session.id);
|
|
6871
|
+
releaseForSession(ctx, session.id);
|
|
6839
6872
|
emit(ctx, feature.id, {
|
|
6840
6873
|
type: "session.ended",
|
|
6841
6874
|
message: "session ended",
|
|
6842
|
-
data: { sessionId }
|
|
6875
|
+
data: { sessionId: session.id }
|
|
6843
6876
|
});
|
|
6877
|
+
await noteResolvedMerge(ctx, session, feature);
|
|
6844
6878
|
return {};
|
|
6845
6879
|
}
|
|
6846
|
-
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) {
|
|
6847
6895
|
const toolName = typeof payload?.tool_name === "string" ? payload.tool_name : undefined;
|
|
6848
6896
|
const toolInput = payload?.tool_input ?? {};
|
|
6849
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;
|
|
6850
6899
|
const denial = evaluateEditGuard({
|
|
6851
6900
|
kind: session.kind,
|
|
6901
|
+
purpose: session.purpose,
|
|
6902
|
+
mergeInProgress: mergeInProgress2,
|
|
6852
6903
|
worktreePath: session.worktreePath,
|
|
6853
6904
|
toolName,
|
|
6854
6905
|
filePath: path,
|
|
@@ -6877,6 +6928,7 @@ var init_hooks = __esm(() => {
|
|
|
6877
6928
|
init_edit_guard();
|
|
6878
6929
|
init_sessions();
|
|
6879
6930
|
init_events();
|
|
6931
|
+
init_git();
|
|
6880
6932
|
init_prep();
|
|
6881
6933
|
init_repo();
|
|
6882
6934
|
init_tickets();
|
|
@@ -6914,7 +6966,7 @@ var init_hooks = __esm(() => {
|
|
|
6914
6966
|
case "session-end":
|
|
6915
6967
|
return c.json(handleProjectScopedSessionEnd(ctx, session));
|
|
6916
6968
|
case "pre-tool":
|
|
6917
|
-
return c.json(handlePreToolUse(session, undefined, body.payload));
|
|
6969
|
+
return c.json(await handlePreToolUse(session, undefined, body.payload));
|
|
6918
6970
|
default:
|
|
6919
6971
|
return c.json({});
|
|
6920
6972
|
}
|
|
@@ -6928,9 +6980,9 @@ var init_hooks = __esm(() => {
|
|
|
6928
6980
|
case "user-prompt":
|
|
6929
6981
|
return c.json(handleUserPrompt(ctx, sessionId, feature, body.payload));
|
|
6930
6982
|
case "session-end":
|
|
6931
|
-
return c.json(handleSessionEnd(ctx,
|
|
6983
|
+
return c.json(await handleSessionEnd(ctx, session, feature));
|
|
6932
6984
|
case "pre-tool":
|
|
6933
|
-
return c.json(handlePreToolUse(session, feature, body.payload));
|
|
6985
|
+
return c.json(await handlePreToolUse(session, feature, body.payload));
|
|
6934
6986
|
default:
|
|
6935
6987
|
return c.json({});
|
|
6936
6988
|
}
|
|
@@ -7072,6 +7124,166 @@ var init_web = __esm(() => {
|
|
|
7072
7124
|
};
|
|
7073
7125
|
});
|
|
7074
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
|
+
|
|
7075
7287
|
// src/trpc/context.ts
|
|
7076
7288
|
import { initTRPC } from "@trpc/server";
|
|
7077
7289
|
var t, errorMapping, router, publicProcedure, createCallerFactory;
|
|
@@ -7120,11 +7332,11 @@ var init_events2 = __esm(() => {
|
|
|
7120
7332
|
});
|
|
7121
7333
|
|
|
7122
7334
|
// src/launcher/artifacts.ts
|
|
7123
|
-
import { mkdirSync as
|
|
7124
|
-
import { fileURLToPath as
|
|
7125
|
-
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";
|
|
7126
7338
|
function hookClientPath() {
|
|
7127
|
-
return resolveAsset(ASSET_ENV.hookClient,
|
|
7339
|
+
return resolveAsset(ASSET_ENV.hookClient, fileURLToPath5(new URL("./hook-client.ts", import.meta.url)));
|
|
7128
7340
|
}
|
|
7129
7341
|
function serverUrlFor(config2) {
|
|
7130
7342
|
return `http://localhost:${config2.serverPort}`;
|
|
@@ -7546,16 +7758,16 @@ function renderMcpConfig(session, config2) {
|
|
|
7546
7758
|
async function writeSessionArtifacts(input) {
|
|
7547
7759
|
const { session, feature, config: config2, waypoint, prepare, projectBrief, lap } = input;
|
|
7548
7760
|
const dir = sessionDir(session.id);
|
|
7549
|
-
|
|
7550
|
-
const systemPromptPath =
|
|
7551
|
-
const settingsPath =
|
|
7552
|
-
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");
|
|
7553
7765
|
const systemPrompt = feature ? renderSystemPrompt(feature, session.kind, waypoint, lap) : prepare ? renderPreparePrompt(prepare) : projectBrief ? renderProjectPrompt(projectBrief) : (() => {
|
|
7554
7766
|
throw new Error(`session ${session.id} has no feature and no project-session brief`);
|
|
7555
7767
|
})();
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
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");
|
|
7559
7771
|
return { systemPromptPath, settingsPath, mcpConfigPath };
|
|
7560
7772
|
}
|
|
7561
7773
|
var RUNCASTLE_MCP_ALLOW_RULES, SESSION_BASH_READ_RULES, SESSION_BASH_WRITE_RULES, SESSION_BASH_ALLOW_RULES, SESSION_START_SOURCES;
|
|
@@ -7599,9 +7811,9 @@ var init_artifacts = __esm(() => {
|
|
|
7599
7811
|
});
|
|
7600
7812
|
|
|
7601
7813
|
// src/launcher/launcher.ts
|
|
7602
|
-
import { existsSync as
|
|
7603
|
-
import { dirname as
|
|
7604
|
-
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";
|
|
7605
7817
|
import { and as and9, eq as eq11 } from "drizzle-orm";
|
|
7606
7818
|
function ptyExitMessage(exitCode) {
|
|
7607
7819
|
const label = typeof exitCode === "number" ? String(exitCode) : "unknown";
|
|
@@ -7633,23 +7845,23 @@ function resolveClaudeExecutable() {
|
|
|
7633
7845
|
function claudeSpawnTarget(claudeArgs) {
|
|
7634
7846
|
return spawnTargetFor(resolveClaudeExecutable(), claudeArgs);
|
|
7635
7847
|
}
|
|
7636
|
-
function resolvePluginDir(fromDir =
|
|
7637
|
-
const rel =
|
|
7848
|
+
function resolvePluginDir(fromDir = dirname7(fileURLToPath6(import.meta.url))) {
|
|
7849
|
+
const rel = join14("packages", "skills", "packs", "runcastle");
|
|
7638
7850
|
const override = process.env[SKILLS_DIR_ENV];
|
|
7639
7851
|
if (override) {
|
|
7640
|
-
const dir2 =
|
|
7641
|
-
if (
|
|
7852
|
+
const dir2 = join14(resolve9(override), "packs", "runcastle");
|
|
7853
|
+
if (existsSync14(dir2))
|
|
7642
7854
|
return dir2;
|
|
7643
7855
|
throw new Error(`${SKILLS_DIR_ENV}=${override} has no plugin dir at ${dir2}`);
|
|
7644
7856
|
}
|
|
7645
7857
|
const searched = [];
|
|
7646
7858
|
let dir = fromDir;
|
|
7647
7859
|
for (let i = 0;i < 8; i += 1) {
|
|
7648
|
-
const candidate =
|
|
7860
|
+
const candidate = join14(dir, rel);
|
|
7649
7861
|
searched.push(candidate);
|
|
7650
|
-
if (
|
|
7862
|
+
if (existsSync14(candidate))
|
|
7651
7863
|
return candidate;
|
|
7652
|
-
const parent =
|
|
7864
|
+
const parent = dirname7(dir);
|
|
7653
7865
|
if (parent === dir)
|
|
7654
7866
|
break;
|
|
7655
7867
|
dir = parent;
|
|
@@ -7718,6 +7930,8 @@ async function launchSession(ctx, input, opts = {}) {
|
|
|
7718
7930
|
const session = createSessionRow(ctx, {
|
|
7719
7931
|
featureId: feature.id,
|
|
7720
7932
|
kind: input.kind,
|
|
7933
|
+
purpose: input.purpose,
|
|
7934
|
+
purposeData: input.purposeData,
|
|
7721
7935
|
worktreePath
|
|
7722
7936
|
});
|
|
7723
7937
|
const plan = planKickoff({ kind: input.kind, lap: feature.lap, kickoffLine: input.kickoffLine });
|
|
@@ -8165,7 +8379,9 @@ var init_feature = __esm(() => {
|
|
|
8165
8379
|
launchSession: publicProcedure.input(z6.object({
|
|
8166
8380
|
featureId: z6.string(),
|
|
8167
8381
|
kind: SessionKind,
|
|
8168
|
-
kickoffLine: z6.string().min(1).optional()
|
|
8382
|
+
kickoffLine: z6.string().min(1).optional(),
|
|
8383
|
+
purpose: SessionPurpose.optional(),
|
|
8384
|
+
purposeData: MergeBranchPair.optional()
|
|
8169
8385
|
})).mutation(({ ctx, input }) => launchSession(ctx, input)),
|
|
8170
8386
|
workWaypoint: publicProcedure.input(z6.object({ featureId: z6.string(), waypointId: z6.string(), endLive: z6.boolean().optional() })).mutation(({ ctx, input }) => workWaypoint(ctx, input)),
|
|
8171
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 })),
|
|
@@ -8216,9 +8432,9 @@ var init_feature = __esm(() => {
|
|
|
8216
8432
|
});
|
|
8217
8433
|
|
|
8218
8434
|
// src/services/fsbrowse.ts
|
|
8219
|
-
import { existsSync as
|
|
8435
|
+
import { existsSync as existsSync15, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
8220
8436
|
import { homedir as homedir2 } from "os";
|
|
8221
|
-
import { basename, dirname as
|
|
8437
|
+
import { basename, dirname as dirname8, isAbsolute as isAbsolute2, join as join15, resolve as resolve10, sep as sep3 } from "path";
|
|
8222
8438
|
function expandPath(input, home = homedir2()) {
|
|
8223
8439
|
const trimmed = input.trim();
|
|
8224
8440
|
if (trimmed === "")
|
|
@@ -8227,12 +8443,12 @@ function expandPath(input, home = homedir2()) {
|
|
|
8227
8443
|
if (out === "~")
|
|
8228
8444
|
out = home;
|
|
8229
8445
|
else if (out.startsWith("~/") || out.startsWith("~\\"))
|
|
8230
|
-
out =
|
|
8446
|
+
out = join15(home, out.slice(2));
|
|
8231
8447
|
return resolve10(out);
|
|
8232
8448
|
}
|
|
8233
8449
|
function looksLikeRepo(dir) {
|
|
8234
8450
|
try {
|
|
8235
|
-
return
|
|
8451
|
+
return existsSync15(join15(dir, ".git"));
|
|
8236
8452
|
} catch {
|
|
8237
8453
|
return false;
|
|
8238
8454
|
}
|
|
@@ -8243,20 +8459,20 @@ function listRoots(home = homedir2()) {
|
|
|
8243
8459
|
for (let code = 67;code <= 90; code++) {
|
|
8244
8460
|
const drive = `${String.fromCharCode(code)}:${sep3}`;
|
|
8245
8461
|
try {
|
|
8246
|
-
if (
|
|
8462
|
+
if (existsSync15(drive))
|
|
8247
8463
|
roots.push({ label: drive.replace(/\\$/, ""), path: drive, kind: "drive" });
|
|
8248
8464
|
} catch {}
|
|
8249
8465
|
}
|
|
8250
8466
|
} else {
|
|
8251
8467
|
roots.push({ label: "/", path: "/", kind: "drive" });
|
|
8252
8468
|
}
|
|
8253
|
-
if (
|
|
8469
|
+
if (existsSync15(home))
|
|
8254
8470
|
roots.push({ label: "Home", path: home, kind: "home" });
|
|
8255
8471
|
const seen = new Set(roots.map(pathKey));
|
|
8256
8472
|
for (const name of COMMON_DIRS) {
|
|
8257
|
-
const candidate =
|
|
8473
|
+
const candidate = join15(home, name);
|
|
8258
8474
|
const key = pathKey({ path: candidate });
|
|
8259
|
-
if (seen.has(key) || !
|
|
8475
|
+
if (seen.has(key) || !existsSync15(candidate))
|
|
8260
8476
|
continue;
|
|
8261
8477
|
seen.add(key);
|
|
8262
8478
|
roots.push({ label: name, path: candidate, kind: "common" });
|
|
@@ -8271,7 +8487,7 @@ function crumbsFor(dir) {
|
|
|
8271
8487
|
let cur = dir;
|
|
8272
8488
|
for (;; ) {
|
|
8273
8489
|
out.unshift({ name: basename(cur) || cur, path: cur });
|
|
8274
|
-
const parent =
|
|
8490
|
+
const parent = dirname8(cur);
|
|
8275
8491
|
if (parent === cur)
|
|
8276
8492
|
break;
|
|
8277
8493
|
cur = parent;
|
|
@@ -8279,7 +8495,7 @@ function crumbsFor(dir) {
|
|
|
8279
8495
|
return out;
|
|
8280
8496
|
}
|
|
8281
8497
|
function parentOf(dir) {
|
|
8282
|
-
const parent =
|
|
8498
|
+
const parent = dirname8(dir);
|
|
8283
8499
|
return parent === dir ? null : parent;
|
|
8284
8500
|
}
|
|
8285
8501
|
function browseDir(input, showHidden = false, home = homedir2()) {
|
|
@@ -8287,13 +8503,13 @@ function browseDir(input, showHidden = false, home = homedir2()) {
|
|
|
8287
8503
|
if (!isAbsolute2(target)) {
|
|
8288
8504
|
throw new InvalidInputError(`path is not absolute: ${target}`);
|
|
8289
8505
|
}
|
|
8290
|
-
if (!
|
|
8506
|
+
if (!existsSync15(target)) {
|
|
8291
8507
|
throw new InvalidInputError(`path does not exist: ${target}`);
|
|
8292
8508
|
}
|
|
8293
8509
|
let dir = target;
|
|
8294
8510
|
try {
|
|
8295
8511
|
if (!statSync2(target).isDirectory())
|
|
8296
|
-
dir =
|
|
8512
|
+
dir = dirname8(target);
|
|
8297
8513
|
} catch (e) {
|
|
8298
8514
|
throw new InvalidInputError(`cannot read path: ${target} (${errMsg4(e)})`);
|
|
8299
8515
|
}
|
|
@@ -8315,7 +8531,7 @@ function browseDir(input, showHidden = false, home = homedir2()) {
|
|
|
8315
8531
|
continue;
|
|
8316
8532
|
if (!showHidden && name.startsWith("."))
|
|
8317
8533
|
continue;
|
|
8318
|
-
const path =
|
|
8534
|
+
const path = join15(dir, name);
|
|
8319
8535
|
const isSymlink = dirent.isSymbolicLink();
|
|
8320
8536
|
if (isSymlink && !showHidden)
|
|
8321
8537
|
continue;
|
|
@@ -8352,8 +8568,8 @@ var init_fsbrowse = __esm(() => {
|
|
|
8352
8568
|
});
|
|
8353
8569
|
|
|
8354
8570
|
// src/services/projects.ts
|
|
8355
|
-
import { existsSync as
|
|
8356
|
-
import { basename as basename2, join as
|
|
8571
|
+
import { existsSync as existsSync16 } from "fs";
|
|
8572
|
+
import { basename as basename2, join as join16 } from "path";
|
|
8357
8573
|
import { eq as eq12, isNull } from "drizzle-orm";
|
|
8358
8574
|
function listProjects(ctx) {
|
|
8359
8575
|
return ctx.db.select().from(projects).where(isNull(projects.closedAt)).all().map(rowToProject);
|
|
@@ -8434,7 +8650,7 @@ async function assertRepoTolerant(ctx, repoPath) {
|
|
|
8434
8650
|
} catch (e) {
|
|
8435
8651
|
if (!isNotImplemented(e))
|
|
8436
8652
|
throw e;
|
|
8437
|
-
if (!
|
|
8653
|
+
if (!existsSync16(join16(repoPath, ".git"))) {
|
|
8438
8654
|
throw new InvalidInputError(`not a git repository: ${repoPath}`);
|
|
8439
8655
|
}
|
|
8440
8656
|
}
|
|
@@ -8507,15 +8723,15 @@ var init_run = __esm(() => {
|
|
|
8507
8723
|
});
|
|
8508
8724
|
|
|
8509
8725
|
// src/services/settings.ts
|
|
8510
|
-
import { existsSync as
|
|
8511
|
-
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";
|
|
8512
8728
|
import * as z9 from "zod";
|
|
8513
8729
|
import { eq as eq13 } from "drizzle-orm";
|
|
8514
8730
|
function readRawConfig(configFile) {
|
|
8515
|
-
if (!
|
|
8731
|
+
if (!existsSync17(configFile))
|
|
8516
8732
|
return {};
|
|
8517
8733
|
try {
|
|
8518
|
-
const parsed = JSON.parse(
|
|
8734
|
+
const parsed = JSON.parse(readFileSync8(configFile, "utf8"));
|
|
8519
8735
|
if (typeof parsed !== "object" || parsed === null)
|
|
8520
8736
|
return {};
|
|
8521
8737
|
return foldLegacyModelConfig(parsed);
|
|
@@ -8654,8 +8870,8 @@ function updateSettings(ctx, input, io = {}) {
|
|
|
8654
8870
|
function writeGlobal(configFile, configKey, value) {
|
|
8655
8871
|
const raw = readRawConfig(configFile);
|
|
8656
8872
|
raw[configKey] = value;
|
|
8657
|
-
|
|
8658
|
-
|
|
8873
|
+
mkdirSync8(dirname9(configFile), { recursive: true });
|
|
8874
|
+
writeFileSync4(configFile, `${JSON.stringify(raw, null, 2)}
|
|
8659
8875
|
`);
|
|
8660
8876
|
}
|
|
8661
8877
|
function updateStepModel(ctx, input, configFile, io) {
|
|
@@ -8693,8 +8909,8 @@ function writeStepModel(configFile, step, value) {
|
|
|
8693
8909
|
else
|
|
8694
8910
|
stepModels[step] = value;
|
|
8695
8911
|
raw.stepModels = stepModels;
|
|
8696
|
-
|
|
8697
|
-
|
|
8912
|
+
mkdirSync8(dirname9(configFile), { recursive: true });
|
|
8913
|
+
writeFileSync4(configFile, `${JSON.stringify(raw, null, 2)}
|
|
8698
8914
|
`);
|
|
8699
8915
|
}
|
|
8700
8916
|
function field(view, key) {
|
|
@@ -9071,9 +9287,9 @@ var init_system_exec = __esm(() => {
|
|
|
9071
9287
|
});
|
|
9072
9288
|
|
|
9073
9289
|
// src/services/setup.ts
|
|
9074
|
-
import { copyFileSync, existsSync as
|
|
9075
|
-
import { dirname as
|
|
9076
|
-
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";
|
|
9077
9293
|
async function writeGitIdentity(exec, identity) {
|
|
9078
9294
|
const name = identity.name.trim();
|
|
9079
9295
|
const email = identity.email.trim();
|
|
@@ -9148,16 +9364,16 @@ async function resolveRuntime(exec, preferred) {
|
|
|
9148
9364
|
function resolveSandcastleBin() {
|
|
9149
9365
|
try {
|
|
9150
9366
|
const mainUrl = import.meta.resolve("@ai-hero/sandcastle");
|
|
9151
|
-
let dir =
|
|
9367
|
+
let dir = dirname10(fileURLToPath7(mainUrl));
|
|
9152
9368
|
for (let hops = 0;hops < 6; hops++) {
|
|
9153
|
-
const manifestPath =
|
|
9154
|
-
if (
|
|
9155
|
-
const manifest = JSON.parse(
|
|
9369
|
+
const manifestPath = join17(dir, "package.json");
|
|
9370
|
+
if (existsSync18(manifestPath)) {
|
|
9371
|
+
const manifest = JSON.parse(readFileSync9(manifestPath, "utf8"));
|
|
9156
9372
|
if (manifest.name === "@ai-hero/sandcastle" && manifest.bin?.sandcastle) {
|
|
9157
|
-
return
|
|
9373
|
+
return join17(dir, manifest.bin.sandcastle);
|
|
9158
9374
|
}
|
|
9159
9375
|
}
|
|
9160
|
-
const parent =
|
|
9376
|
+
const parent = dirname10(dir);
|
|
9161
9377
|
if (parent === dir)
|
|
9162
9378
|
break;
|
|
9163
9379
|
dir = parent;
|
|
@@ -9179,21 +9395,21 @@ function terminalSpec(kind, opts) {
|
|
|
9179
9395
|
};
|
|
9180
9396
|
}
|
|
9181
9397
|
function sandcastleTemplateDir() {
|
|
9182
|
-
return resolveAsset(ASSET_ENV.sandcastleTemplate,
|
|
9398
|
+
return resolveAsset(ASSET_ENV.sandcastleTemplate, fileURLToPath7(new URL("../assets/sandcastle", import.meta.url)));
|
|
9183
9399
|
}
|
|
9184
9400
|
function scaffoldSandcastleConfig(templateDir, targetDir) {
|
|
9185
|
-
const dir =
|
|
9186
|
-
if (
|
|
9401
|
+
const dir = join17(targetDir, ".sandcastle");
|
|
9402
|
+
if (existsSync18(dir))
|
|
9187
9403
|
return { scaffolded: false, dir };
|
|
9188
|
-
|
|
9404
|
+
mkdirSync9(dir, { recursive: true });
|
|
9189
9405
|
for (const name of readdirSync6(templateDir)) {
|
|
9190
|
-
copyFileSync(
|
|
9406
|
+
copyFileSync(join17(templateDir, name), join17(dir, name));
|
|
9191
9407
|
}
|
|
9192
9408
|
return { scaffolded: true, dir };
|
|
9193
9409
|
}
|
|
9194
9410
|
function prepareSandboxBuildContext() {
|
|
9195
9411
|
const target = sandboxBuildDir();
|
|
9196
|
-
|
|
9412
|
+
mkdirSync9(target, { recursive: true });
|
|
9197
9413
|
scaffoldSandcastleConfig(sandcastleTemplateDir(), target);
|
|
9198
9414
|
return target;
|
|
9199
9415
|
}
|
|
@@ -9229,10 +9445,10 @@ function createTokenVerifier(exec) {
|
|
|
9229
9445
|
function fileAfkTokenIo(exec) {
|
|
9230
9446
|
const path = envPath();
|
|
9231
9447
|
return {
|
|
9232
|
-
read: () =>
|
|
9448
|
+
read: () => existsSync18(path) ? readFileSync9(path, "utf8") : "",
|
|
9233
9449
|
write: (content) => {
|
|
9234
|
-
|
|
9235
|
-
|
|
9450
|
+
mkdirSync9(dirname10(path), { recursive: true });
|
|
9451
|
+
writeFileSync5(path, content, "utf8");
|
|
9236
9452
|
},
|
|
9237
9453
|
verify: createTokenVerifier(exec)
|
|
9238
9454
|
};
|
|
@@ -9285,110 +9501,6 @@ var init_setup2 = __esm(() => {
|
|
|
9285
9501
|
});
|
|
9286
9502
|
});
|
|
9287
9503
|
|
|
9288
|
-
// src/version.ts
|
|
9289
|
-
import { readFileSync as readFileSync8 } from "fs";
|
|
9290
|
-
import { dirname as dirname9, join as join17 } from "path";
|
|
9291
|
-
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
9292
|
-
function runcastleVersion() {
|
|
9293
|
-
if (cached2)
|
|
9294
|
-
return cached2;
|
|
9295
|
-
const here = dirname9(fileURLToPath7(import.meta.url));
|
|
9296
|
-
for (const pkgPath of [join17(here, "..", "package.json"), join17(here, "package.json")]) {
|
|
9297
|
-
try {
|
|
9298
|
-
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
9299
|
-
if (typeof pkg.version === "string") {
|
|
9300
|
-
cached2 = pkg.version;
|
|
9301
|
-
return cached2;
|
|
9302
|
-
}
|
|
9303
|
-
} catch {}
|
|
9304
|
-
}
|
|
9305
|
-
cached2 = UNKNOWN_VERSION;
|
|
9306
|
-
return cached2;
|
|
9307
|
-
}
|
|
9308
|
-
var cached2, UNKNOWN_VERSION = "0.0.0";
|
|
9309
|
-
var init_version = () => {};
|
|
9310
|
-
|
|
9311
|
-
// src/services/update-check.ts
|
|
9312
|
-
function parse(version) {
|
|
9313
|
-
const cleaned = version.trim().replace(/^v/, "");
|
|
9314
|
-
const [core = "", pre = ""] = cleaned.split("-", 2);
|
|
9315
|
-
const release2 = core.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
9316
|
-
return { release: release2, pre: pre ? pre.split(".") : [] };
|
|
9317
|
-
}
|
|
9318
|
-
function compareSemver(a, b) {
|
|
9319
|
-
const pa = parse(a);
|
|
9320
|
-
const pb = parse(b);
|
|
9321
|
-
const len = Math.max(pa.release.length, pb.release.length);
|
|
9322
|
-
for (let i = 0;i < len; i++) {
|
|
9323
|
-
const diff = (pa.release[i] ?? 0) - (pb.release[i] ?? 0);
|
|
9324
|
-
if (diff !== 0)
|
|
9325
|
-
return diff;
|
|
9326
|
-
}
|
|
9327
|
-
if (pa.pre.length === 0 && pb.pre.length === 0)
|
|
9328
|
-
return 0;
|
|
9329
|
-
if (pa.pre.length === 0)
|
|
9330
|
-
return 1;
|
|
9331
|
-
if (pb.pre.length === 0)
|
|
9332
|
-
return -1;
|
|
9333
|
-
const preLen = Math.max(pa.pre.length, pb.pre.length);
|
|
9334
|
-
for (let i = 0;i < preLen; i++) {
|
|
9335
|
-
const x = pa.pre[i];
|
|
9336
|
-
const y = pb.pre[i];
|
|
9337
|
-
if (x === undefined)
|
|
9338
|
-
return -1;
|
|
9339
|
-
if (y === undefined)
|
|
9340
|
-
return 1;
|
|
9341
|
-
const nx = Number(x);
|
|
9342
|
-
const ny = Number(y);
|
|
9343
|
-
const bothNumeric = !Number.isNaN(nx) && !Number.isNaN(ny);
|
|
9344
|
-
const cmp = bothNumeric ? nx - ny : x < y ? -1 : x > y ? 1 : 0;
|
|
9345
|
-
if (cmp !== 0)
|
|
9346
|
-
return cmp;
|
|
9347
|
-
}
|
|
9348
|
-
return 0;
|
|
9349
|
-
}
|
|
9350
|
-
async function checkForUpdate(opts) {
|
|
9351
|
-
const { current: current2 } = opts;
|
|
9352
|
-
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
9353
|
-
if (current2 === UNKNOWN_VERSION)
|
|
9354
|
-
return NO_UPDATE(current2);
|
|
9355
|
-
try {
|
|
9356
|
-
const res = await fetchImpl(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
|
|
9357
|
-
if (!res.ok)
|
|
9358
|
-
return NO_UPDATE(current2);
|
|
9359
|
-
const body = await res.json();
|
|
9360
|
-
const latest = typeof body.version === "string" ? body.version : null;
|
|
9361
|
-
if (!latest)
|
|
9362
|
-
return NO_UPDATE(current2);
|
|
9363
|
-
return {
|
|
9364
|
-
current: current2,
|
|
9365
|
-
latest,
|
|
9366
|
-
updateAvailable: compareSemver(latest, current2) > 0,
|
|
9367
|
-
command: UPDATE_COMMAND
|
|
9368
|
-
};
|
|
9369
|
-
} catch {
|
|
9370
|
-
return NO_UPDATE(current2);
|
|
9371
|
-
}
|
|
9372
|
-
}
|
|
9373
|
-
async function getUpdateInfo(current2, fetchImpl) {
|
|
9374
|
-
if (cache)
|
|
9375
|
-
return cache;
|
|
9376
|
-
const info = await checkForUpdate({ current: current2, fetchImpl });
|
|
9377
|
-
if (info.latest !== null)
|
|
9378
|
-
cache = info;
|
|
9379
|
-
return info;
|
|
9380
|
-
}
|
|
9381
|
-
var PACKAGE_NAME = "runcastle", UPDATE_COMMAND, NO_UPDATE = (current2) => ({
|
|
9382
|
-
current: current2,
|
|
9383
|
-
latest: null,
|
|
9384
|
-
updateAvailable: false,
|
|
9385
|
-
command: UPDATE_COMMAND
|
|
9386
|
-
}), cache;
|
|
9387
|
-
var init_update_check = __esm(() => {
|
|
9388
|
-
init_version();
|
|
9389
|
-
UPDATE_COMMAND = `bun add -g ${PACKAGE_NAME}@latest`;
|
|
9390
|
-
});
|
|
9391
|
-
|
|
9392
9504
|
// src/trpc/routers/system.ts
|
|
9393
9505
|
var systemRouter;
|
|
9394
9506
|
var init_system = __esm(() => {
|
|
@@ -9402,8 +9514,8 @@ var init_system = __esm(() => {
|
|
|
9402
9514
|
});
|
|
9403
9515
|
|
|
9404
9516
|
// src/services/test-notes.ts
|
|
9405
|
-
import { mkdirSync as
|
|
9406
|
-
import { dirname as
|
|
9517
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
9518
|
+
import { dirname as dirname11 } from "path";
|
|
9407
9519
|
import { asc as asc4, eq as eq14 } from "drizzle-orm";
|
|
9408
9520
|
function rowToNote(row) {
|
|
9409
9521
|
return {
|
|
@@ -9548,8 +9660,8 @@ function renderTestNotes(ctx, feature) {
|
|
|
9548
9660
|
lines.push(noteLine(ctx, note));
|
|
9549
9661
|
}
|
|
9550
9662
|
const path = featureDocPath(projectForFeature(ctx, feature), feature, "test-notes.md");
|
|
9551
|
-
|
|
9552
|
-
|
|
9663
|
+
mkdirSync10(dirname11(path), { recursive: true });
|
|
9664
|
+
writeFileSync6(path, `${lines.join(`
|
|
9553
9665
|
`)}
|
|
9554
9666
|
`, "utf8");
|
|
9555
9667
|
}
|
|
@@ -9748,6 +9860,7 @@ async function startServer() {
|
|
|
9748
9860
|
websocket: terminalWebSocket
|
|
9749
9861
|
});
|
|
9750
9862
|
console.log(`runcastle${process.env.RUNCASTLE_DEV ? " [dev]" : ""} server listening on ` + `http://localhost:${config2.serverPort} \u2014 data dir: ${dataDir()}`);
|
|
9863
|
+
getUpdateInfo(runcastleVersion()).catch(() => {});
|
|
9751
9864
|
const SHUTDOWN_KEY = Symbol.for("runcastle.shutdown.wired");
|
|
9752
9865
|
const g = globalThis;
|
|
9753
9866
|
if (!g[SHUTDOWN_KEY]) {
|
|
@@ -9773,7 +9886,9 @@ var init_src2 = __esm(() => {
|
|
|
9773
9886
|
init_hooks();
|
|
9774
9887
|
init_stream();
|
|
9775
9888
|
init_web();
|
|
9889
|
+
init_update_check();
|
|
9776
9890
|
init_router();
|
|
9891
|
+
init_version();
|
|
9777
9892
|
init_reconcile_runs();
|
|
9778
9893
|
});
|
|
9779
9894
|
init_src2();
|