wildcrane-runner 0.6.14 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/akb/dist/kanban.mjs +261 -252
- package/dist/akb/package.json +1 -1
- package/dist/launcher.mjs +2 -2
- package/dist/runner.mjs +16 -7
- package/package.json +1 -1
package/dist/akb/dist/kanban.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// ai4kanban 0.11.
|
|
2
|
+
// ai4kanban 0.11.12 — built by runner/scripts/build.mjs. Do not edit.
|
|
3
3
|
import { createRequire as __ai4kanbanRequire } from 'node:module'
|
|
4
4
|
const require = __ai4kanbanRequire(import.meta.url)
|
|
5
5
|
var __create = Object.create;
|
|
@@ -3754,192 +3754,6 @@ var init_env = __esm({
|
|
|
3754
3754
|
}
|
|
3755
3755
|
});
|
|
3756
3756
|
|
|
3757
|
-
// ../cli/src/lib/lock.ts
|
|
3758
|
-
import { randomUUID } from "node:crypto";
|
|
3759
|
-
import fs4 from "node:fs";
|
|
3760
|
-
import path5 from "node:path";
|
|
3761
|
-
function sleep(ms) {
|
|
3762
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
3763
|
-
}
|
|
3764
|
-
function ageOf(dir) {
|
|
3765
|
-
try {
|
|
3766
|
-
return Date.now() - fs4.statSync(dir).mtimeMs;
|
|
3767
|
-
} catch {
|
|
3768
|
-
return 0;
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
function pidAlive(pid) {
|
|
3772
|
-
if (!pid) return false;
|
|
3773
|
-
try {
|
|
3774
|
-
process.kill(pid, 0);
|
|
3775
|
-
return true;
|
|
3776
|
-
} catch (e) {
|
|
3777
|
-
return e.code === "EPERM";
|
|
3778
|
-
}
|
|
3779
|
-
}
|
|
3780
|
-
function ownerOf(dir) {
|
|
3781
|
-
try {
|
|
3782
|
-
const pid = Number(fs4.readFileSync(path5.join(dir, OWNER), "utf8").trim());
|
|
3783
|
-
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
3784
|
-
} catch {
|
|
3785
|
-
return void 0;
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
function claimBreak(dir) {
|
|
3789
|
-
try {
|
|
3790
|
-
fs4.mkdirSync(path5.join(dir, BREAKING));
|
|
3791
|
-
return true;
|
|
3792
|
-
} catch {
|
|
3793
|
-
return false;
|
|
3794
|
-
}
|
|
3795
|
-
}
|
|
3796
|
-
function removeClaim(dir, token3) {
|
|
3797
|
-
if (token3) {
|
|
3798
|
-
try {
|
|
3799
|
-
fs4.unlinkSync(path5.join(dir, token3));
|
|
3800
|
-
} catch (err) {
|
|
3801
|
-
if (err.code !== "ENOENT") throw err;
|
|
3802
|
-
}
|
|
3803
|
-
}
|
|
3804
|
-
try {
|
|
3805
|
-
fs4.rmdirSync(dir);
|
|
3806
|
-
return true;
|
|
3807
|
-
} catch (err) {
|
|
3808
|
-
const code = err.code;
|
|
3809
|
-
if (code === "ENOENT") return true;
|
|
3810
|
-
if (code === "ENOTEMPTY" || code === "EEXIST") return false;
|
|
3811
|
-
throw err;
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
3814
|
-
function tryLock(dir) {
|
|
3815
|
-
fs4.mkdirSync(path5.dirname(dir), { recursive: true });
|
|
3816
|
-
const token3 = `${process.pid}-${randomUUID()}`;
|
|
3817
|
-
const candidate = fs4.mkdtempSync(`${dir}.`);
|
|
3818
|
-
try {
|
|
3819
|
-
fs4.writeFileSync(path5.join(candidate, token3), "");
|
|
3820
|
-
for (; ; ) {
|
|
3821
|
-
try {
|
|
3822
|
-
fs4.renameSync(candidate, dir);
|
|
3823
|
-
return () => {
|
|
3824
|
-
removeClaim(dir, token3);
|
|
3825
|
-
};
|
|
3826
|
-
} catch (err) {
|
|
3827
|
-
const code = err.code;
|
|
3828
|
-
if (code !== "ENOTEMPTY" && code !== "EEXIST") throw err;
|
|
3829
|
-
}
|
|
3830
|
-
let entries;
|
|
3831
|
-
try {
|
|
3832
|
-
entries = fs4.readdirSync(dir);
|
|
3833
|
-
} catch (err) {
|
|
3834
|
-
if (err.code === "ENOENT") continue;
|
|
3835
|
-
throw err;
|
|
3836
|
-
}
|
|
3837
|
-
if (entries.length) {
|
|
3838
|
-
if (entries.length !== 1 || !/^\d+-[a-f0-9-]{36}$/.test(entries[0])) return void 0;
|
|
3839
|
-
const owner = Number(entries[0].split("-")[0]);
|
|
3840
|
-
if (pidAlive(owner)) return void 0;
|
|
3841
|
-
}
|
|
3842
|
-
if (!removeClaim(dir, entries[0])) return void 0;
|
|
3843
|
-
}
|
|
3844
|
-
} finally {
|
|
3845
|
-
fs4.rmSync(candidate, { recursive: true, force: true });
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
function withLock(dir, what, fn) {
|
|
3849
|
-
const until = Date.now() + WAIT_MS;
|
|
3850
|
-
let held4;
|
|
3851
|
-
for (; ; ) {
|
|
3852
|
-
try {
|
|
3853
|
-
fs4.mkdirSync(dir, { recursive: false });
|
|
3854
|
-
try {
|
|
3855
|
-
fs4.writeFileSync(path5.join(dir, OWNER), `${process.pid}
|
|
3856
|
-
`);
|
|
3857
|
-
} catch {
|
|
3858
|
-
}
|
|
3859
|
-
break;
|
|
3860
|
-
} catch (err) {
|
|
3861
|
-
const code = err.code;
|
|
3862
|
-
if (code === "ENOENT") {
|
|
3863
|
-
fs4.mkdirSync(path5.dirname(dir), { recursive: true });
|
|
3864
|
-
continue;
|
|
3865
|
-
}
|
|
3866
|
-
if (code !== "EEXIST") throw err;
|
|
3867
|
-
if (ageOf(dir) > STALE_MS) {
|
|
3868
|
-
fs4.rmSync(dir, { recursive: true, force: true });
|
|
3869
|
-
continue;
|
|
3870
|
-
}
|
|
3871
|
-
held4 = ownerOf(dir);
|
|
3872
|
-
if (held4 !== void 0 && !pidAlive(held4) && claimBreak(dir)) {
|
|
3873
|
-
fs4.rmSync(dir, { recursive: true, force: true });
|
|
3874
|
-
continue;
|
|
3875
|
-
}
|
|
3876
|
-
if (Date.now() > until) {
|
|
3877
|
-
die(
|
|
3878
|
-
`another command is ${what}${held4 ? ` (process ${held4})` : ""} and did not finish in ${WAIT_MS / 1e3}s. If nothing else is running, remove ${rel(dir)}/ and try again.`,
|
|
3879
|
-
"board-busy"
|
|
3880
|
-
);
|
|
3881
|
-
}
|
|
3882
|
-
sleep(RETRY_MS);
|
|
3883
|
-
}
|
|
3884
|
-
}
|
|
3885
|
-
try {
|
|
3886
|
-
return fn();
|
|
3887
|
-
} finally {
|
|
3888
|
-
const owner = ownerOf(dir);
|
|
3889
|
-
if (owner === void 0 || owner === process.pid) fs4.rmSync(dir, { recursive: true, force: true });
|
|
3890
|
-
}
|
|
3891
|
-
}
|
|
3892
|
-
function withBoardLock(fn) {
|
|
3893
|
-
if (!fs4.existsSync(KANBAN)) return fn();
|
|
3894
|
-
if (depth > 0) return fn();
|
|
3895
|
-
try {
|
|
3896
|
-
ensureAkbDir();
|
|
3897
|
-
} catch {
|
|
3898
|
-
}
|
|
3899
|
-
depth++;
|
|
3900
|
-
try {
|
|
3901
|
-
return withLock(LOCK, "writing this board", fn);
|
|
3902
|
-
} finally {
|
|
3903
|
-
depth--;
|
|
3904
|
-
}
|
|
3905
|
-
}
|
|
3906
|
-
var WAIT_MS, STALE_MS, RETRY_MS, OWNER, BREAKING, depth;
|
|
3907
|
-
var init_lock = __esm({
|
|
3908
|
-
"../cli/src/lib/lock.ts"() {
|
|
3909
|
-
"use strict";
|
|
3910
|
-
init_paths();
|
|
3911
|
-
WAIT_MS = 1e4;
|
|
3912
|
-
STALE_MS = 6e4;
|
|
3913
|
-
RETRY_MS = 25;
|
|
3914
|
-
OWNER = "owner";
|
|
3915
|
-
BREAKING = "breaking";
|
|
3916
|
-
depth = 0;
|
|
3917
|
-
}
|
|
3918
|
-
});
|
|
3919
|
-
|
|
3920
|
-
// ../cli/src/lib/agent/creation-lock.ts
|
|
3921
|
-
import path6 from "node:path";
|
|
3922
|
-
function withCreationLock(fn) {
|
|
3923
|
-
if (depth2) return fn();
|
|
3924
|
-
return withLock(path6.join(SESSIONS_DIR, "creation.lock"), "changing card creation", () => {
|
|
3925
|
-
depth2++;
|
|
3926
|
-
try {
|
|
3927
|
-
return fn();
|
|
3928
|
-
} finally {
|
|
3929
|
-
depth2--;
|
|
3930
|
-
}
|
|
3931
|
-
});
|
|
3932
|
-
}
|
|
3933
|
-
var depth2;
|
|
3934
|
-
var init_creation_lock = __esm({
|
|
3935
|
-
"../cli/src/lib/agent/creation-lock.ts"() {
|
|
3936
|
-
"use strict";
|
|
3937
|
-
init_lock();
|
|
3938
|
-
init_paths();
|
|
3939
|
-
depth2 = 0;
|
|
3940
|
-
}
|
|
3941
|
-
});
|
|
3942
|
-
|
|
3943
3757
|
// ../cli/src/lib/cloud/config.ts
|
|
3944
3758
|
function cloudEndpoints() {
|
|
3945
3759
|
return {
|
|
@@ -3965,12 +3779,12 @@ var init_config = __esm({
|
|
|
3965
3779
|
});
|
|
3966
3780
|
|
|
3967
3781
|
// ../cli/src/lib/cloud/session.ts
|
|
3968
|
-
import
|
|
3969
|
-
import
|
|
3782
|
+
import fs4 from "node:fs";
|
|
3783
|
+
import path5 from "node:path";
|
|
3970
3784
|
function readSession() {
|
|
3971
3785
|
let raw;
|
|
3972
3786
|
try {
|
|
3973
|
-
raw =
|
|
3787
|
+
raw = fs4.readFileSync(sessionFile(), "utf8");
|
|
3974
3788
|
} catch {
|
|
3975
3789
|
return null;
|
|
3976
3790
|
}
|
|
@@ -3984,15 +3798,15 @@ function readSession() {
|
|
|
3984
3798
|
}
|
|
3985
3799
|
}
|
|
3986
3800
|
function writeSession(session) {
|
|
3987
|
-
|
|
3801
|
+
fs4.mkdirSync(machineHome(), { recursive: true, mode: 448 });
|
|
3988
3802
|
const tmp = `${sessionFile()}.${process.pid}.tmp`;
|
|
3989
|
-
|
|
3803
|
+
fs4.writeFileSync(tmp, `${JSON.stringify(session, null, 2)}
|
|
3990
3804
|
`, { mode: 384 });
|
|
3991
|
-
|
|
3805
|
+
fs4.renameSync(tmp, sessionFile());
|
|
3992
3806
|
}
|
|
3993
3807
|
function clearSession() {
|
|
3994
3808
|
try {
|
|
3995
|
-
|
|
3809
|
+
fs4.rmSync(sessionFile());
|
|
3996
3810
|
} catch {
|
|
3997
3811
|
}
|
|
3998
3812
|
}
|
|
@@ -4057,42 +3871,42 @@ async function takeLock() {
|
|
|
4057
3871
|
const until = Date.now() + LOCK_WAIT_MS;
|
|
4058
3872
|
for (; ; ) {
|
|
4059
3873
|
try {
|
|
4060
|
-
|
|
4061
|
-
|
|
3874
|
+
fs4.mkdirSync(machineHome(), { recursive: true, mode: 448 });
|
|
3875
|
+
fs4.mkdirSync(lockDir());
|
|
4062
3876
|
try {
|
|
4063
|
-
|
|
3877
|
+
fs4.writeFileSync(lockOwner(), `${process.pid}
|
|
4064
3878
|
`);
|
|
4065
3879
|
} catch {
|
|
4066
3880
|
}
|
|
4067
3881
|
return () => {
|
|
4068
3882
|
try {
|
|
4069
|
-
|
|
3883
|
+
fs4.rmSync(lockDir(), { recursive: true, force: true });
|
|
4070
3884
|
} catch {
|
|
4071
3885
|
}
|
|
4072
3886
|
};
|
|
4073
3887
|
} catch (err) {
|
|
4074
3888
|
if (err.code !== "EEXIST") throw err;
|
|
4075
3889
|
if (stale()) {
|
|
4076
|
-
|
|
3890
|
+
fs4.rmSync(lockDir(), { recursive: true, force: true });
|
|
4077
3891
|
continue;
|
|
4078
3892
|
}
|
|
4079
3893
|
if (Date.now() > until) return () => {
|
|
4080
3894
|
};
|
|
4081
|
-
await
|
|
3895
|
+
await sleep(LOCK_RETRY_MS);
|
|
4082
3896
|
}
|
|
4083
3897
|
}
|
|
4084
3898
|
}
|
|
4085
3899
|
function stale() {
|
|
4086
3900
|
let held4;
|
|
4087
3901
|
try {
|
|
4088
|
-
const pid = Number(
|
|
3902
|
+
const pid = Number(fs4.readFileSync(lockOwner(), "utf8").trim());
|
|
4089
3903
|
held4 = Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4090
3904
|
} catch {
|
|
4091
3905
|
held4 = void 0;
|
|
4092
3906
|
}
|
|
4093
3907
|
if (held4 !== void 0 && !alive(held4)) return true;
|
|
4094
3908
|
try {
|
|
4095
|
-
return Date.now() -
|
|
3909
|
+
return Date.now() - fs4.statSync(lockDir()).mtimeMs > LOCK_STALE_MS;
|
|
4096
3910
|
} catch {
|
|
4097
3911
|
return false;
|
|
4098
3912
|
}
|
|
@@ -4105,7 +3919,7 @@ function alive(pid) {
|
|
|
4105
3919
|
return e.code === "EPERM";
|
|
4106
3920
|
}
|
|
4107
3921
|
}
|
|
4108
|
-
var REFRESH_MARGIN_MS, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_RETRY_MS, sessionFile, lockDir, lockOwner, expiringSoon,
|
|
3922
|
+
var REFRESH_MARGIN_MS, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_RETRY_MS, sessionFile, lockDir, lockOwner, expiringSoon, sleep;
|
|
4109
3923
|
var init_session = __esm({
|
|
4110
3924
|
"../cli/src/lib/cloud/session.ts"() {
|
|
4111
3925
|
"use strict";
|
|
@@ -4115,11 +3929,234 @@ var init_session = __esm({
|
|
|
4115
3929
|
LOCK_STALE_MS = 3e4;
|
|
4116
3930
|
LOCK_WAIT_MS = 2e4;
|
|
4117
3931
|
LOCK_RETRY_MS = 50;
|
|
4118
|
-
sessionFile = () =>
|
|
4119
|
-
lockDir = () =>
|
|
4120
|
-
lockOwner = () =>
|
|
3932
|
+
sessionFile = () => path5.join(machineHome(), "session.json");
|
|
3933
|
+
lockDir = () => path5.join(machineHome(), "session.lock");
|
|
3934
|
+
lockOwner = () => path5.join(lockDir(), "owner");
|
|
4121
3935
|
expiringSoon = (session) => session.expiresAt - REFRESH_MARGIN_MS <= Date.now();
|
|
4122
|
-
|
|
3936
|
+
sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
3937
|
+
}
|
|
3938
|
+
});
|
|
3939
|
+
|
|
3940
|
+
// ../cli/src/lib/cloud/execution.ts
|
|
3941
|
+
function executionOf(value = process.env.AI4KANBAN_EXECUTION) {
|
|
3942
|
+
try {
|
|
3943
|
+
const held4 = typeof value === "string" ? JSON.parse(value) : value;
|
|
3944
|
+
if (held4 && UUID.test(held4.job) && UUID.test(held4.machine)) return { job: held4.job, machine: held4.machine };
|
|
3945
|
+
} catch {
|
|
3946
|
+
}
|
|
3947
|
+
return void 0;
|
|
3948
|
+
}
|
|
3949
|
+
async function executionHeld(execution) {
|
|
3950
|
+
if (!execution) return true;
|
|
3951
|
+
try {
|
|
3952
|
+
const auth = await accessToken();
|
|
3953
|
+
if (!auth.ok) return false;
|
|
3954
|
+
const response = await fetch(`${cloudEndpoints().api}/v1/runner/jobs/${execution.job}/renew`, {
|
|
3955
|
+
method: "POST",
|
|
3956
|
+
headers: { authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
|
|
3957
|
+
body: JSON.stringify({ machineId: execution.machine }),
|
|
3958
|
+
signal: AbortSignal.timeout(2e4)
|
|
3959
|
+
});
|
|
3960
|
+
if (!response.ok) return false;
|
|
3961
|
+
const held4 = await response.json();
|
|
3962
|
+
return held4.renewed === true && held4.stop !== true;
|
|
3963
|
+
} catch {
|
|
3964
|
+
return false;
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
var UUID;
|
|
3968
|
+
var init_execution = __esm({
|
|
3969
|
+
"../cli/src/lib/cloud/execution.ts"() {
|
|
3970
|
+
"use strict";
|
|
3971
|
+
init_config();
|
|
3972
|
+
init_session();
|
|
3973
|
+
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3974
|
+
}
|
|
3975
|
+
});
|
|
3976
|
+
|
|
3977
|
+
// ../cli/src/lib/lock.ts
|
|
3978
|
+
import { randomUUID } from "node:crypto";
|
|
3979
|
+
import fs5 from "node:fs";
|
|
3980
|
+
import path6 from "node:path";
|
|
3981
|
+
function sleep2(ms) {
|
|
3982
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
3983
|
+
}
|
|
3984
|
+
function ageOf(dir) {
|
|
3985
|
+
try {
|
|
3986
|
+
return Date.now() - fs5.statSync(dir).mtimeMs;
|
|
3987
|
+
} catch {
|
|
3988
|
+
return 0;
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
function pidAlive(pid) {
|
|
3992
|
+
if (!pid) return false;
|
|
3993
|
+
try {
|
|
3994
|
+
process.kill(pid, 0);
|
|
3995
|
+
return true;
|
|
3996
|
+
} catch (e) {
|
|
3997
|
+
return e.code === "EPERM";
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
function ownerOf(dir) {
|
|
4001
|
+
try {
|
|
4002
|
+
const pid = Number(fs5.readFileSync(path6.join(dir, OWNER), "utf8").trim());
|
|
4003
|
+
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
4004
|
+
} catch {
|
|
4005
|
+
return void 0;
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
function claimBreak(dir) {
|
|
4009
|
+
try {
|
|
4010
|
+
fs5.mkdirSync(path6.join(dir, BREAKING));
|
|
4011
|
+
return true;
|
|
4012
|
+
} catch {
|
|
4013
|
+
return false;
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
function removeClaim(dir, token3) {
|
|
4017
|
+
if (token3) {
|
|
4018
|
+
try {
|
|
4019
|
+
fs5.unlinkSync(path6.join(dir, token3));
|
|
4020
|
+
} catch (err) {
|
|
4021
|
+
if (err.code !== "ENOENT") throw err;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
try {
|
|
4025
|
+
fs5.rmdirSync(dir);
|
|
4026
|
+
return true;
|
|
4027
|
+
} catch (err) {
|
|
4028
|
+
const code = err.code;
|
|
4029
|
+
if (code === "ENOENT") return true;
|
|
4030
|
+
if (code === "ENOTEMPTY" || code === "EEXIST") return false;
|
|
4031
|
+
throw err;
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
function tryLock(dir) {
|
|
4035
|
+
fs5.mkdirSync(path6.dirname(dir), { recursive: true });
|
|
4036
|
+
const token3 = `${process.pid}-${randomUUID()}`;
|
|
4037
|
+
const candidate = fs5.mkdtempSync(`${dir}.`);
|
|
4038
|
+
try {
|
|
4039
|
+
fs5.writeFileSync(path6.join(candidate, token3), "");
|
|
4040
|
+
for (; ; ) {
|
|
4041
|
+
try {
|
|
4042
|
+
fs5.renameSync(candidate, dir);
|
|
4043
|
+
return () => {
|
|
4044
|
+
removeClaim(dir, token3);
|
|
4045
|
+
};
|
|
4046
|
+
} catch (err) {
|
|
4047
|
+
const code = err.code;
|
|
4048
|
+
if (code !== "ENOTEMPTY" && code !== "EEXIST") throw err;
|
|
4049
|
+
}
|
|
4050
|
+
let entries;
|
|
4051
|
+
try {
|
|
4052
|
+
entries = fs5.readdirSync(dir);
|
|
4053
|
+
} catch (err) {
|
|
4054
|
+
if (err.code === "ENOENT") continue;
|
|
4055
|
+
throw err;
|
|
4056
|
+
}
|
|
4057
|
+
if (entries.length) {
|
|
4058
|
+
if (entries.length !== 1 || !/^\d+-[a-f0-9-]{36}$/.test(entries[0])) return void 0;
|
|
4059
|
+
const owner = Number(entries[0].split("-")[0]);
|
|
4060
|
+
if (pidAlive(owner)) return void 0;
|
|
4061
|
+
}
|
|
4062
|
+
if (!removeClaim(dir, entries[0])) return void 0;
|
|
4063
|
+
}
|
|
4064
|
+
} finally {
|
|
4065
|
+
fs5.rmSync(candidate, { recursive: true, force: true });
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
function withLock(dir, what, fn) {
|
|
4069
|
+
const until = Date.now() + WAIT_MS;
|
|
4070
|
+
let held4;
|
|
4071
|
+
for (; ; ) {
|
|
4072
|
+
try {
|
|
4073
|
+
fs5.mkdirSync(dir, { recursive: false });
|
|
4074
|
+
try {
|
|
4075
|
+
fs5.writeFileSync(path6.join(dir, OWNER), `${process.pid}
|
|
4076
|
+
`);
|
|
4077
|
+
} catch {
|
|
4078
|
+
}
|
|
4079
|
+
break;
|
|
4080
|
+
} catch (err) {
|
|
4081
|
+
const code = err.code;
|
|
4082
|
+
if (code === "ENOENT") {
|
|
4083
|
+
fs5.mkdirSync(path6.dirname(dir), { recursive: true });
|
|
4084
|
+
continue;
|
|
4085
|
+
}
|
|
4086
|
+
if (code !== "EEXIST") throw err;
|
|
4087
|
+
if (ageOf(dir) > STALE_MS) {
|
|
4088
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
4089
|
+
continue;
|
|
4090
|
+
}
|
|
4091
|
+
held4 = ownerOf(dir);
|
|
4092
|
+
if (held4 !== void 0 && !pidAlive(held4) && claimBreak(dir)) {
|
|
4093
|
+
fs5.rmSync(dir, { recursive: true, force: true });
|
|
4094
|
+
continue;
|
|
4095
|
+
}
|
|
4096
|
+
if (Date.now() > until) {
|
|
4097
|
+
die(
|
|
4098
|
+
`another command is ${what}${held4 ? ` (process ${held4})` : ""} and did not finish in ${WAIT_MS / 1e3}s. If nothing else is running, remove ${rel(dir)}/ and try again.`,
|
|
4099
|
+
"board-busy"
|
|
4100
|
+
);
|
|
4101
|
+
}
|
|
4102
|
+
sleep2(RETRY_MS);
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
try {
|
|
4106
|
+
return fn();
|
|
4107
|
+
} finally {
|
|
4108
|
+
const owner = ownerOf(dir);
|
|
4109
|
+
if (owner === void 0 || owner === process.pid) fs5.rmSync(dir, { recursive: true, force: true });
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
function withBoardLock(fn) {
|
|
4113
|
+
if (!fs5.existsSync(KANBAN)) return fn();
|
|
4114
|
+
if (depth > 0) return fn();
|
|
4115
|
+
try {
|
|
4116
|
+
ensureAkbDir();
|
|
4117
|
+
} catch {
|
|
4118
|
+
}
|
|
4119
|
+
depth++;
|
|
4120
|
+
try {
|
|
4121
|
+
return withLock(LOCK, "writing this board", fn);
|
|
4122
|
+
} finally {
|
|
4123
|
+
depth--;
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
var WAIT_MS, STALE_MS, RETRY_MS, OWNER, BREAKING, depth;
|
|
4127
|
+
var init_lock = __esm({
|
|
4128
|
+
"../cli/src/lib/lock.ts"() {
|
|
4129
|
+
"use strict";
|
|
4130
|
+
init_paths();
|
|
4131
|
+
WAIT_MS = 1e4;
|
|
4132
|
+
STALE_MS = 6e4;
|
|
4133
|
+
RETRY_MS = 25;
|
|
4134
|
+
OWNER = "owner";
|
|
4135
|
+
BREAKING = "breaking";
|
|
4136
|
+
depth = 0;
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
|
|
4140
|
+
// ../cli/src/lib/agent/creation-lock.ts
|
|
4141
|
+
import path7 from "node:path";
|
|
4142
|
+
function withCreationLock(fn) {
|
|
4143
|
+
if (depth2) return fn();
|
|
4144
|
+
return withLock(path7.join(SESSIONS_DIR, "creation.lock"), "changing card creation", () => {
|
|
4145
|
+
depth2++;
|
|
4146
|
+
try {
|
|
4147
|
+
return fn();
|
|
4148
|
+
} finally {
|
|
4149
|
+
depth2--;
|
|
4150
|
+
}
|
|
4151
|
+
});
|
|
4152
|
+
}
|
|
4153
|
+
var depth2;
|
|
4154
|
+
var init_creation_lock = __esm({
|
|
4155
|
+
"../cli/src/lib/agent/creation-lock.ts"() {
|
|
4156
|
+
"use strict";
|
|
4157
|
+
init_lock();
|
|
4158
|
+
init_paths();
|
|
4159
|
+
depth2 = 0;
|
|
4123
4160
|
}
|
|
4124
4161
|
});
|
|
4125
4162
|
|
|
@@ -5477,7 +5514,7 @@ var init_package = __esm({
|
|
|
5477
5514
|
"../cli/package.json"() {
|
|
5478
5515
|
package_default = {
|
|
5479
5516
|
name: "wildcrane",
|
|
5480
|
-
version: "0.11.
|
|
5517
|
+
version: "0.11.12",
|
|
5481
5518
|
akbContract: 5,
|
|
5482
5519
|
description: "WildCrane's `akb` command \u2014 scaffold the Markdown board under docs/kanban/, put an agent on a card, and carry the flows the agent works by.",
|
|
5483
5520
|
keywords: [
|
|
@@ -11586,43 +11623,6 @@ var init_run_settings = __esm({
|
|
|
11586
11623
|
}
|
|
11587
11624
|
});
|
|
11588
11625
|
|
|
11589
|
-
// ../cli/src/lib/cloud/execution.ts
|
|
11590
|
-
function executionOf(value = process.env.AI4KANBAN_EXECUTION) {
|
|
11591
|
-
try {
|
|
11592
|
-
const held4 = typeof value === "string" ? JSON.parse(value) : value;
|
|
11593
|
-
if (held4 && UUID.test(held4.job) && UUID.test(held4.machine)) return { job: held4.job, machine: held4.machine };
|
|
11594
|
-
} catch {
|
|
11595
|
-
}
|
|
11596
|
-
return void 0;
|
|
11597
|
-
}
|
|
11598
|
-
async function executionHeld(execution) {
|
|
11599
|
-
if (!execution) return true;
|
|
11600
|
-
try {
|
|
11601
|
-
const auth = await accessToken();
|
|
11602
|
-
if (!auth.ok) return false;
|
|
11603
|
-
const response = await fetch(`${cloudEndpoints().api}/v1/runner/jobs/${execution.job}/renew`, {
|
|
11604
|
-
method: "POST",
|
|
11605
|
-
headers: { authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
|
|
11606
|
-
body: JSON.stringify({ machineId: execution.machine }),
|
|
11607
|
-
signal: AbortSignal.timeout(2e4)
|
|
11608
|
-
});
|
|
11609
|
-
if (!response.ok) return false;
|
|
11610
|
-
const held4 = await response.json();
|
|
11611
|
-
return held4.renewed === true && held4.stop !== true;
|
|
11612
|
-
} catch {
|
|
11613
|
-
return false;
|
|
11614
|
-
}
|
|
11615
|
-
}
|
|
11616
|
-
var UUID;
|
|
11617
|
-
var init_execution = __esm({
|
|
11618
|
-
"../cli/src/lib/cloud/execution.ts"() {
|
|
11619
|
-
"use strict";
|
|
11620
|
-
init_config();
|
|
11621
|
-
init_session();
|
|
11622
|
-
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11623
|
-
}
|
|
11624
|
-
});
|
|
11625
|
-
|
|
11626
11626
|
// ../cli/src/lib/agent/log.ts
|
|
11627
11627
|
import fs28 from "node:fs";
|
|
11628
11628
|
import path31 from "node:path";
|
|
@@ -16899,21 +16899,27 @@ function sweepCheckouts() {
|
|
|
16899
16899
|
dropEmptyWorktreeFolders();
|
|
16900
16900
|
return complaints;
|
|
16901
16901
|
}
|
|
16902
|
-
function resumeRecord(id) {
|
|
16903
|
-
const
|
|
16902
|
+
function resumeRecord(id, execution) {
|
|
16903
|
+
const continueActive = !!execution;
|
|
16904
|
+
const found2 = (continueActive ? namedDelivery(id) : void 0) ?? endedDelivery(id);
|
|
16904
16905
|
if (!found2) return { ok: false, ...unknownDelivery(id) };
|
|
16905
16906
|
const refused = resumeRefusal(found2);
|
|
16906
|
-
if (refused) return { ok: false, ...refused };
|
|
16907
|
+
if (refused && !(continueActive && found2.status === "active")) return { ok: false, ...refused };
|
|
16907
16908
|
const out = withStore((store) => {
|
|
16908
16909
|
const audit = wholeAudit(auditPath(found2.deliveryId));
|
|
16909
|
-
const
|
|
16910
|
+
const live = store.deliveries.find((d) => d.deliveryId === found2.deliveryId);
|
|
16911
|
+
const row = continueActive && live?.status === "active" ? live : (audit && auditRow(audit)) ?? live;
|
|
16910
16912
|
if (!row) return { ok: false, ...unknownDelivery(id) };
|
|
16911
16913
|
const again = resumeRefusal(row, store);
|
|
16912
|
-
if (
|
|
16914
|
+
if (continueActive && row.status === "active") {
|
|
16915
|
+
if (store.runs.some((r) => r.deliveryId === row.deliveryId && runIsLive(r))) {
|
|
16916
|
+
return { ok: false, ...refusal("deliveryRunGoing", "the delivery still has a running agent; stop it before continuing") };
|
|
16917
|
+
}
|
|
16918
|
+
} else if (again) return { ok: false, ...again };
|
|
16913
16919
|
const at = Date.now();
|
|
16914
16920
|
const resumed = {
|
|
16915
16921
|
...row,
|
|
16916
|
-
cloudExecution: executionOf() ?? row.cloudExecution,
|
|
16922
|
+
cloudExecution: execution ?? executionOf() ?? row.cloudExecution,
|
|
16917
16923
|
status: "active",
|
|
16918
16924
|
endedAt: void 0,
|
|
16919
16925
|
steps: [...row.steps, { step: "resume", at }],
|
|
@@ -19463,7 +19469,9 @@ async function runIsDown(sessionId) {
|
|
|
19463
19469
|
}
|
|
19464
19470
|
async function resumeDelivery(id) {
|
|
19465
19471
|
if (!id.trim()) return { ok: false, ...refusal("deliveryUnnamed", "name the delivery to carry on", { action: "resume" }) };
|
|
19466
|
-
const
|
|
19472
|
+
const execution = executionOf();
|
|
19473
|
+
if (execution && !await executionHeld(execution)) return { ok: false, error: "\u65E0\u6CD5\u786E\u8BA4\u672C\u6B21\u7EED\u8DD1\u7684\u6267\u884C\u6743\uFF0C\u4EA4\u4ED8\u4FDD\u6301\u539F\u72B6\u3002" };
|
|
19474
|
+
const put = resumeRecord(id, execution);
|
|
19467
19475
|
if (!put.ok) return put;
|
|
19468
19476
|
const delivery = put.delivery;
|
|
19469
19477
|
if (await settleAlreadyLanded(delivery)) {
|
|
@@ -19557,6 +19565,7 @@ var INDEX_ACTIONS, SINGLETON_ACTIONS, VERB, SINGLETON_BUSY, RUN_STATUS, specPath
|
|
|
19557
19565
|
var init_sessions = __esm({
|
|
19558
19566
|
"../cli/src/lib/agent/sessions.ts"() {
|
|
19559
19567
|
"use strict";
|
|
19568
|
+
init_execution();
|
|
19560
19569
|
init_creation();
|
|
19561
19570
|
init_cards();
|
|
19562
19571
|
init_frontmatter();
|
package/dist/akb/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"wildcrane","version":"0.11.
|
|
1
|
+
{"name":"wildcrane","version":"0.11.12","akbContract":5,"type":"module","private":true}
|
package/dist/launcher.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// wildcrane-runner 0.6.
|
|
1
|
+
// wildcrane-runner 0.6.15 — built by runner/scripts/build.mjs. Do not edit.
|
|
2
2
|
import { createRequire as __ai4kanbanRequire } from 'node:module'
|
|
3
3
|
const require = __ai4kanbanRequire(import.meta.url)
|
|
4
4
|
|
|
@@ -107,7 +107,7 @@ function status(line) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/protocol.ts
|
|
110
|
-
var VERSION = true ? "0.6.
|
|
110
|
+
var VERSION = true ? "0.6.15" : "0.0.0";
|
|
111
111
|
var EXIT_REFUSED = 10;
|
|
112
112
|
var EXIT_OUTDATED = 11;
|
|
113
113
|
var EXIT_UPDATE = 12;
|
package/dist/runner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// wildcrane-runner 0.6.
|
|
1
|
+
// wildcrane-runner 0.6.15 — built by runner/scripts/build.mjs. Do not edit.
|
|
2
2
|
import { createRequire as __ai4kanbanRequire } from 'node:module'
|
|
3
3
|
const require = __ai4kanbanRequire(import.meta.url)
|
|
4
4
|
var __create = Object.create;
|
|
@@ -84435,8 +84435,8 @@ function status(line) {
|
|
|
84435
84435
|
}
|
|
84436
84436
|
|
|
84437
84437
|
// src/protocol.ts
|
|
84438
|
-
var VERSION = true ? "0.6.
|
|
84439
|
-
var AKB_VERSION = true ? "0.11.
|
|
84438
|
+
var VERSION = true ? "0.6.15" : "0.0.0";
|
|
84439
|
+
var AKB_VERSION = true ? "0.11.12" : "0.0.0";
|
|
84440
84440
|
var AKB_CONTRACT = true ? 5 : 0;
|
|
84441
84441
|
var EXIT_REFUSED = 10;
|
|
84442
84442
|
var EXIT_OUTDATED = 11;
|
|
@@ -85923,7 +85923,7 @@ function createTail(log) {
|
|
|
85923
85923
|
// ../cli/package.json
|
|
85924
85924
|
var package_default = {
|
|
85925
85925
|
name: "wildcrane",
|
|
85926
|
-
version: "0.11.
|
|
85926
|
+
version: "0.11.12",
|
|
85927
85927
|
akbContract: 5,
|
|
85928
85928
|
description: "WildCrane's `akb` command \u2014 scaffold the Markdown board under docs/kanban/, put an agent on a card, and carry the flows the agent works by.",
|
|
85929
85929
|
keywords: [
|
|
@@ -92105,16 +92105,25 @@ function prepareKanbanSkill(root) {
|
|
|
92105
92105
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
92106
92106
|
import fs27 from "node:fs";
|
|
92107
92107
|
import path29 from "node:path";
|
|
92108
|
+
|
|
92109
|
+
// ../cli/src/lib/agent/landing-lock.ts
|
|
92110
|
+
var landingLimits = { staleMs: 5 * 6e4 };
|
|
92111
|
+
|
|
92112
|
+
// ../cli/src/lib/skill/hook.ts
|
|
92108
92113
|
var MARKER = "# ai4kanban commit guard";
|
|
92109
92114
|
var MARKER_LINE = new RegExp(`^${MARKER}\\b`, "m");
|
|
92110
92115
|
var CHECK = [
|
|
92111
92116
|
"try{",
|
|
92112
92117
|
'const[top,branch]=require("child_process")',
|
|
92113
|
-
'.execFileSync("git",["rev-parse","--show-toplevel","--abbrev-ref","HEAD"],{encoding:"utf8"}).trim().split("\\n");',
|
|
92118
|
+
'.execFileSync("git",["rev-parse","--show-toplevel","--abbrev-ref","HEAD"],{encoding:"utf8",timeout:3000,killSignal:"SIGKILL"}).trim().split("\\n");',
|
|
92114
92119
|
'if(/\\/\\.akb\\/worktrees\\//.test(top+"/"))process.exit(0);',
|
|
92115
92120
|
'const d=(JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")).deliveries||[])',
|
|
92116
|
-
'.find((x)=>x&&x.status==="active"&&x.commitMode==="auto"&&x.targetBranch===branch&&
|
|
92121
|
+
'.find((x)=>x&&x.status==="active"&&x.commitMode==="auto"&&x.targetBranch===branch&&x.landing&&x.landing.status==="landing");',
|
|
92117
92122
|
"if(!d)process.exit(0);",
|
|
92123
|
+
'const fs=require("fs"),lock=process.argv[2],entries=fs.readdirSync(lock);',
|
|
92124
|
+
`if(Date.now()-fs.statSync(lock).mtimeMs>${landingLimits.staleMs})process.exit(0);`,
|
|
92125
|
+
"if(entries.length!==1||!/^\\d+-[a-f0-9-]{36}$/.test(entries[0]))process.exit(0);",
|
|
92126
|
+
'try{process.kill(Number(entries[0].split("-")[0]),0)}catch(e){if(e.code!=="EPERM")process.exit(0)}',
|
|
92118
92127
|
"process.stderr.write(",
|
|
92119
92128
|
'"ai4kanban: delivery "+d.deliveryId+" is landing #"+d.cardId+" on "+branch+", so a commit here moves the target under it\\n"',
|
|
92120
92129
|
'+" \u2014 commit on another branch, or `git commit --no-verify` to commit anyway.\\n");',
|
|
@@ -92122,7 +92131,7 @@ var CHECK = [
|
|
|
92122
92131
|
"}catch(e){process.exit(0)}"
|
|
92123
92132
|
].join("");
|
|
92124
92133
|
var quoted = (p2) => `"${p2.replace(/(["$`\\])/g, "\\$1")}"`;
|
|
92125
|
-
var guardLine = (sessions) => `if command -v node >/dev/null 2>&1; then node -e '${CHECK}' ${quoted(sessions)} || exit 1; fi`;
|
|
92134
|
+
var guardLine = (sessions) => `if command -v node >/dev/null 2>&1; then node -e '${CHECK}' ${quoted(sessions)} ${quoted(path29.join(path29.dirname(sessions), SESSIONS_FOLDER, ".landing.lock"))} || exit 1; fi`;
|
|
92126
92135
|
function hookScript(sessions) {
|
|
92127
92136
|
return [
|
|
92128
92137
|
"#!/bin/sh",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wildcrane-runner",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15",
|
|
4
4
|
"description": "WildCrane's Local Runner — connects this computer to WildCrane and runs its jobs on your local Claude Code, Codex, OpenCode or Cursor.",
|
|
5
5
|
"license": "FSL-1.1-ALv2",
|
|
6
6
|
"homepage": "https://wildcrane.ai/",
|