claude-yes 1.34.0 → 1.35.1
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/cli.js +228 -88
- package/dist/index.js +114 -43
- package/package.json +4 -3
- package/ts/parseCliArgs.spec.ts +17 -0
- package/ts/parseCliArgs.ts +2 -1
- package/ts/pidStore.ts +152 -47
package/dist/cli.js
CHANGED
|
@@ -20732,24 +20732,81 @@ var init_fifo = __esm(() => {
|
|
|
20732
20732
|
});
|
|
20733
20733
|
|
|
20734
20734
|
// ts/pidStore.ts
|
|
20735
|
-
import Datastore from "@seald-io/nedb";
|
|
20736
20735
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
20737
20736
|
import path9 from "path";
|
|
20738
20737
|
|
|
20738
|
+
class SqliteAdapter {
|
|
20739
|
+
db;
|
|
20740
|
+
async init(dbPath) {
|
|
20741
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
20742
|
+
try {
|
|
20743
|
+
const { Database } = await import("bun:sqlite");
|
|
20744
|
+
this.db = new Database(dbPath);
|
|
20745
|
+
} catch (error) {
|
|
20746
|
+
logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
|
|
20747
|
+
const Database = (await import("better-sqlite3")).default;
|
|
20748
|
+
this.db = new Database(dbPath);
|
|
20749
|
+
}
|
|
20750
|
+
} else {
|
|
20751
|
+
const Database = (await import("better-sqlite3")).default;
|
|
20752
|
+
this.db = new Database(dbPath);
|
|
20753
|
+
}
|
|
20754
|
+
}
|
|
20755
|
+
query(sql, params = []) {
|
|
20756
|
+
if (typeof this.db.prepare === "function") {
|
|
20757
|
+
return this.db.prepare(sql).all(params);
|
|
20758
|
+
} else {
|
|
20759
|
+
return this.db.query(sql).all(params);
|
|
20760
|
+
}
|
|
20761
|
+
}
|
|
20762
|
+
run(sql, params = []) {
|
|
20763
|
+
if (typeof this.db.prepare === "function") {
|
|
20764
|
+
return this.db.prepare(sql).run(params);
|
|
20765
|
+
} else {
|
|
20766
|
+
this.db.run(sql, params);
|
|
20767
|
+
return {};
|
|
20768
|
+
}
|
|
20769
|
+
}
|
|
20770
|
+
close() {
|
|
20771
|
+
if (this.db.close) {
|
|
20772
|
+
this.db.close();
|
|
20773
|
+
}
|
|
20774
|
+
}
|
|
20775
|
+
}
|
|
20776
|
+
|
|
20739
20777
|
class PidStore {
|
|
20740
20778
|
db;
|
|
20741
20779
|
baseDir;
|
|
20780
|
+
dbPath;
|
|
20742
20781
|
constructor(workingDir) {
|
|
20743
20782
|
this.baseDir = path9.resolve(workingDir, ".agent-yes");
|
|
20783
|
+
this.dbPath = path9.join(this.baseDir, "pid.sqlite");
|
|
20744
20784
|
}
|
|
20745
20785
|
async init() {
|
|
20746
20786
|
await mkdir3(path9.join(this.baseDir, "logs"), { recursive: true });
|
|
20747
20787
|
await mkdir3(path9.join(this.baseDir, "fifo"), { recursive: true });
|
|
20748
|
-
this.db = new
|
|
20749
|
-
|
|
20750
|
-
|
|
20751
|
-
|
|
20752
|
-
|
|
20788
|
+
this.db = new SqliteAdapter;
|
|
20789
|
+
await this.db.init(this.dbPath);
|
|
20790
|
+
this.db.run("PRAGMA journal_mode=WAL");
|
|
20791
|
+
this.db.run("PRAGMA synchronous=NORMAL");
|
|
20792
|
+
this.db.run("PRAGMA cache_size=1000");
|
|
20793
|
+
this.db.run("PRAGMA temp_store=memory");
|
|
20794
|
+
this.db.run(`
|
|
20795
|
+
CREATE TABLE IF NOT EXISTS pid_records (
|
|
20796
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20797
|
+
pid INTEGER NOT NULL UNIQUE,
|
|
20798
|
+
cli TEXT NOT NULL,
|
|
20799
|
+
args TEXT NOT NULL,
|
|
20800
|
+
prompt TEXT,
|
|
20801
|
+
logFile TEXT NOT NULL,
|
|
20802
|
+
fifoFile TEXT NOT NULL,
|
|
20803
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
20804
|
+
exitReason TEXT NOT NULL DEFAULT '',
|
|
20805
|
+
exitCode INTEGER,
|
|
20806
|
+
startedAt INTEGER NOT NULL,
|
|
20807
|
+
updatedAt INTEGER NOT NULL
|
|
20808
|
+
)
|
|
20809
|
+
`);
|
|
20753
20810
|
await this.cleanStaleRecords();
|
|
20754
20811
|
}
|
|
20755
20812
|
async registerProcess({
|
|
@@ -20759,29 +20816,43 @@ class PidStore {
|
|
|
20759
20816
|
prompt
|
|
20760
20817
|
}) {
|
|
20761
20818
|
const now = Date.now();
|
|
20762
|
-
const
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
20767
|
-
|
|
20768
|
-
|
|
20769
|
-
|
|
20770
|
-
|
|
20771
|
-
|
|
20772
|
-
|
|
20773
|
-
|
|
20774
|
-
|
|
20819
|
+
const argsJson = JSON.stringify(args);
|
|
20820
|
+
const logFile = this.getLogPath(pid);
|
|
20821
|
+
const fifoFile = this.getFifoPath(pid);
|
|
20822
|
+
try {
|
|
20823
|
+
this.db.run(`
|
|
20824
|
+
INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
|
|
20825
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
|
|
20826
|
+
`, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
|
|
20827
|
+
} catch (error) {
|
|
20828
|
+
if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
|
|
20829
|
+
this.db.run(`
|
|
20830
|
+
UPDATE pid_records
|
|
20831
|
+
SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
|
|
20832
|
+
WHERE pid = ?
|
|
20833
|
+
`, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
|
|
20834
|
+
} else {
|
|
20835
|
+
throw error;
|
|
20836
|
+
}
|
|
20837
|
+
}
|
|
20838
|
+
const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
|
|
20839
|
+
if (!result) {
|
|
20840
|
+
const allRecords = this.db.query("SELECT * FROM pid_records");
|
|
20841
|
+
logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
|
|
20842
|
+
throw new Error(`Failed to register process ${pid}`);
|
|
20843
|
+
}
|
|
20775
20844
|
logger.debug(`[pidStore] Registered process ${pid}`);
|
|
20776
|
-
return
|
|
20845
|
+
return result;
|
|
20777
20846
|
}
|
|
20778
20847
|
async updateStatus(pid, status, extra) {
|
|
20779
|
-
const
|
|
20780
|
-
|
|
20781
|
-
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
20848
|
+
const updatedAt = Date.now();
|
|
20849
|
+
const exitReason = extra?.exitReason || "";
|
|
20850
|
+
const exitCode = extra?.exitCode;
|
|
20851
|
+
if (exitCode !== undefined) {
|
|
20852
|
+
this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, exitCode, updatedAt, pid]);
|
|
20853
|
+
} else {
|
|
20854
|
+
this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, updatedAt, pid]);
|
|
20855
|
+
}
|
|
20785
20856
|
logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
|
|
20786
20857
|
}
|
|
20787
20858
|
getLogPath(pid) {
|
|
@@ -20791,25 +20862,23 @@ class PidStore {
|
|
|
20791
20862
|
return path9.resolve(this.baseDir, "fifo", `${pid}.stdin`);
|
|
20792
20863
|
}
|
|
20793
20864
|
async cleanStaleRecords() {
|
|
20794
|
-
const activeRecords =
|
|
20795
|
-
status: { $ne: "exited" }
|
|
20796
|
-
});
|
|
20865
|
+
const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
|
|
20797
20866
|
for (const record of activeRecords) {
|
|
20798
20867
|
if (!this.isProcessAlive(record.pid)) {
|
|
20799
|
-
|
|
20800
|
-
$set: {
|
|
20801
|
-
status: "exited",
|
|
20802
|
-
exitReason: "stale-cleanup",
|
|
20803
|
-
updatedAt: Date.now()
|
|
20804
|
-
}
|
|
20805
|
-
}, {});
|
|
20868
|
+
this.db.run("UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?", [Date.now(), record.pid]);
|
|
20806
20869
|
logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
|
|
20807
20870
|
}
|
|
20808
20871
|
}
|
|
20809
20872
|
}
|
|
20810
20873
|
async close() {
|
|
20811
|
-
|
|
20812
|
-
|
|
20874
|
+
try {
|
|
20875
|
+
this.db.run("PRAGMA optimize");
|
|
20876
|
+
this.db.run("VACUUM");
|
|
20877
|
+
} catch (error) {
|
|
20878
|
+
logger.warn("[pidStore] Failed to optimize database:", error);
|
|
20879
|
+
}
|
|
20880
|
+
this.db.close();
|
|
20881
|
+
logger.debug("[pidStore] Database optimized and closed");
|
|
20813
20882
|
}
|
|
20814
20883
|
isProcessAlive(pid) {
|
|
20815
20884
|
try {
|
|
@@ -20822,10 +20891,9 @@ class PidStore {
|
|
|
20822
20891
|
static async findActiveFifo(workingDir) {
|
|
20823
20892
|
const store = new PidStore(workingDir);
|
|
20824
20893
|
await store.init();
|
|
20825
|
-
const records =
|
|
20894
|
+
const records = store.db.query("SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1");
|
|
20826
20895
|
await store.close();
|
|
20827
|
-
|
|
20828
|
-
return sorted[0]?.fifoFile ?? null;
|
|
20896
|
+
return records[0]?.fifoFile ?? null;
|
|
20829
20897
|
}
|
|
20830
20898
|
}
|
|
20831
20899
|
var init_pidStore = __esm(() => {
|
|
@@ -20959,7 +21027,10 @@ function getDefaultConfig() {
|
|
|
20959
21027
|
},
|
|
20960
21028
|
amp: {
|
|
20961
21029
|
help: "",
|
|
20962
|
-
install: "npm i -g @sourcegraph/amp"
|
|
21030
|
+
install: "npm i -g @sourcegraph/amp",
|
|
21031
|
+
enter: [
|
|
21032
|
+
/^.{0,4} Approve /
|
|
21033
|
+
]
|
|
20963
21034
|
}
|
|
20964
21035
|
}
|
|
20965
21036
|
});
|
|
@@ -27688,7 +27759,7 @@ var package_default = {
|
|
|
27688
27759
|
registry: "https://registry.npmjs.org/"
|
|
27689
27760
|
},
|
|
27690
27761
|
scripts: {
|
|
27691
|
-
build: "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external
|
|
27762
|
+
build: "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=better-sqlite3 --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
27692
27763
|
postbuild: "bun ./ts/postbuild.ts",
|
|
27693
27764
|
demo: "bun run build && bun link && claude-yes -- demo",
|
|
27694
27765
|
dev: "bun ts/index.ts",
|
|
@@ -27700,9 +27771,9 @@ var package_default = {
|
|
|
27700
27771
|
test: "bun test --coverage"
|
|
27701
27772
|
},
|
|
27702
27773
|
dependencies: {
|
|
27703
|
-
"@seald-io/nedb": "^4.0.4",
|
|
27704
27774
|
"@snomiao/bun-pty": "^0.3.4",
|
|
27705
27775
|
"bun-pty": "^0.4.8",
|
|
27776
|
+
"better-sqlite3": "^12.1.0",
|
|
27706
27777
|
"from-node-stream": "^0.1.2"
|
|
27707
27778
|
},
|
|
27708
27779
|
devDependencies: {
|
|
@@ -27714,6 +27785,7 @@ var package_default = {
|
|
|
27714
27785
|
"@types/bun": "^1.3.6",
|
|
27715
27786
|
"@types/jest": "^30.0.0",
|
|
27716
27787
|
"@types/ms": "^2.1.0",
|
|
27788
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
27717
27789
|
"@types/node": "^25.0.10",
|
|
27718
27790
|
"@types/yargs": "^17.0.35",
|
|
27719
27791
|
"cpu-wait": "^0.0.10",
|
|
@@ -27775,7 +27847,7 @@ var package_default = {
|
|
|
27775
27847
|
|
|
27776
27848
|
// ts/parseCliArgs.ts
|
|
27777
27849
|
function parseCliArgs(argv) {
|
|
27778
|
-
const cliName = argv[1]?.split(/[/\\]/).at(-1)?.replace(/(\.[jt]s)?$/, "").replace(/^(cli|agent)(-yes$)?/, "").replace(/-yes$/, "") || undefined;
|
|
27850
|
+
const cliName = argv[1]?.split(/[/\\]/).at(-1)?.replace(/(\.[jt]s)?$/, "").replace(/^(cli|agent)(-yes$)?/, "").replace(/^ay$/, "").replace(/-yes$/, "") || undefined;
|
|
27779
27851
|
const parsedArgv = yargs_default(hideBin(argv)).usage("Usage: $0 [cli] [agent-yes args] [agent-cli args] [--] [prompts...]").example("$0 claude --idle=30s -- solve all todos in my codebase, commit one by one", "Run Claude with a 30 seconds idle timeout, and the prompt is everything after `--`").option("robust", {
|
|
27780
27852
|
type: "boolean",
|
|
27781
27853
|
default: true,
|
|
@@ -27889,7 +27961,7 @@ function parseCliArgs(argv) {
|
|
|
27889
27961
|
return {
|
|
27890
27962
|
cwd: process.cwd(),
|
|
27891
27963
|
env: process.env,
|
|
27892
|
-
cli: cliName || parsedArgv.cli || parsedArgv._[0]?.toString()?.replace?.(/-yes$/, ""),
|
|
27964
|
+
cli: cliName || parsedArgv.cli || (dashIndex !== 0 ? parsedArgv._[0]?.toString()?.replace?.(/-yes$/, "") : undefined),
|
|
27893
27965
|
cliArgs: cliArgsForSpawn,
|
|
27894
27966
|
prompt: [parsedArgv.prompt, dashPrompt].filter(Boolean).join(" ") || undefined,
|
|
27895
27967
|
install: parsedArgv.install,
|
|
@@ -27924,24 +27996,81 @@ var logger2 = import_winston3.default.createLogger({
|
|
|
27924
27996
|
|
|
27925
27997
|
// ts/pidStore.ts
|
|
27926
27998
|
init_logger();
|
|
27927
|
-
import Datastore2 from "@seald-io/nedb";
|
|
27928
27999
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
27929
28000
|
import path12 from "path";
|
|
27930
28001
|
|
|
28002
|
+
class SqliteAdapter2 {
|
|
28003
|
+
db;
|
|
28004
|
+
async init(dbPath) {
|
|
28005
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
28006
|
+
try {
|
|
28007
|
+
const { Database } = await import("bun:sqlite");
|
|
28008
|
+
this.db = new Database(dbPath);
|
|
28009
|
+
} catch (error) {
|
|
28010
|
+
logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
|
|
28011
|
+
const Database = (await import("better-sqlite3")).default;
|
|
28012
|
+
this.db = new Database(dbPath);
|
|
28013
|
+
}
|
|
28014
|
+
} else {
|
|
28015
|
+
const Database = (await import("better-sqlite3")).default;
|
|
28016
|
+
this.db = new Database(dbPath);
|
|
28017
|
+
}
|
|
28018
|
+
}
|
|
28019
|
+
query(sql, params = []) {
|
|
28020
|
+
if (typeof this.db.prepare === "function") {
|
|
28021
|
+
return this.db.prepare(sql).all(params);
|
|
28022
|
+
} else {
|
|
28023
|
+
return this.db.query(sql).all(params);
|
|
28024
|
+
}
|
|
28025
|
+
}
|
|
28026
|
+
run(sql, params = []) {
|
|
28027
|
+
if (typeof this.db.prepare === "function") {
|
|
28028
|
+
return this.db.prepare(sql).run(params);
|
|
28029
|
+
} else {
|
|
28030
|
+
this.db.run(sql, params);
|
|
28031
|
+
return {};
|
|
28032
|
+
}
|
|
28033
|
+
}
|
|
28034
|
+
close() {
|
|
28035
|
+
if (this.db.close) {
|
|
28036
|
+
this.db.close();
|
|
28037
|
+
}
|
|
28038
|
+
}
|
|
28039
|
+
}
|
|
28040
|
+
|
|
27931
28041
|
class PidStore2 {
|
|
27932
28042
|
db;
|
|
27933
28043
|
baseDir;
|
|
28044
|
+
dbPath;
|
|
27934
28045
|
constructor(workingDir) {
|
|
27935
28046
|
this.baseDir = path12.resolve(workingDir, ".agent-yes");
|
|
28047
|
+
this.dbPath = path12.join(this.baseDir, "pid.sqlite");
|
|
27936
28048
|
}
|
|
27937
28049
|
async init() {
|
|
27938
28050
|
await mkdir6(path12.join(this.baseDir, "logs"), { recursive: true });
|
|
27939
28051
|
await mkdir6(path12.join(this.baseDir, "fifo"), { recursive: true });
|
|
27940
|
-
this.db = new
|
|
27941
|
-
|
|
27942
|
-
|
|
27943
|
-
|
|
27944
|
-
|
|
28052
|
+
this.db = new SqliteAdapter2;
|
|
28053
|
+
await this.db.init(this.dbPath);
|
|
28054
|
+
this.db.run("PRAGMA journal_mode=WAL");
|
|
28055
|
+
this.db.run("PRAGMA synchronous=NORMAL");
|
|
28056
|
+
this.db.run("PRAGMA cache_size=1000");
|
|
28057
|
+
this.db.run("PRAGMA temp_store=memory");
|
|
28058
|
+
this.db.run(`
|
|
28059
|
+
CREATE TABLE IF NOT EXISTS pid_records (
|
|
28060
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
28061
|
+
pid INTEGER NOT NULL UNIQUE,
|
|
28062
|
+
cli TEXT NOT NULL,
|
|
28063
|
+
args TEXT NOT NULL,
|
|
28064
|
+
prompt TEXT,
|
|
28065
|
+
logFile TEXT NOT NULL,
|
|
28066
|
+
fifoFile TEXT NOT NULL,
|
|
28067
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
28068
|
+
exitReason TEXT NOT NULL DEFAULT '',
|
|
28069
|
+
exitCode INTEGER,
|
|
28070
|
+
startedAt INTEGER NOT NULL,
|
|
28071
|
+
updatedAt INTEGER NOT NULL
|
|
28072
|
+
)
|
|
28073
|
+
`);
|
|
27945
28074
|
await this.cleanStaleRecords();
|
|
27946
28075
|
}
|
|
27947
28076
|
async registerProcess({
|
|
@@ -27951,29 +28080,43 @@ class PidStore2 {
|
|
|
27951
28080
|
prompt
|
|
27952
28081
|
}) {
|
|
27953
28082
|
const now = Date.now();
|
|
27954
|
-
const
|
|
27955
|
-
|
|
27956
|
-
|
|
27957
|
-
|
|
27958
|
-
|
|
27959
|
-
|
|
27960
|
-
|
|
27961
|
-
|
|
27962
|
-
|
|
27963
|
-
|
|
27964
|
-
|
|
27965
|
-
|
|
27966
|
-
|
|
28083
|
+
const argsJson = JSON.stringify(args);
|
|
28084
|
+
const logFile = this.getLogPath(pid);
|
|
28085
|
+
const fifoFile = this.getFifoPath(pid);
|
|
28086
|
+
try {
|
|
28087
|
+
this.db.run(`
|
|
28088
|
+
INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
|
|
28089
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
|
|
28090
|
+
`, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
|
|
28091
|
+
} catch (error) {
|
|
28092
|
+
if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
|
|
28093
|
+
this.db.run(`
|
|
28094
|
+
UPDATE pid_records
|
|
28095
|
+
SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
|
|
28096
|
+
WHERE pid = ?
|
|
28097
|
+
`, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
|
|
28098
|
+
} else {
|
|
28099
|
+
throw error;
|
|
28100
|
+
}
|
|
28101
|
+
}
|
|
28102
|
+
const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
|
|
28103
|
+
if (!result) {
|
|
28104
|
+
const allRecords = this.db.query("SELECT * FROM pid_records");
|
|
28105
|
+
logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
|
|
28106
|
+
throw new Error(`Failed to register process ${pid}`);
|
|
28107
|
+
}
|
|
27967
28108
|
logger.debug(`[pidStore] Registered process ${pid}`);
|
|
27968
|
-
return
|
|
28109
|
+
return result;
|
|
27969
28110
|
}
|
|
27970
28111
|
async updateStatus(pid, status, extra) {
|
|
27971
|
-
const
|
|
27972
|
-
|
|
27973
|
-
|
|
27974
|
-
|
|
27975
|
-
|
|
27976
|
-
|
|
28112
|
+
const updatedAt = Date.now();
|
|
28113
|
+
const exitReason = extra?.exitReason || "";
|
|
28114
|
+
const exitCode = extra?.exitCode;
|
|
28115
|
+
if (exitCode !== undefined) {
|
|
28116
|
+
this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, exitCode, updatedAt, pid]);
|
|
28117
|
+
} else {
|
|
28118
|
+
this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, updatedAt, pid]);
|
|
28119
|
+
}
|
|
27977
28120
|
logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
|
|
27978
28121
|
}
|
|
27979
28122
|
getLogPath(pid) {
|
|
@@ -27983,25 +28126,23 @@ class PidStore2 {
|
|
|
27983
28126
|
return path12.resolve(this.baseDir, "fifo", `${pid}.stdin`);
|
|
27984
28127
|
}
|
|
27985
28128
|
async cleanStaleRecords() {
|
|
27986
|
-
const activeRecords =
|
|
27987
|
-
status: { $ne: "exited" }
|
|
27988
|
-
});
|
|
28129
|
+
const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
|
|
27989
28130
|
for (const record of activeRecords) {
|
|
27990
28131
|
if (!this.isProcessAlive(record.pid)) {
|
|
27991
|
-
|
|
27992
|
-
$set: {
|
|
27993
|
-
status: "exited",
|
|
27994
|
-
exitReason: "stale-cleanup",
|
|
27995
|
-
updatedAt: Date.now()
|
|
27996
|
-
}
|
|
27997
|
-
}, {});
|
|
28132
|
+
this.db.run("UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?", [Date.now(), record.pid]);
|
|
27998
28133
|
logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
|
|
27999
28134
|
}
|
|
28000
28135
|
}
|
|
28001
28136
|
}
|
|
28002
28137
|
async close() {
|
|
28003
|
-
|
|
28004
|
-
|
|
28138
|
+
try {
|
|
28139
|
+
this.db.run("PRAGMA optimize");
|
|
28140
|
+
this.db.run("VACUUM");
|
|
28141
|
+
} catch (error) {
|
|
28142
|
+
logger.warn("[pidStore] Failed to optimize database:", error);
|
|
28143
|
+
}
|
|
28144
|
+
this.db.close();
|
|
28145
|
+
logger.debug("[pidStore] Database optimized and closed");
|
|
28005
28146
|
}
|
|
28006
28147
|
isProcessAlive(pid) {
|
|
28007
28148
|
try {
|
|
@@ -28014,10 +28155,9 @@ class PidStore2 {
|
|
|
28014
28155
|
static async findActiveFifo(workingDir) {
|
|
28015
28156
|
const store = new PidStore2(workingDir);
|
|
28016
28157
|
await store.init();
|
|
28017
|
-
const records =
|
|
28158
|
+
const records = store.db.query("SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1");
|
|
28018
28159
|
await store.close();
|
|
28019
|
-
|
|
28020
|
-
return sorted[0]?.fifoFile ?? null;
|
|
28160
|
+
return records[0]?.fifoFile ?? null;
|
|
28021
28161
|
}
|
|
28022
28162
|
}
|
|
28023
28163
|
|
|
@@ -28050,5 +28190,5 @@ var { exitCode } = await cliYes(config3);
|
|
|
28050
28190
|
console.log("exiting process");
|
|
28051
28191
|
process.exit(exitCode ?? 1);
|
|
28052
28192
|
|
|
28053
|
-
//# debugId=
|
|
28193
|
+
//# debugId=624A56792A6F741964756E2164756E21
|
|
28054
28194
|
//# sourceMappingURL=cli.js.map
|
package/dist/index.js
CHANGED
|
@@ -20730,24 +20730,81 @@ var init_fifo = __esm(() => {
|
|
|
20730
20730
|
});
|
|
20731
20731
|
|
|
20732
20732
|
// ts/pidStore.ts
|
|
20733
|
-
import Datastore from "@seald-io/nedb";
|
|
20734
20733
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
20735
20734
|
import path9 from "path";
|
|
20736
20735
|
|
|
20736
|
+
class SqliteAdapter {
|
|
20737
|
+
db;
|
|
20738
|
+
async init(dbPath) {
|
|
20739
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
20740
|
+
try {
|
|
20741
|
+
const { Database } = await import("bun:sqlite");
|
|
20742
|
+
this.db = new Database(dbPath);
|
|
20743
|
+
} catch (error) {
|
|
20744
|
+
logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
|
|
20745
|
+
const Database = (await import("better-sqlite3")).default;
|
|
20746
|
+
this.db = new Database(dbPath);
|
|
20747
|
+
}
|
|
20748
|
+
} else {
|
|
20749
|
+
const Database = (await import("better-sqlite3")).default;
|
|
20750
|
+
this.db = new Database(dbPath);
|
|
20751
|
+
}
|
|
20752
|
+
}
|
|
20753
|
+
query(sql, params = []) {
|
|
20754
|
+
if (typeof this.db.prepare === "function") {
|
|
20755
|
+
return this.db.prepare(sql).all(params);
|
|
20756
|
+
} else {
|
|
20757
|
+
return this.db.query(sql).all(params);
|
|
20758
|
+
}
|
|
20759
|
+
}
|
|
20760
|
+
run(sql, params = []) {
|
|
20761
|
+
if (typeof this.db.prepare === "function") {
|
|
20762
|
+
return this.db.prepare(sql).run(params);
|
|
20763
|
+
} else {
|
|
20764
|
+
this.db.run(sql, params);
|
|
20765
|
+
return {};
|
|
20766
|
+
}
|
|
20767
|
+
}
|
|
20768
|
+
close() {
|
|
20769
|
+
if (this.db.close) {
|
|
20770
|
+
this.db.close();
|
|
20771
|
+
}
|
|
20772
|
+
}
|
|
20773
|
+
}
|
|
20774
|
+
|
|
20737
20775
|
class PidStore {
|
|
20738
20776
|
db;
|
|
20739
20777
|
baseDir;
|
|
20778
|
+
dbPath;
|
|
20740
20779
|
constructor(workingDir) {
|
|
20741
20780
|
this.baseDir = path9.resolve(workingDir, ".agent-yes");
|
|
20781
|
+
this.dbPath = path9.join(this.baseDir, "pid.sqlite");
|
|
20742
20782
|
}
|
|
20743
20783
|
async init() {
|
|
20744
20784
|
await mkdir3(path9.join(this.baseDir, "logs"), { recursive: true });
|
|
20745
20785
|
await mkdir3(path9.join(this.baseDir, "fifo"), { recursive: true });
|
|
20746
|
-
this.db = new
|
|
20747
|
-
|
|
20748
|
-
|
|
20749
|
-
|
|
20750
|
-
|
|
20786
|
+
this.db = new SqliteAdapter;
|
|
20787
|
+
await this.db.init(this.dbPath);
|
|
20788
|
+
this.db.run("PRAGMA journal_mode=WAL");
|
|
20789
|
+
this.db.run("PRAGMA synchronous=NORMAL");
|
|
20790
|
+
this.db.run("PRAGMA cache_size=1000");
|
|
20791
|
+
this.db.run("PRAGMA temp_store=memory");
|
|
20792
|
+
this.db.run(`
|
|
20793
|
+
CREATE TABLE IF NOT EXISTS pid_records (
|
|
20794
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20795
|
+
pid INTEGER NOT NULL UNIQUE,
|
|
20796
|
+
cli TEXT NOT NULL,
|
|
20797
|
+
args TEXT NOT NULL,
|
|
20798
|
+
prompt TEXT,
|
|
20799
|
+
logFile TEXT NOT NULL,
|
|
20800
|
+
fifoFile TEXT NOT NULL,
|
|
20801
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
20802
|
+
exitReason TEXT NOT NULL DEFAULT '',
|
|
20803
|
+
exitCode INTEGER,
|
|
20804
|
+
startedAt INTEGER NOT NULL,
|
|
20805
|
+
updatedAt INTEGER NOT NULL
|
|
20806
|
+
)
|
|
20807
|
+
`);
|
|
20751
20808
|
await this.cleanStaleRecords();
|
|
20752
20809
|
}
|
|
20753
20810
|
async registerProcess({
|
|
@@ -20757,29 +20814,43 @@ class PidStore {
|
|
|
20757
20814
|
prompt
|
|
20758
20815
|
}) {
|
|
20759
20816
|
const now = Date.now();
|
|
20760
|
-
const
|
|
20761
|
-
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
20767
|
-
|
|
20768
|
-
|
|
20769
|
-
|
|
20770
|
-
|
|
20771
|
-
|
|
20772
|
-
|
|
20817
|
+
const argsJson = JSON.stringify(args);
|
|
20818
|
+
const logFile = this.getLogPath(pid);
|
|
20819
|
+
const fifoFile = this.getFifoPath(pid);
|
|
20820
|
+
try {
|
|
20821
|
+
this.db.run(`
|
|
20822
|
+
INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
|
|
20823
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
|
|
20824
|
+
`, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
|
|
20825
|
+
} catch (error) {
|
|
20826
|
+
if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
|
|
20827
|
+
this.db.run(`
|
|
20828
|
+
UPDATE pid_records
|
|
20829
|
+
SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
|
|
20830
|
+
WHERE pid = ?
|
|
20831
|
+
`, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
|
|
20832
|
+
} else {
|
|
20833
|
+
throw error;
|
|
20834
|
+
}
|
|
20835
|
+
}
|
|
20836
|
+
const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
|
|
20837
|
+
if (!result) {
|
|
20838
|
+
const allRecords = this.db.query("SELECT * FROM pid_records");
|
|
20839
|
+
logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
|
|
20840
|
+
throw new Error(`Failed to register process ${pid}`);
|
|
20841
|
+
}
|
|
20773
20842
|
logger.debug(`[pidStore] Registered process ${pid}`);
|
|
20774
|
-
return
|
|
20843
|
+
return result;
|
|
20775
20844
|
}
|
|
20776
20845
|
async updateStatus(pid, status, extra) {
|
|
20777
|
-
const
|
|
20778
|
-
|
|
20779
|
-
|
|
20780
|
-
|
|
20781
|
-
|
|
20782
|
-
|
|
20846
|
+
const updatedAt = Date.now();
|
|
20847
|
+
const exitReason = extra?.exitReason || "";
|
|
20848
|
+
const exitCode = extra?.exitCode;
|
|
20849
|
+
if (exitCode !== undefined) {
|
|
20850
|
+
this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, exitCode, updatedAt, pid]);
|
|
20851
|
+
} else {
|
|
20852
|
+
this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, updatedAt, pid]);
|
|
20853
|
+
}
|
|
20783
20854
|
logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
|
|
20784
20855
|
}
|
|
20785
20856
|
getLogPath(pid) {
|
|
@@ -20789,25 +20860,23 @@ class PidStore {
|
|
|
20789
20860
|
return path9.resolve(this.baseDir, "fifo", `${pid}.stdin`);
|
|
20790
20861
|
}
|
|
20791
20862
|
async cleanStaleRecords() {
|
|
20792
|
-
const activeRecords =
|
|
20793
|
-
status: { $ne: "exited" }
|
|
20794
|
-
});
|
|
20863
|
+
const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
|
|
20795
20864
|
for (const record of activeRecords) {
|
|
20796
20865
|
if (!this.isProcessAlive(record.pid)) {
|
|
20797
|
-
|
|
20798
|
-
$set: {
|
|
20799
|
-
status: "exited",
|
|
20800
|
-
exitReason: "stale-cleanup",
|
|
20801
|
-
updatedAt: Date.now()
|
|
20802
|
-
}
|
|
20803
|
-
}, {});
|
|
20866
|
+
this.db.run("UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?", [Date.now(), record.pid]);
|
|
20804
20867
|
logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
|
|
20805
20868
|
}
|
|
20806
20869
|
}
|
|
20807
20870
|
}
|
|
20808
20871
|
async close() {
|
|
20809
|
-
|
|
20810
|
-
|
|
20872
|
+
try {
|
|
20873
|
+
this.db.run("PRAGMA optimize");
|
|
20874
|
+
this.db.run("VACUUM");
|
|
20875
|
+
} catch (error) {
|
|
20876
|
+
logger.warn("[pidStore] Failed to optimize database:", error);
|
|
20877
|
+
}
|
|
20878
|
+
this.db.close();
|
|
20879
|
+
logger.debug("[pidStore] Database optimized and closed");
|
|
20811
20880
|
}
|
|
20812
20881
|
isProcessAlive(pid) {
|
|
20813
20882
|
try {
|
|
@@ -20820,10 +20889,9 @@ class PidStore {
|
|
|
20820
20889
|
static async findActiveFifo(workingDir) {
|
|
20821
20890
|
const store = new PidStore(workingDir);
|
|
20822
20891
|
await store.init();
|
|
20823
|
-
const records =
|
|
20892
|
+
const records = store.db.query("SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1");
|
|
20824
20893
|
await store.close();
|
|
20825
|
-
|
|
20826
|
-
return sorted[0]?.fifoFile ?? null;
|
|
20894
|
+
return records[0]?.fifoFile ?? null;
|
|
20827
20895
|
}
|
|
20828
20896
|
}
|
|
20829
20897
|
var init_pidStore = __esm(() => {
|
|
@@ -20957,7 +21025,10 @@ function getDefaultConfig() {
|
|
|
20957
21025
|
},
|
|
20958
21026
|
amp: {
|
|
20959
21027
|
help: "",
|
|
20960
|
-
install: "npm i -g @sourcegraph/amp"
|
|
21028
|
+
install: "npm i -g @sourcegraph/amp",
|
|
21029
|
+
enter: [
|
|
21030
|
+
/^.{0,4} Approve /
|
|
21031
|
+
]
|
|
20961
21032
|
}
|
|
20962
21033
|
}
|
|
20963
21034
|
});
|
|
@@ -21654,5 +21725,5 @@ export {
|
|
|
21654
21725
|
CLIS_CONFIG
|
|
21655
21726
|
};
|
|
21656
21727
|
|
|
21657
|
-
//# debugId=
|
|
21728
|
+
//# debugId=41F67CAFCE0253CB64756E2164756E21
|
|
21658
21729
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-yes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.1",
|
|
4
4
|
"description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"registry": "https://registry.npmjs.org/"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
|
-
"build": "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external
|
|
65
|
+
"build": "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=better-sqlite3 --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
66
66
|
"postbuild": "bun ./ts/postbuild.ts",
|
|
67
67
|
"demo": "bun run build && bun link && claude-yes -- demo",
|
|
68
68
|
"dev": "bun ts/index.ts",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"test": "bun test --coverage"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@seald-io/nedb": "^4.0.4",
|
|
78
77
|
"@snomiao/bun-pty": "^0.3.4",
|
|
78
|
+
"better-sqlite3": "^12.1.0",
|
|
79
79
|
"bun-pty": "^0.4.8",
|
|
80
80
|
"from-node-stream": "^0.1.2"
|
|
81
81
|
},
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
"@semantic-release/exec": "^7.1.0",
|
|
86
86
|
"@semantic-release/git": "^10.0.1",
|
|
87
87
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
88
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
88
89
|
"@types/bun": "^1.3.6",
|
|
89
90
|
"@types/jest": "^30.0.0",
|
|
90
91
|
"@types/ms": "^2.1.0",
|
package/ts/parseCliArgs.spec.ts
CHANGED
|
@@ -228,4 +228,21 @@ describe("CLI argument parsing", () => {
|
|
|
228
228
|
expect(result.cli).toBe("codex");
|
|
229
229
|
expect(result.prompt).toBe("Implement feature");
|
|
230
230
|
});
|
|
231
|
+
|
|
232
|
+
it("should parse ay -- hello command (ay is wrapper, so CLI is undefined)", () => {
|
|
233
|
+
const result = parseCliArgs([
|
|
234
|
+
"node",
|
|
235
|
+
"/path/to/ay",
|
|
236
|
+
"--",
|
|
237
|
+
"hello",
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
// "ay" is a wrapper script like "agent-yes", so cliName is stripped to undefined
|
|
241
|
+
// cli.ts will default this to "claude" at runtime
|
|
242
|
+
expect(result.cli).toBeUndefined();
|
|
243
|
+
expect(result.cliArgs).toEqual([]);
|
|
244
|
+
expect(result.prompt).toBe("hello");
|
|
245
|
+
expect(result.verbose).toBe(false);
|
|
246
|
+
expect(result.robust).toBe(true);
|
|
247
|
+
});
|
|
231
248
|
});
|
package/ts/parseCliArgs.ts
CHANGED
|
@@ -17,6 +17,7 @@ export function parseCliArgs(argv: string[]) {
|
|
|
17
17
|
.at(-1)
|
|
18
18
|
?.replace(/(\.[jt]s)?$/, "")
|
|
19
19
|
.replace(/^(cli|agent)(-yes$)?/, "")
|
|
20
|
+
.replace(/^ay$/, "") // treat standalone "ay" same as "agent-yes"
|
|
20
21
|
.replace(/-yes$/, "") || undefined;
|
|
21
22
|
|
|
22
23
|
// Parse args with yargs (same logic as cli.ts:16-73)
|
|
@@ -174,7 +175,7 @@ export function parseCliArgs(argv: string[]) {
|
|
|
174
175
|
env: process.env as Record<string, string>,
|
|
175
176
|
cli: (cliName ||
|
|
176
177
|
parsedArgv.cli ||
|
|
177
|
-
parsedArgv._[0]?.toString()?.replace?.(/-yes$/, "")) as (typeof SUPPORTED_CLIS)[number],
|
|
178
|
+
(dashIndex !== 0 ? parsedArgv._[0]?.toString()?.replace?.(/-yes$/, "") : undefined)) as (typeof SUPPORTED_CLIS)[number],
|
|
178
179
|
cliArgs: cliArgsForSpawn,
|
|
179
180
|
prompt: [parsedArgv.prompt, dashPrompt].filter(Boolean).join(" ") || undefined,
|
|
180
181
|
install: parsedArgv.install,
|
package/ts/pidStore.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import Datastore from "@seald-io/nedb";
|
|
2
1
|
import { mkdir } from "fs/promises";
|
|
3
2
|
import path from "path";
|
|
4
3
|
import { logger } from "./logger.ts";
|
|
5
4
|
|
|
6
5
|
export interface PidRecord {
|
|
6
|
+
id?: number;
|
|
7
7
|
pid: number;
|
|
8
8
|
cli: string;
|
|
9
|
-
args: string
|
|
9
|
+
args: string;
|
|
10
10
|
prompt?: string;
|
|
11
11
|
logFile: string;
|
|
12
12
|
fifoFile: string;
|
|
@@ -17,23 +17,95 @@ export interface PidRecord {
|
|
|
17
17
|
updatedAt: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// Direct SQLite implementation to avoid Kysely compatibility issues
|
|
21
|
+
class SqliteAdapter {
|
|
22
|
+
private db: any;
|
|
23
|
+
|
|
24
|
+
async init(dbPath: string) {
|
|
25
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
26
|
+
try {
|
|
27
|
+
const { Database } = await import("bun:sqlite");
|
|
28
|
+
this.db = new Database(dbPath);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
|
|
31
|
+
const Database = (await import("better-sqlite3")).default;
|
|
32
|
+
this.db = new Database(dbPath);
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
const Database = (await import("better-sqlite3")).default;
|
|
36
|
+
this.db = new Database(dbPath);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
query(sql: string, params: any[] = []): any[] {
|
|
41
|
+
if (typeof this.db.prepare === "function") {
|
|
42
|
+
// better-sqlite3 style
|
|
43
|
+
return this.db.prepare(sql).all(params);
|
|
44
|
+
} else {
|
|
45
|
+
// bun:sqlite style
|
|
46
|
+
return this.db.query(sql).all(params);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
run(sql: string, params: any[] = []): { lastInsertRowid?: number; changes?: number } {
|
|
51
|
+
if (typeof this.db.prepare === "function") {
|
|
52
|
+
// better-sqlite3 style
|
|
53
|
+
return this.db.prepare(sql).run(params);
|
|
54
|
+
} else {
|
|
55
|
+
// bun:sqlite style
|
|
56
|
+
this.db.run(sql, params);
|
|
57
|
+
return {}; // Bun doesn't return metadata in the same way
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
close() {
|
|
62
|
+
if (this.db.close) {
|
|
63
|
+
this.db.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
20
68
|
export class PidStore {
|
|
21
|
-
protected db!:
|
|
69
|
+
protected db!: SqliteAdapter;
|
|
22
70
|
private baseDir: string;
|
|
71
|
+
private dbPath: string;
|
|
23
72
|
|
|
24
73
|
constructor(workingDir: string) {
|
|
25
74
|
this.baseDir = path.resolve(workingDir, ".agent-yes");
|
|
75
|
+
this.dbPath = path.join(this.baseDir, "pid.sqlite");
|
|
26
76
|
}
|
|
27
77
|
|
|
28
78
|
async init(): Promise<void> {
|
|
29
79
|
await mkdir(path.join(this.baseDir, "logs"), { recursive: true });
|
|
30
80
|
await mkdir(path.join(this.baseDir, "fifo"), { recursive: true });
|
|
31
81
|
|
|
32
|
-
this.db = new
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
82
|
+
this.db = new SqliteAdapter();
|
|
83
|
+
await this.db.init(this.dbPath);
|
|
84
|
+
|
|
85
|
+
// Enable WAL mode for better concurrency and performance
|
|
86
|
+
this.db.run("PRAGMA journal_mode=WAL");
|
|
87
|
+
this.db.run("PRAGMA synchronous=NORMAL");
|
|
88
|
+
this.db.run("PRAGMA cache_size=1000");
|
|
89
|
+
this.db.run("PRAGMA temp_store=memory");
|
|
90
|
+
|
|
91
|
+
// Create table if it doesn't exist
|
|
92
|
+
this.db.run(`
|
|
93
|
+
CREATE TABLE IF NOT EXISTS pid_records (
|
|
94
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
95
|
+
pid INTEGER NOT NULL UNIQUE,
|
|
96
|
+
cli TEXT NOT NULL,
|
|
97
|
+
args TEXT NOT NULL,
|
|
98
|
+
prompt TEXT,
|
|
99
|
+
logFile TEXT NOT NULL,
|
|
100
|
+
fifoFile TEXT NOT NULL,
|
|
101
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
102
|
+
exitReason TEXT NOT NULL DEFAULT '',
|
|
103
|
+
exitCode INTEGER,
|
|
104
|
+
startedAt INTEGER NOT NULL,
|
|
105
|
+
updatedAt INTEGER NOT NULL
|
|
106
|
+
)
|
|
107
|
+
`);
|
|
108
|
+
|
|
37
109
|
await this.cleanStaleRecords();
|
|
38
110
|
}
|
|
39
111
|
|
|
@@ -49,21 +121,40 @@ export class PidStore {
|
|
|
49
121
|
prompt?: string;
|
|
50
122
|
}): Promise<PidRecord> {
|
|
51
123
|
const now = Date.now();
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
124
|
+
const argsJson = JSON.stringify(args);
|
|
125
|
+
const logFile = this.getLogPath(pid);
|
|
126
|
+
const fifoFile = this.getFifoPath(pid);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
this.db.run(`
|
|
130
|
+
INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
|
|
131
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
|
|
132
|
+
`, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
|
|
133
|
+
} catch (error: any) {
|
|
134
|
+
// Handle unique constraint violation by updating existing record
|
|
135
|
+
if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
|
|
136
|
+
this.db.run(`
|
|
137
|
+
UPDATE pid_records
|
|
138
|
+
SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
|
|
139
|
+
WHERE pid = ?
|
|
140
|
+
`, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
|
|
141
|
+
} else {
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Fetch the record
|
|
147
|
+
const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
|
|
148
|
+
|
|
149
|
+
if (!result) {
|
|
150
|
+
// Log all records for debugging
|
|
151
|
+
const allRecords = this.db.query("SELECT * FROM pid_records");
|
|
152
|
+
logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
|
|
153
|
+
throw new Error(`Failed to register process ${pid}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
65
156
|
logger.debug(`[pidStore] Registered process ${pid}`);
|
|
66
|
-
return
|
|
157
|
+
return result;
|
|
67
158
|
}
|
|
68
159
|
|
|
69
160
|
async updateStatus(
|
|
@@ -71,12 +162,22 @@ export class PidStore {
|
|
|
71
162
|
status: PidRecord["status"],
|
|
72
163
|
extra?: { exitReason?: string; exitCode?: number },
|
|
73
164
|
): Promise<void> {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
165
|
+
const updatedAt = Date.now();
|
|
166
|
+
const exitReason = extra?.exitReason || "";
|
|
167
|
+
const exitCode = extra?.exitCode;
|
|
168
|
+
|
|
169
|
+
if (exitCode !== undefined) {
|
|
170
|
+
this.db.run(
|
|
171
|
+
"UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?",
|
|
172
|
+
[status, exitReason, exitCode, updatedAt, pid]
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
this.db.run(
|
|
176
|
+
"UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?",
|
|
177
|
+
[status, exitReason, updatedAt, pid]
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
80
181
|
logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
|
|
81
182
|
}
|
|
82
183
|
|
|
@@ -89,30 +190,31 @@ export class PidStore {
|
|
|
89
190
|
}
|
|
90
191
|
|
|
91
192
|
async cleanStaleRecords(): Promise<void> {
|
|
92
|
-
const activeRecords =
|
|
93
|
-
|
|
94
|
-
});
|
|
193
|
+
const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
|
|
194
|
+
|
|
95
195
|
for (const record of activeRecords) {
|
|
96
196
|
if (!this.isProcessAlive(record.pid)) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
$set: {
|
|
101
|
-
status: "exited" as const,
|
|
102
|
-
exitReason: "stale-cleanup",
|
|
103
|
-
updatedAt: Date.now(),
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
{},
|
|
197
|
+
this.db.run(
|
|
198
|
+
"UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?",
|
|
199
|
+
[Date.now(), record.pid]
|
|
107
200
|
);
|
|
201
|
+
|
|
108
202
|
logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
|
|
109
203
|
}
|
|
110
204
|
}
|
|
111
205
|
}
|
|
112
206
|
|
|
113
207
|
async close(): Promise<void> {
|
|
114
|
-
|
|
115
|
-
|
|
208
|
+
// Optimize the database (equivalent to compacting in nedb)
|
|
209
|
+
try {
|
|
210
|
+
this.db.run("PRAGMA optimize");
|
|
211
|
+
this.db.run("VACUUM");
|
|
212
|
+
} catch (error) {
|
|
213
|
+
logger.warn("[pidStore] Failed to optimize database:", error);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
this.db.close();
|
|
217
|
+
logger.debug("[pidStore] Database optimized and closed");
|
|
116
218
|
}
|
|
117
219
|
|
|
118
220
|
private isProcessAlive(pid: number): boolean {
|
|
@@ -127,9 +229,12 @@ export class PidStore {
|
|
|
127
229
|
static async findActiveFifo(workingDir: string): Promise<string | null> {
|
|
128
230
|
const store = new PidStore(workingDir);
|
|
129
231
|
await store.init();
|
|
130
|
-
|
|
232
|
+
|
|
233
|
+
const records = store.db.query(
|
|
234
|
+
"SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1"
|
|
235
|
+
);
|
|
236
|
+
|
|
131
237
|
await store.close();
|
|
132
|
-
|
|
133
|
-
return sorted[0]?.fifoFile ?? null;
|
|
238
|
+
return records[0]?.fifoFile ?? null;
|
|
134
239
|
}
|
|
135
|
-
}
|
|
240
|
+
}
|