claude-yes 1.33.0 → 1.35.0
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 +335 -120
- package/dist/index.js +160 -51
- package/package.json +8 -7
- package/ts/index.ts +63 -11
- package/ts/logger.ts +0 -1
- package/ts/pidStore.ts +152 -47
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
|
+
}
|