bitpull 0.1.10 → 0.1.12
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/bitpull.cjs +129 -5
- package/package.json +1 -1
package/bitpull.cjs
CHANGED
|
@@ -18444,6 +18444,55 @@ function sandboxArgs(sb, name, workdir, spec) {
|
|
|
18444
18444
|
...spec.command
|
|
18445
18445
|
];
|
|
18446
18446
|
}
|
|
18447
|
+
function dockerCreateArgs(sb, name, volume, spec) {
|
|
18448
|
+
const args = sandboxArgs(sb, name, "HOST", spec);
|
|
18449
|
+
const v = args.indexOf("-v");
|
|
18450
|
+
args.splice(v, 2, "-v", `${volume}:/work:rw`);
|
|
18451
|
+
args.splice(0, 2, "create");
|
|
18452
|
+
return args;
|
|
18453
|
+
}
|
|
18454
|
+
var usesVolumes = (sb) => sb.cli === "docker";
|
|
18455
|
+
function tarArchive(entries) {
|
|
18456
|
+
const blocks = [];
|
|
18457
|
+
const octal = (n, len) => n.toString(8).padStart(len - 1, "0") + "\0";
|
|
18458
|
+
for (const e of entries) {
|
|
18459
|
+
const isDir = e.data === void 0;
|
|
18460
|
+
const full = isDir && !e.path.endsWith("/") ? `${e.path}/` : e.path;
|
|
18461
|
+
let name = full;
|
|
18462
|
+
let prefix = "";
|
|
18463
|
+
if (Buffer.byteLength(full) > 100) {
|
|
18464
|
+
const cut = [...full.matchAll(/\//g)].map((m) => m.index).find((i) => i <= 155 && Buffer.byteLength(full.slice(i + 1)) <= 100);
|
|
18465
|
+
if (cut === void 0) throw new Error(`path too long: ${e.path}`);
|
|
18466
|
+
prefix = full.slice(0, cut);
|
|
18467
|
+
name = full.slice(cut + 1);
|
|
18468
|
+
}
|
|
18469
|
+
const h = Buffer.alloc(512);
|
|
18470
|
+
h.write(name, 0, 100);
|
|
18471
|
+
h.write(octal(isDir ? 511 : 438, 8), 100);
|
|
18472
|
+
h.write(octal(65534, 8), 108);
|
|
18473
|
+
h.write(octal(65534, 8), 116);
|
|
18474
|
+
h.write(octal(isDir ? 0 : e.data.length, 12), 124);
|
|
18475
|
+
h.write(octal(Math.floor(Date.now() / 1e3), 12), 136);
|
|
18476
|
+
h.write(" ", 148);
|
|
18477
|
+
h.write(isDir ? "5" : "0", 156);
|
|
18478
|
+
h.write("ustar\0", 257);
|
|
18479
|
+
h.write("00", 263);
|
|
18480
|
+
h.write("nobody", 265);
|
|
18481
|
+
h.write("nobody", 297);
|
|
18482
|
+
h.write(prefix, 345, 155);
|
|
18483
|
+
let sum = 0;
|
|
18484
|
+
for (const b of h) sum += b;
|
|
18485
|
+
h.write(octal(sum, 7) + " ", 148);
|
|
18486
|
+
blocks.push(h);
|
|
18487
|
+
if (!isDir && e.data.length) {
|
|
18488
|
+
blocks.push(e.data);
|
|
18489
|
+
const pad = (512 - e.data.length % 512) % 512;
|
|
18490
|
+
if (pad) blocks.push(Buffer.alloc(pad));
|
|
18491
|
+
}
|
|
18492
|
+
}
|
|
18493
|
+
blocks.push(Buffer.alloc(1024));
|
|
18494
|
+
return Buffer.concat(blocks);
|
|
18495
|
+
}
|
|
18447
18496
|
function killArgs(sb, name) {
|
|
18448
18497
|
return sb.runtime === "apple" ? ["kill", name] : ["kill", name];
|
|
18449
18498
|
}
|
|
@@ -18751,9 +18800,82 @@ async function collectOutputs(dir) {
|
|
|
18751
18800
|
return out;
|
|
18752
18801
|
}
|
|
18753
18802
|
async function runContainer(spec, sandbox, signal) {
|
|
18803
|
+
return usesVolumes(sandbox) ? runDockerJob(spec, sandbox, signal) : runFolderJob(spec, sandbox, signal);
|
|
18804
|
+
}
|
|
18805
|
+
function inputFiles(spec) {
|
|
18806
|
+
const files = [];
|
|
18807
|
+
for (const [path, b64] of Object.entries(spec.inputs ?? {})) {
|
|
18808
|
+
const rel = normalizeRepoPath(path);
|
|
18809
|
+
if (!rel || rel.startsWith("out/")) return null;
|
|
18810
|
+
files.push({ path: rel, data: Buffer.from(b64, "base64") });
|
|
18811
|
+
}
|
|
18812
|
+
return files;
|
|
18813
|
+
}
|
|
18814
|
+
function runWithInput(cmd, args, input) {
|
|
18815
|
+
return new Promise((resolve, reject) => {
|
|
18816
|
+
const child = (0, import_node_child_process5.spawn)(cmd, args, { stdio: ["pipe", "ignore", "pipe"], windowsHide: true });
|
|
18817
|
+
let stderr = "";
|
|
18818
|
+
child.stderr.on("data", (c) => stderr += c.toString("utf8"));
|
|
18819
|
+
child.on("error", reject);
|
|
18820
|
+
child.on("close", (code) => resolve({ code: code ?? -1, stderr }));
|
|
18821
|
+
child.stdin.end(input);
|
|
18822
|
+
});
|
|
18823
|
+
}
|
|
18824
|
+
async function runDockerJob(spec, sandbox, signal) {
|
|
18825
|
+
const files = inputFiles(spec);
|
|
18826
|
+
if (!files) return { ok: false, usage: {}, error: "bad input path" };
|
|
18827
|
+
const name = `bitpull-${(0, import_node_crypto6.randomBytes)(6).toString("hex")}`;
|
|
18828
|
+
const volume = `${name}-work`;
|
|
18829
|
+
const docker = (args, timeout = 12e4) => exec4("docker", args, { timeout, windowsHide: true });
|
|
18830
|
+
let outDir = null;
|
|
18831
|
+
try {
|
|
18832
|
+
for (const image of [spec.image, PROBE_IMAGE]) {
|
|
18833
|
+
const pulled = await ensureImage(sandbox, image);
|
|
18834
|
+
if (!pulled.ok) return { ok: false, usage: {}, error: `sandbox_error: ${pulled.error}` };
|
|
18835
|
+
}
|
|
18836
|
+
await docker(["volume", "create", volume]);
|
|
18837
|
+
await docker(dockerCreateArgs(sandbox, name, volume, spec));
|
|
18838
|
+
const tar = tarArchive([{ path: "." }, { path: "out" }, ...[...new Set(files.flatMap((f) => f.path.split("/").slice(0, -1).map((_, i, a) => a.slice(0, i + 1).join("/"))))].map((d) => ({ path: d })), ...files]);
|
|
18839
|
+
const copied = await runWithInput("docker", ["cp", "-a", "-", `${name}:/work`], tar);
|
|
18840
|
+
if (copied.code !== 0) return { ok: false, usage: {}, error: `sandbox_error: ${copied.stderr.trim().slice(0, 300)}` };
|
|
18841
|
+
await docker(["run", "--rm", "--network", "none", "--cap-drop", "ALL", "--cap-add", "CHOWN", "--security-opt", "no-new-privileges", "-v", `${volume}:/work`, PROBE_IMAGE, "chown", "65534:65534", "/work"]);
|
|
18842
|
+
const started = Date.now();
|
|
18843
|
+
const stdout = capped();
|
|
18844
|
+
const stderr = capped();
|
|
18845
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
18846
|
+
const child = (0, import_node_child_process5.spawn)("docker", ["start", "-a", name], { stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
18847
|
+
child.stdout.on("data", (c) => stdout.push(c));
|
|
18848
|
+
child.stderr.on("data", (c) => stderr.push(c));
|
|
18849
|
+
const kill = () => (0, import_node_child_process5.spawn)("docker", ["kill", name], { stdio: "ignore", windowsHide: true });
|
|
18850
|
+
const timer = setTimeout(kill, spec.timeoutSec * 1e3);
|
|
18851
|
+
signal.addEventListener("abort", kill, { once: true });
|
|
18852
|
+
child.on("error", reject);
|
|
18853
|
+
child.on("close", (code) => {
|
|
18854
|
+
clearTimeout(timer);
|
|
18855
|
+
resolve(code ?? -1);
|
|
18856
|
+
});
|
|
18857
|
+
});
|
|
18858
|
+
const seconds = (Date.now() - started) / 1e3;
|
|
18859
|
+
if (signal.aborted) return { ok: false, usage: { seconds }, error: "stopped_by_owner" };
|
|
18860
|
+
outDir = await (0, import_promises12.mkdtemp)((0, import_node_path10.join)((0, import_node_os6.tmpdir)(), "bitpull-out-"));
|
|
18861
|
+
await docker(["cp", `${name}:/work/out/.`, outDir]).catch(() => void 0);
|
|
18862
|
+
return {
|
|
18863
|
+
ok: true,
|
|
18864
|
+
result: { exitCode, stdout: stdout.text(), stderr: stderr.text(), outputs: await collectOutputs(outDir) },
|
|
18865
|
+
usage: { seconds }
|
|
18866
|
+
};
|
|
18867
|
+
} catch (err) {
|
|
18868
|
+
return { ok: false, usage: {}, error: `sandbox_error: ${String(err.stderr || err.message).trim().slice(0, 300)}` };
|
|
18869
|
+
} finally {
|
|
18870
|
+
await docker(["rm", "-f", name], 6e4).catch(() => void 0);
|
|
18871
|
+
await docker(["volume", "rm", "-f", volume], 6e4).catch(() => void 0);
|
|
18872
|
+
if (outDir) await (0, import_promises12.rm)(outDir, { recursive: true, force: true });
|
|
18873
|
+
}
|
|
18874
|
+
}
|
|
18875
|
+
async function runFolderJob(spec, sandbox, signal) {
|
|
18754
18876
|
const workdir = await (0, import_promises12.mkdtemp)((0, import_node_path10.join)((0, import_node_os6.tmpdir)(), "bitpull-job-"));
|
|
18755
18877
|
const name = `bitpull-${(0, import_node_crypto6.randomBytes)(6).toString("hex")}`;
|
|
18756
|
-
|
|
18878
|
+
let started = Date.now();
|
|
18757
18879
|
try {
|
|
18758
18880
|
for (const [path, b64] of Object.entries(spec.inputs ?? {})) {
|
|
18759
18881
|
const rel = normalizeRepoPath(path);
|
|
@@ -18765,7 +18887,8 @@ async function runContainer(spec, sandbox, signal) {
|
|
|
18765
18887
|
await (0, import_promises12.chmod)(workdir, 511);
|
|
18766
18888
|
await (0, import_promises12.chmod)((0, import_node_path10.join)(workdir, "out"), 511);
|
|
18767
18889
|
const pulled = await ensureImage(sandbox, spec.image);
|
|
18768
|
-
if (!pulled.ok) return { ok: false, usage: {
|
|
18890
|
+
if (!pulled.ok) return { ok: false, usage: {}, error: `sandbox_error: ${pulled.error}` };
|
|
18891
|
+
started = Date.now();
|
|
18769
18892
|
const stdout = capped();
|
|
18770
18893
|
const stderr = capped();
|
|
18771
18894
|
const exitCode = await new Promise((resolve, reject) => {
|
|
@@ -19131,7 +19254,7 @@ async function doctor(cfg) {
|
|
|
19131
19254
|
}
|
|
19132
19255
|
|
|
19133
19256
|
// apps/agent/src/control.ts
|
|
19134
|
-
var APP_VERSION = true ? "0.1.
|
|
19257
|
+
var APP_VERSION = true ? "0.1.12" : "dev";
|
|
19135
19258
|
var UPDATE_MANIFEST = "https://github.com/SamuealPK/bitpull-releases/releases/latest/download/latest.json";
|
|
19136
19259
|
var inDesktopApp = () => /bitpull-agent(\.exe)?$/i.test(process.execPath);
|
|
19137
19260
|
function newerVersion(a, b) {
|
|
@@ -19273,7 +19396,8 @@ function startControlServer(ctx, node, port = ctx.config.controlPort) {
|
|
|
19273
19396
|
const home = ctx.home ?? bitpullHome();
|
|
19274
19397
|
const status = await (0, import_promises15.readFile)((0, import_node_path12.join)(home, "update-status.json"), "utf8").then((x) => JSON.parse(x)).catch(() => null);
|
|
19275
19398
|
const latest = await fetch(UPDATE_MANIFEST, { signal: AbortSignal.timeout(8e3) }).then((r) => r.ok ? r.json() : null).then((m) => m?.version ?? null).catch(() => null);
|
|
19276
|
-
|
|
19399
|
+
const shown = latest && APP_VERSION !== "dev" && newerVersion(APP_VERSION, latest) ? APP_VERSION : latest;
|
|
19400
|
+
return { current: APP_VERSION, latest: shown, available: !!latest && APP_VERSION !== "dev" && newerVersion(latest, APP_VERSION), inApp: inDesktopApp(), status };
|
|
19277
19401
|
},
|
|
19278
19402
|
/** Asks the desktop app to check for an update and install it now. */
|
|
19279
19403
|
"POST /api/update": async () => {
|
|
@@ -30550,7 +30674,7 @@ function openBrowser(url) {
|
|
|
30550
30674
|
const args = (0, import_node_os10.platform)() === "win32" ? ["/c", "start", "", url] : [url];
|
|
30551
30675
|
(0, import_node_child_process7.spawn)(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
30552
30676
|
}
|
|
30553
|
-
var agentVersion = () => true ? "0.1.
|
|
30677
|
+
var agentVersion = () => true ? "0.1.12" : "dev";
|
|
30554
30678
|
var dashboardUrl = (cfg) => `http://127.0.0.1:${cfg.controlPort}/#token=${cfg.controlToken}`;
|
|
30555
30679
|
var AppNotRunning = class extends Error {
|
|
30556
30680
|
};
|
package/package.json
CHANGED