runcastle 1.0.18 → 1.0.19
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/index.js
CHANGED
|
@@ -5430,9 +5430,143 @@ var init_feature = __esm(() => {
|
|
|
5430
5430
|
});
|
|
5431
5431
|
});
|
|
5432
5432
|
|
|
5433
|
+
// src/services/fsbrowse.ts
|
|
5434
|
+
import { existsSync as existsSync14, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
5435
|
+
import { homedir as homedir2 } from "os";
|
|
5436
|
+
import { basename, dirname as dirname6, isAbsolute, join as join14, resolve as resolve8, sep as sep3 } from "path";
|
|
5437
|
+
function expandPath(input, home = homedir2()) {
|
|
5438
|
+
const trimmed = input.trim();
|
|
5439
|
+
if (trimmed === "")
|
|
5440
|
+
return trimmed;
|
|
5441
|
+
let out = trimmed;
|
|
5442
|
+
if (out === "~")
|
|
5443
|
+
out = home;
|
|
5444
|
+
else if (out.startsWith("~/") || out.startsWith("~\\"))
|
|
5445
|
+
out = join14(home, out.slice(2));
|
|
5446
|
+
return resolve8(out);
|
|
5447
|
+
}
|
|
5448
|
+
function looksLikeRepo(dir) {
|
|
5449
|
+
try {
|
|
5450
|
+
return existsSync14(join14(dir, ".git"));
|
|
5451
|
+
} catch {
|
|
5452
|
+
return false;
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
function listRoots(home = homedir2()) {
|
|
5456
|
+
const roots = [];
|
|
5457
|
+
if (process.platform === "win32") {
|
|
5458
|
+
for (let code = 67;code <= 90; code++) {
|
|
5459
|
+
const drive = `${String.fromCharCode(code)}:${sep3}`;
|
|
5460
|
+
try {
|
|
5461
|
+
if (existsSync14(drive))
|
|
5462
|
+
roots.push({ label: drive.replace(/\\$/, ""), path: drive, kind: "drive" });
|
|
5463
|
+
} catch {}
|
|
5464
|
+
}
|
|
5465
|
+
} else {
|
|
5466
|
+
roots.push({ label: "/", path: "/", kind: "drive" });
|
|
5467
|
+
}
|
|
5468
|
+
if (existsSync14(home))
|
|
5469
|
+
roots.push({ label: "Home", path: home, kind: "home" });
|
|
5470
|
+
const seen = new Set(roots.map(pathKey));
|
|
5471
|
+
for (const name of COMMON_DIRS) {
|
|
5472
|
+
const candidate = join14(home, name);
|
|
5473
|
+
const key = pathKey({ path: candidate });
|
|
5474
|
+
if (seen.has(key) || !existsSync14(candidate))
|
|
5475
|
+
continue;
|
|
5476
|
+
seen.add(key);
|
|
5477
|
+
roots.push({ label: name, path: candidate, kind: "common" });
|
|
5478
|
+
}
|
|
5479
|
+
return roots;
|
|
5480
|
+
}
|
|
5481
|
+
function pathKey(r) {
|
|
5482
|
+
return process.platform === "win32" ? r.path.toLowerCase() : r.path;
|
|
5483
|
+
}
|
|
5484
|
+
function crumbsFor(dir) {
|
|
5485
|
+
const out = [];
|
|
5486
|
+
let cur = dir;
|
|
5487
|
+
for (;; ) {
|
|
5488
|
+
out.unshift({ name: basename(cur) || cur, path: cur });
|
|
5489
|
+
const parent = dirname6(cur);
|
|
5490
|
+
if (parent === cur)
|
|
5491
|
+
break;
|
|
5492
|
+
cur = parent;
|
|
5493
|
+
}
|
|
5494
|
+
return out;
|
|
5495
|
+
}
|
|
5496
|
+
function parentOf(dir) {
|
|
5497
|
+
const parent = dirname6(dir);
|
|
5498
|
+
return parent === dir ? null : parent;
|
|
5499
|
+
}
|
|
5500
|
+
function browseDir(input, showHidden = false, home = homedir2()) {
|
|
5501
|
+
const target = input && input.trim() !== "" ? expandPath(input, home) : home;
|
|
5502
|
+
if (!isAbsolute(target)) {
|
|
5503
|
+
throw new InvalidInputError(`path is not absolute: ${target}`);
|
|
5504
|
+
}
|
|
5505
|
+
if (!existsSync14(target)) {
|
|
5506
|
+
throw new InvalidInputError(`path does not exist: ${target}`);
|
|
5507
|
+
}
|
|
5508
|
+
let dir = target;
|
|
5509
|
+
try {
|
|
5510
|
+
if (!statSync2(target).isDirectory())
|
|
5511
|
+
dir = dirname6(target);
|
|
5512
|
+
} catch (e) {
|
|
5513
|
+
throw new InvalidInputError(`cannot read path: ${target} (${errMsg2(e)})`);
|
|
5514
|
+
}
|
|
5515
|
+
let dirents;
|
|
5516
|
+
try {
|
|
5517
|
+
dirents = readdirSync5(dir, { withFileTypes: true, encoding: "utf8" });
|
|
5518
|
+
} catch (e) {
|
|
5519
|
+
throw new InvalidInputError(`cannot read directory: ${dir} (${errMsg2(e)})`);
|
|
5520
|
+
}
|
|
5521
|
+
const entries = [];
|
|
5522
|
+
let truncated = false;
|
|
5523
|
+
for (const dirent of dirents) {
|
|
5524
|
+
if (entries.length >= MAX_ENTRIES) {
|
|
5525
|
+
truncated = true;
|
|
5526
|
+
break;
|
|
5527
|
+
}
|
|
5528
|
+
const name = dirent.name;
|
|
5529
|
+
if (SKIP_ALWAYS.has(name))
|
|
5530
|
+
continue;
|
|
5531
|
+
if (!showHidden && name.startsWith("."))
|
|
5532
|
+
continue;
|
|
5533
|
+
const path = join14(dir, name);
|
|
5534
|
+
const isSymlink = dirent.isSymbolicLink();
|
|
5535
|
+
let isDirectory = dirent.isDirectory();
|
|
5536
|
+
if (isSymlink) {
|
|
5537
|
+
try {
|
|
5538
|
+
isDirectory = statSync2(path).isDirectory();
|
|
5539
|
+
} catch {
|
|
5540
|
+
continue;
|
|
5541
|
+
}
|
|
5542
|
+
}
|
|
5543
|
+
if (!isDirectory)
|
|
5544
|
+
continue;
|
|
5545
|
+
entries.push({ name, path, isRepo: looksLikeRepo(path), isSymlink });
|
|
5546
|
+
}
|
|
5547
|
+
entries.sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: "base" }));
|
|
5548
|
+
return {
|
|
5549
|
+
dir,
|
|
5550
|
+
parent: parentOf(dir),
|
|
5551
|
+
crumbs: crumbsFor(dir),
|
|
5552
|
+
entries,
|
|
5553
|
+
isRepo: looksLikeRepo(dir),
|
|
5554
|
+
truncated
|
|
5555
|
+
};
|
|
5556
|
+
}
|
|
5557
|
+
function errMsg2(e) {
|
|
5558
|
+
return e instanceof Error ? e.message : String(e);
|
|
5559
|
+
}
|
|
5560
|
+
var SKIP_ALWAYS, MAX_ENTRIES = 1000, COMMON_DIRS;
|
|
5561
|
+
var init_fsbrowse = __esm(() => {
|
|
5562
|
+
init_errors();
|
|
5563
|
+
SKIP_ALWAYS = new Set(["node_modules", "$RECYCLE.BIN", "System Volume Information"]);
|
|
5564
|
+
COMMON_DIRS = ["Projects", "projects", "code", "Code", "src", "dev", "repos", "git"];
|
|
5565
|
+
});
|
|
5566
|
+
|
|
5433
5567
|
// src/services/projects.ts
|
|
5434
|
-
import { existsSync as
|
|
5435
|
-
import { basename, join as
|
|
5568
|
+
import { existsSync as existsSync15 } from "fs";
|
|
5569
|
+
import { basename as basename2, join as join15 } from "path";
|
|
5436
5570
|
import { eq as eq9, isNull } from "drizzle-orm";
|
|
5437
5571
|
function listProjects(ctx) {
|
|
5438
5572
|
return ctx.db.select().from(projects).where(isNull(projects.closedAt)).all().map(rowToProject);
|
|
@@ -5448,14 +5582,15 @@ function warnIfTranslatedMount(ctx, projectId, repoPath) {
|
|
|
5448
5582
|
message: `${repoPath} is a Windows drive mounted into Linux (/mnt) \u2014 git and burns pay a per-file translation tax here; clone the repo into the Linux filesystem (e.g. ~/projects) for native speed`
|
|
5449
5583
|
});
|
|
5450
5584
|
}
|
|
5451
|
-
async function openProject(ctx,
|
|
5585
|
+
async function openProject(ctx, rawPath) {
|
|
5586
|
+
const repoPath = expandPath(rawPath);
|
|
5452
5587
|
await assertRepoTolerant(ctx, repoPath);
|
|
5453
5588
|
const mainBranch = await detectMainBranchTolerant(ctx, repoPath);
|
|
5454
|
-
const name =
|
|
5455
|
-
const existing = getProjectByRepoPath(ctx, repoPath);
|
|
5589
|
+
const name = basename2(repoPath) || repoPath;
|
|
5590
|
+
const existing = getProjectByRepoPath(ctx, repoPath) ?? findProjectByCanonPath(ctx, repoPath);
|
|
5456
5591
|
if (existing) {
|
|
5457
|
-
ctx.db.update(projects).set({ mainBranch, closedAt: null }).where(eq9(projects.id, existing.id)).run();
|
|
5458
|
-
const reopened = { ...existing, mainBranch };
|
|
5592
|
+
ctx.db.update(projects).set({ mainBranch, repoPath, closedAt: null }).where(eq9(projects.id, existing.id)).run();
|
|
5593
|
+
const reopened = { ...existing, mainBranch, repoPath };
|
|
5459
5594
|
emitProject(ctx, existing.id, {
|
|
5460
5595
|
type: "project.opened",
|
|
5461
5596
|
message: `project ${existing.name} re-opened at ${repoPath} (${mainBranch})`
|
|
@@ -5502,13 +5637,17 @@ function renameProject(ctx, projectId, name) {
|
|
|
5502
5637
|
});
|
|
5503
5638
|
return { ...project, name };
|
|
5504
5639
|
}
|
|
5640
|
+
function findProjectByCanonPath(ctx, repoPath) {
|
|
5641
|
+
const key = canon(repoPath);
|
|
5642
|
+
return allProjects(ctx).find((p) => canon(p.repoPath) === key) ?? null;
|
|
5643
|
+
}
|
|
5505
5644
|
async function assertRepoTolerant(ctx, repoPath) {
|
|
5506
5645
|
try {
|
|
5507
5646
|
await assertRepo(repoPath);
|
|
5508
5647
|
} catch (e) {
|
|
5509
5648
|
if (!isNotImplemented(e))
|
|
5510
5649
|
throw e;
|
|
5511
|
-
if (!
|
|
5650
|
+
if (!existsSync15(join15(repoPath, ".git"))) {
|
|
5512
5651
|
throw new InvalidInputError(`not a git repository: ${repoPath}`);
|
|
5513
5652
|
}
|
|
5514
5653
|
}
|
|
@@ -5527,6 +5666,7 @@ var init_projects = __esm(() => {
|
|
|
5527
5666
|
init_schema();
|
|
5528
5667
|
init_errors();
|
|
5529
5668
|
init_events();
|
|
5669
|
+
init_fsbrowse();
|
|
5530
5670
|
init_git();
|
|
5531
5671
|
init_repo();
|
|
5532
5672
|
});
|
|
@@ -5535,12 +5675,15 @@ var init_projects = __esm(() => {
|
|
|
5535
5675
|
import * as z7 from "zod";
|
|
5536
5676
|
var projectRouter;
|
|
5537
5677
|
var init_project = __esm(() => {
|
|
5678
|
+
init_fsbrowse();
|
|
5538
5679
|
init_git();
|
|
5539
5680
|
init_projects();
|
|
5540
5681
|
init_repo();
|
|
5541
5682
|
init_context();
|
|
5542
5683
|
projectRouter = router({
|
|
5543
5684
|
list: publicProcedure.query(({ ctx }) => listProjects(ctx)),
|
|
5685
|
+
roots: publicProcedure.query(() => listRoots()),
|
|
5686
|
+
browse: publicProcedure.input(z7.object({ dir: z7.string().optional(), showHidden: z7.boolean().optional() }).optional()).query(({ input }) => browseDir(input?.dir, input?.showHidden ?? false)),
|
|
5544
5687
|
branches: publicProcedure.input(z7.object({ projectId: z7.string() })).query(({ ctx, input }) => listBranches(requireProjectById(ctx, input.projectId))),
|
|
5545
5688
|
open: publicProcedure.input(z7.object({ repoPath: z7.string().min(1) })).mutation(({ ctx, input }) => openProject(ctx, input.repoPath)),
|
|
5546
5689
|
close: publicProcedure.input(z7.object({ projectId: z7.string() })).mutation(({ ctx, input }) => closeProject(ctx, input.projectId)),
|
|
@@ -5567,12 +5710,12 @@ var init_run = __esm(() => {
|
|
|
5567
5710
|
});
|
|
5568
5711
|
|
|
5569
5712
|
// src/services/settings.ts
|
|
5570
|
-
import { existsSync as
|
|
5571
|
-
import { dirname as
|
|
5713
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
5714
|
+
import { dirname as dirname7 } from "path";
|
|
5572
5715
|
import * as z9 from "zod";
|
|
5573
5716
|
import { eq as eq10 } from "drizzle-orm";
|
|
5574
5717
|
function readRawConfig(configFile) {
|
|
5575
|
-
if (!
|
|
5718
|
+
if (!existsSync16(configFile))
|
|
5576
5719
|
return {};
|
|
5577
5720
|
try {
|
|
5578
5721
|
const parsed = JSON.parse(readFileSync6(configFile, "utf8"));
|
|
@@ -5694,7 +5837,7 @@ function updateSettings(ctx, input, io = {}) {
|
|
|
5694
5837
|
function writeGlobal(configFile, configKey, value) {
|
|
5695
5838
|
const raw = readRawConfig(configFile);
|
|
5696
5839
|
raw[configKey] = value;
|
|
5697
|
-
mkdirSync7(
|
|
5840
|
+
mkdirSync7(dirname7(configFile), { recursive: true });
|
|
5698
5841
|
writeFileSync3(configFile, `${JSON.stringify(raw, null, 2)}
|
|
5699
5842
|
`);
|
|
5700
5843
|
}
|
|
@@ -5733,7 +5876,7 @@ function writeStepModel(configFile, step, value) {
|
|
|
5733
5876
|
else
|
|
5734
5877
|
stepModels[step] = value;
|
|
5735
5878
|
raw.stepModels = stepModels;
|
|
5736
|
-
mkdirSync7(
|
|
5879
|
+
mkdirSync7(dirname7(configFile), { recursive: true });
|
|
5737
5880
|
writeFileSync3(configFile, `${JSON.stringify(raw, null, 2)}
|
|
5738
5881
|
`);
|
|
5739
5882
|
}
|
|
@@ -6015,7 +6158,7 @@ var init_doctor = __esm(() => {
|
|
|
6015
6158
|
// src/doctor/system-exec.ts
|
|
6016
6159
|
import { spawn as spawn2 } from "child_process";
|
|
6017
6160
|
function createSystemExec(opts = {}) {
|
|
6018
|
-
return (command, args) => new Promise((
|
|
6161
|
+
return (command, args) => new Promise((resolve9) => {
|
|
6019
6162
|
const resolved = resolveExecutable(command);
|
|
6020
6163
|
const isShim = /\.(cmd|bat)$/i.test(resolved);
|
|
6021
6164
|
const file = isShim ? process.env.ComSpec ?? "cmd.exe" : resolved;
|
|
@@ -6030,10 +6173,10 @@ function createSystemExec(opts = {}) {
|
|
|
6030
6173
|
stderr += d.toString();
|
|
6031
6174
|
});
|
|
6032
6175
|
child.on("error", (err) => {
|
|
6033
|
-
|
|
6176
|
+
resolve9({ ok: false, code: null, stdout, stderr: stderr || String(err) });
|
|
6034
6177
|
});
|
|
6035
6178
|
child.on("close", (code) => {
|
|
6036
|
-
|
|
6179
|
+
resolve9({ ok: true, code: code ?? 0, stdout, stderr });
|
|
6037
6180
|
});
|
|
6038
6181
|
});
|
|
6039
6182
|
}
|
|
@@ -6042,8 +6185,8 @@ var init_system_exec = __esm(() => {
|
|
|
6042
6185
|
});
|
|
6043
6186
|
|
|
6044
6187
|
// src/services/setup.ts
|
|
6045
|
-
import { copyFileSync, existsSync as
|
|
6046
|
-
import { dirname as
|
|
6188
|
+
import { copyFileSync, existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync7, readdirSync as readdirSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
6189
|
+
import { dirname as dirname8, join as join16 } from "path";
|
|
6047
6190
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
6048
6191
|
async function writeGitIdentity(exec, identity) {
|
|
6049
6192
|
const name = identity.name.trim();
|
|
@@ -6119,16 +6262,16 @@ async function resolveRuntime(exec, preferred) {
|
|
|
6119
6262
|
function resolveSandcastleBin() {
|
|
6120
6263
|
try {
|
|
6121
6264
|
const mainUrl = import.meta.resolve("@ai-hero/sandcastle");
|
|
6122
|
-
let dir =
|
|
6265
|
+
let dir = dirname8(fileURLToPath6(mainUrl));
|
|
6123
6266
|
for (let hops = 0;hops < 6; hops++) {
|
|
6124
|
-
const manifestPath =
|
|
6125
|
-
if (
|
|
6267
|
+
const manifestPath = join16(dir, "package.json");
|
|
6268
|
+
if (existsSync17(manifestPath)) {
|
|
6126
6269
|
const manifest = JSON.parse(readFileSync7(manifestPath, "utf8"));
|
|
6127
6270
|
if (manifest.name === "@ai-hero/sandcastle" && manifest.bin?.sandcastle) {
|
|
6128
|
-
return
|
|
6271
|
+
return join16(dir, manifest.bin.sandcastle);
|
|
6129
6272
|
}
|
|
6130
6273
|
}
|
|
6131
|
-
const parent =
|
|
6274
|
+
const parent = dirname8(dir);
|
|
6132
6275
|
if (parent === dir)
|
|
6133
6276
|
break;
|
|
6134
6277
|
dir = parent;
|
|
@@ -6153,12 +6296,12 @@ function sandcastleTemplateDir() {
|
|
|
6153
6296
|
return resolveAsset(ASSET_ENV.sandcastleTemplate, fileURLToPath6(new URL("../assets/sandcastle", import.meta.url)));
|
|
6154
6297
|
}
|
|
6155
6298
|
function scaffoldSandcastleConfig(templateDir, targetDir) {
|
|
6156
|
-
const dir =
|
|
6157
|
-
if (
|
|
6299
|
+
const dir = join16(targetDir, ".sandcastle");
|
|
6300
|
+
if (existsSync17(dir))
|
|
6158
6301
|
return { scaffolded: false, dir };
|
|
6159
6302
|
mkdirSync8(dir, { recursive: true });
|
|
6160
|
-
for (const name of
|
|
6161
|
-
copyFileSync(
|
|
6303
|
+
for (const name of readdirSync6(templateDir)) {
|
|
6304
|
+
copyFileSync(join16(templateDir, name), join16(dir, name));
|
|
6162
6305
|
}
|
|
6163
6306
|
return { scaffolded: true, dir };
|
|
6164
6307
|
}
|
|
@@ -6183,9 +6326,9 @@ function createTokenVerifier(exec) {
|
|
|
6183
6326
|
function fileAfkTokenIo(exec) {
|
|
6184
6327
|
const path = envPath();
|
|
6185
6328
|
return {
|
|
6186
|
-
read: () =>
|
|
6329
|
+
read: () => existsSync17(path) ? readFileSync7(path, "utf8") : "",
|
|
6187
6330
|
write: (content) => {
|
|
6188
|
-
mkdirSync8(
|
|
6331
|
+
mkdirSync8(dirname8(path), { recursive: true });
|
|
6189
6332
|
writeFileSync4(path, content, "utf8");
|
|
6190
6333
|
},
|
|
6191
6334
|
verify: createTokenVerifier(exec)
|
|
@@ -6322,13 +6465,13 @@ var init_update_check = __esm(() => {
|
|
|
6322
6465
|
|
|
6323
6466
|
// src/version.ts
|
|
6324
6467
|
import { readFileSync as readFileSync8 } from "fs";
|
|
6325
|
-
import { dirname as
|
|
6468
|
+
import { dirname as dirname9, join as join17 } from "path";
|
|
6326
6469
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
6327
6470
|
function runcastleVersion() {
|
|
6328
6471
|
if (cached2)
|
|
6329
6472
|
return cached2;
|
|
6330
|
-
const here =
|
|
6331
|
-
for (const pkgPath of [
|
|
6473
|
+
const here = dirname9(fileURLToPath7(import.meta.url));
|
|
6474
|
+
for (const pkgPath of [join17(here, "..", "package.json"), join17(here, "package.json")]) {
|
|
6332
6475
|
try {
|
|
6333
6476
|
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
6334
6477
|
if (typeof pkg.version === "string") {
|