agent-yes 1.53.1 → 1.54.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.
@@ -1,7 +1,7 @@
1
1
  import { a as __esmMin, c as __toESM, i as __commonJSMin, n as require_winston, o as __exportAll, s as __require, t as logger } from "./logger-DH1Rx9HI.js";
2
2
  import { arch, platform } from "process";
3
3
  import { execSync } from "child_process";
4
- import { existsSync, mkdirSync } from "fs";
4
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync } from "fs";
5
5
  import path, { dirname, join } from "path";
6
6
  import { constants } from "node:os";
7
7
  import path$1 from "node:path";
@@ -21,7 +21,8 @@ import { finished } from "node:stream/promises";
21
21
  import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
22
22
  import { Buffer as Buffer$1 } from "node:buffer";
23
23
  import { fromWritable } from "from-node-stream";
24
- import { mkdir, readFile, readdir, rename, writeFile as writeFile$1 } from "fs/promises";
24
+ import { appendFile, mkdir, readFile, readdir, rename, writeFile as writeFile$1 } from "fs/promises";
25
+ import { lock } from "proper-lockfile";
25
26
 
26
27
  //#region node_modules/is-plain-obj/index.js
27
28
  function isPlainObject(value) {
@@ -9315,180 +9316,220 @@ function shouldUseLock(_cwd) {
9315
9316
  }
9316
9317
 
9317
9318
  //#endregion
9318
- //#region ts/SqliteAdapter.ts
9319
- var SqliteAdapter = class {
9320
- db;
9321
- isInitialized = false;
9322
- async init(dbPath) {
9319
+ //#region ts/JsonlStore.ts
9320
+ /**
9321
+ * A lightweight NeDB-style JSONL persistence layer.
9322
+ *
9323
+ * - Append-only writes (one JSON object per line)
9324
+ * - Same `_id` → last line wins (fields merged)
9325
+ * - `$$deleted` lines act as tombstones
9326
+ * - Crash recovery: skip partial last line, recover from temp file
9327
+ * - Multi-process safe via proper-lockfile (reads don't need lock)
9328
+ * - Compact on close: deduplicates into clean file via atomic rename
9329
+ */
9330
+ var JsonlStore = class {
9331
+ filePath;
9332
+ tempPath;
9333
+ lockPath;
9334
+ docs = /* @__PURE__ */ new Map();
9335
+ constructor(filePath) {
9336
+ this.filePath = filePath;
9337
+ this.tempPath = filePath + "~";
9338
+ this.lockPath = path.dirname(filePath);
9339
+ }
9340
+ /**
9341
+ * Load all records from the JSONL file. No lock needed.
9342
+ * Handles crash recovery: partial last line skipped, temp file recovery.
9343
+ */
9344
+ async load() {
9345
+ await mkdir(path.dirname(this.filePath), { recursive: true });
9346
+ if (!existsSync(this.filePath) && existsSync(this.tempPath)) {
9347
+ logger.debug("[JsonlStore] Recovering from temp file");
9348
+ await rename(this.tempPath, this.filePath);
9349
+ }
9350
+ this.docs = /* @__PURE__ */ new Map();
9351
+ let raw = "";
9323
9352
  try {
9324
- await mkdir(path.dirname(dbPath), { recursive: true });
9325
- if (typeof globalThis.Bun !== "undefined") {
9326
- const { Database } = await import("bun:sqlite");
9327
- this.db = new Database(dbPath);
9328
- } else {
9329
- const { DatabaseSync } = await import("node:sqlite");
9330
- this.db = new DatabaseSync(dbPath);
9331
- }
9332
- this.isInitialized = true;
9333
- logger.debug(`[SqliteAdapter] Initialized database at ${dbPath}`);
9334
- } catch (error) {
9335
- logger.warn(`[SqliteAdapter] Failed to initialize database at ${dbPath}:`, error);
9336
- this.db = this.createFallbackDb();
9337
- this.isInitialized = false;
9353
+ raw = await readFile(this.filePath, "utf-8");
9354
+ } catch (err) {
9355
+ if (err.code === "ENOENT") return this.docs;
9356
+ throw err;
9338
9357
  }
9339
- }
9340
- createFallbackDb() {
9341
- const storage = /* @__PURE__ */ new Map();
9342
- return {
9343
- prepare: (sql) => ({
9344
- all: (...params) => {
9345
- logger.debug("[SqliteAdapter] Using fallback mode (query):", sql);
9346
- return storage.get(sql) || [];
9347
- },
9348
- run: (...params) => {
9349
- logger.debug("[SqliteAdapter] Using fallback mode (run):", sql);
9350
- return {
9351
- lastInsertRowid: 0,
9352
- changes: 0
9353
- };
9358
+ const lines = raw.split("\n");
9359
+ let corruptCount = 0;
9360
+ for (const line of lines) {
9361
+ const trimmed = line.trim();
9362
+ if (!trimmed) continue;
9363
+ try {
9364
+ const doc = JSON.parse(trimmed);
9365
+ if (!doc._id) continue;
9366
+ if (doc.$$deleted) this.docs.delete(doc._id);
9367
+ else {
9368
+ const existing = this.docs.get(doc._id);
9369
+ if (existing) this.docs.set(doc._id, {
9370
+ ...existing,
9371
+ ...doc
9372
+ });
9373
+ else this.docs.set(doc._id, doc);
9354
9374
  }
9355
- }),
9356
- query: (sql) => ({ all: (params) => {
9357
- logger.debug("[SqliteAdapter] Using fallback mode (query):", sql);
9358
- return storage.get(sql) || [];
9359
- } }),
9360
- run: (sql, params) => {
9361
- logger.debug("[SqliteAdapter] Using fallback mode (run):", sql);
9362
- },
9363
- close: () => {
9364
- logger.debug("[SqliteAdapter] Closing fallback db");
9375
+ } catch {
9376
+ corruptCount++;
9365
9377
  }
9378
+ }
9379
+ if (corruptCount > 0) logger.debug(`[JsonlStore] Skipped ${corruptCount} corrupt line(s) in ${this.filePath}`);
9380
+ return this.docs;
9381
+ }
9382
+ /** Get all live documents. */
9383
+ getAll() {
9384
+ return Array.from(this.docs.values());
9385
+ }
9386
+ /** Find a document by _id. */
9387
+ getById(id) {
9388
+ return this.docs.get(id);
9389
+ }
9390
+ /** Find documents matching a predicate. */
9391
+ find(predicate) {
9392
+ return this.getAll().filter(predicate);
9393
+ }
9394
+ /** Find first document matching a predicate. */
9395
+ findOne(predicate) {
9396
+ for (const doc of this.docs.values()) if (predicate(doc)) return doc;
9397
+ }
9398
+ /**
9399
+ * Append a new document. Acquires lock.
9400
+ * If no _id is provided, one is generated.
9401
+ */
9402
+ async append(doc) {
9403
+ const id = doc._id || generateId();
9404
+ const { _id: _, ...rest } = doc;
9405
+ const fullDoc = {
9406
+ _id: id,
9407
+ ...rest
9366
9408
  };
9409
+ return await this.withLock(async () => {
9410
+ await appendFile(this.filePath, JSON.stringify(fullDoc) + "\n");
9411
+ const existing = this.docs.get(fullDoc._id);
9412
+ if (existing) this.docs.set(fullDoc._id, {
9413
+ ...existing,
9414
+ ...fullDoc
9415
+ });
9416
+ else this.docs.set(fullDoc._id, fullDoc);
9417
+ return fullDoc;
9418
+ });
9367
9419
  }
9368
- query(sql, params = []) {
9369
- try {
9370
- if (typeof this.db.query === "function") return this.db.query(sql).all(params);
9371
- else return this.db.prepare(sql).all(...params);
9372
- } catch (error) {
9373
- logger.debug("[SqliteAdapter] Query failed:", error);
9374
- return [];
9375
- }
9420
+ /**
9421
+ * Update a document by _id. Appends a merge line. Acquires lock.
9422
+ */
9423
+ async updateById(id, patch) {
9424
+ await this.withLock(async () => {
9425
+ const line = {
9426
+ _id: id,
9427
+ ...patch
9428
+ };
9429
+ await appendFile(this.filePath, JSON.stringify(line) + "\n");
9430
+ const existing = this.docs.get(id);
9431
+ if (existing) this.docs.set(id, {
9432
+ ...existing,
9433
+ ...patch
9434
+ });
9435
+ });
9376
9436
  }
9377
- run(sql, params = []) {
9378
- try {
9379
- if (typeof this.db.query === "function") {
9380
- this.db.run(sql, params);
9381
- return {};
9382
- } else return this.db.prepare(sql).run(...params);
9383
- } catch (error) {
9384
- logger.debug("[SqliteAdapter] Run failed:", error);
9385
- return {};
9386
- }
9437
+ /**
9438
+ * Delete a document by _id. Appends a tombstone. Acquires lock.
9439
+ */
9440
+ async deleteById(id) {
9441
+ await this.withLock(async () => {
9442
+ const tombstone = {
9443
+ _id: id,
9444
+ $$deleted: true
9445
+ };
9446
+ await appendFile(this.filePath, JSON.stringify(tombstone) + "\n");
9447
+ this.docs.delete(id);
9448
+ });
9449
+ }
9450
+ /**
9451
+ * Compact the file: deduplicate entries, remove tombstones.
9452
+ * Writes to temp file, fsyncs, then atomic renames.
9453
+ * Acquires lock.
9454
+ */
9455
+ async compact() {
9456
+ await this.withLock(async () => {
9457
+ const lines = Array.from(this.docs.values()).map((doc) => {
9458
+ const { _id, $$deleted, ...rest } = doc;
9459
+ return JSON.stringify({
9460
+ _id,
9461
+ ...rest
9462
+ });
9463
+ }).join("\n");
9464
+ const content = lines ? lines + "\n" : "";
9465
+ await writeFile$1(this.tempPath, content);
9466
+ const fd = openSync(this.tempPath, "r");
9467
+ fsyncSync(fd);
9468
+ closeSync(fd);
9469
+ await rename(this.tempPath, this.filePath);
9470
+ });
9387
9471
  }
9388
- close() {
9472
+ async withLock(fn) {
9473
+ const dir = path.dirname(this.filePath);
9474
+ let release;
9389
9475
  try {
9390
- if (this.db?.close) this.db.close();
9391
- } catch (error) {
9392
- logger.debug("[SqliteAdapter] Close failed:", error);
9476
+ release = await lock(dir, {
9477
+ lockfilePath: this.filePath + ".lock",
9478
+ retries: {
9479
+ retries: 5,
9480
+ minTimeout: 50,
9481
+ maxTimeout: 500
9482
+ }
9483
+ });
9484
+ return await fn();
9485
+ } finally {
9486
+ if (release) await release();
9393
9487
  }
9394
9488
  }
9395
- isReady() {
9396
- return this.isInitialized;
9397
- }
9398
9489
  };
9490
+ let idCounter = 0;
9491
+ function generateId() {
9492
+ return Date.now().toString(36) + (idCounter++).toString(36) + Math.random().toString(36).slice(2, 6);
9493
+ }
9399
9494
 
9400
9495
  //#endregion
9401
9496
  //#region ts/pidStore.ts
9402
9497
  var PidStore = class PidStore {
9403
- db;
9404
9498
  storeDir;
9405
- dbPath;
9499
+ store;
9406
9500
  constructor(workingDir) {
9407
9501
  this.storeDir = path.resolve(workingDir, ".agent-yes");
9408
- this.dbPath = path.join(this.storeDir, "pid.sqlite");
9502
+ this.store = new JsonlStore(path.join(this.storeDir, "pid-records.jsonl"));
9409
9503
  }
9410
9504
  async init() {
9411
9505
  try {
9412
9506
  await this.ensureGitignore();
9413
- this.db = new SqliteAdapter();
9414
- await this.db.init(this.dbPath);
9415
- if (this.db.isReady()) {
9416
- this.db.run("PRAGMA journal_mode=WAL");
9417
- this.db.run("PRAGMA synchronous=NORMAL");
9418
- this.db.run("PRAGMA cache_size=1000");
9419
- this.db.run("PRAGMA temp_store=memory");
9420
- this.db.run(`
9421
- CREATE TABLE IF NOT EXISTS pid_records (
9422
- id INTEGER PRIMARY KEY AUTOINCREMENT,
9423
- pid INTEGER NOT NULL UNIQUE,
9424
- cli TEXT NOT NULL,
9425
- args TEXT NOT NULL,
9426
- prompt TEXT,
9427
- cwd TEXT NOT NULL,
9428
- logFile TEXT NOT NULL,
9429
- fifoFile TEXT NOT NULL,
9430
- status TEXT NOT NULL DEFAULT 'active',
9431
- exitReason TEXT NOT NULL DEFAULT '',
9432
- exitCode INTEGER,
9433
- startedAt INTEGER NOT NULL,
9434
- updatedAt INTEGER NOT NULL
9435
- )
9436
- `);
9437
- try {
9438
- if (!this.db.query("PRAGMA table_info(pid_records)").some((col) => col.name === "cwd")) {
9439
- logger.info("[pidStore] Migrating database: adding cwd column");
9440
- this.db.run("ALTER TABLE pid_records ADD COLUMN cwd TEXT NOT NULL DEFAULT ''");
9441
- }
9442
- } catch (error) {
9443
- logger.warn("[pidStore] Migration check failed:", error);
9444
- }
9445
- await this.cleanStaleRecords();
9446
- } else logger.warn("[pidStore] Database not ready, running in fallback mode");
9507
+ await this.store.load();
9508
+ await this.cleanStaleRecords();
9447
9509
  } catch (error) {
9448
9510
  logger.warn("[pidStore] Failed to initialize:", error);
9449
9511
  }
9450
9512
  }
9451
9513
  async registerProcess({ pid, cli, args, prompt, cwd }) {
9452
9514
  const now = Date.now();
9453
- const argsJson = JSON.stringify(args);
9454
- const logFile = path.resolve(this.getLogDir(), `${pid}.log`);
9455
- const fifoFile = this.getFifoPath(pid);
9456
- try {
9457
- this.db.run(`
9458
- INSERT INTO pid_records (pid, cli, args, prompt, cwd, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
9459
- VALUES (?, ?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
9460
- `, [
9461
- pid,
9462
- cli,
9463
- argsJson,
9464
- prompt,
9465
- cwd,
9466
- logFile,
9467
- fifoFile,
9468
- now,
9469
- now
9470
- ]);
9471
- } catch (error) {
9472
- if (error.code === "SQLITE_CONSTRAINT_UNIQUE") this.db.run(`
9473
- UPDATE pid_records
9474
- SET cli = ?, args = ?, prompt = ?, cwd = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
9475
- WHERE pid = ?
9476
- `, [
9477
- cli,
9478
- argsJson,
9479
- prompt,
9480
- cwd,
9481
- logFile,
9482
- fifoFile,
9483
- now,
9484
- now,
9485
- pid
9486
- ]);
9487
- else throw error;
9488
- }
9489
- const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
9515
+ const record = {
9516
+ pid,
9517
+ cli,
9518
+ args: JSON.stringify(args),
9519
+ prompt,
9520
+ cwd,
9521
+ logFile: path.resolve(this.getLogDir(), `${pid}.log`),
9522
+ fifoFile: this.getFifoPath(pid),
9523
+ status: "active",
9524
+ exitReason: "",
9525
+ startedAt: now
9526
+ };
9527
+ const existing = this.store.findOne((doc) => doc.pid === pid);
9528
+ if (existing) await this.store.updateById(existing._id, record);
9529
+ else await this.store.append(record);
9530
+ const result = this.store.findOne((doc) => doc.pid === pid);
9490
9531
  if (!result) {
9491
- const allRecords = this.db.query("SELECT * FROM pid_records");
9532
+ const allRecords = this.store.getAll();
9492
9533
  logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
9493
9534
  throw new Error(`Failed to register process ${pid}`);
9494
9535
  }
@@ -9496,26 +9537,16 @@ var PidStore = class PidStore {
9496
9537
  return result;
9497
9538
  }
9498
9539
  async updateStatus(pid, status, extra) {
9499
- const updatedAt = Date.now();
9500
- const exitReason = extra?.exitReason || "";
9501
- const exitCode = extra?.exitCode;
9502
- if (exitCode !== void 0) this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?", [
9503
- status,
9504
- exitReason,
9505
- exitCode,
9506
- updatedAt,
9507
- pid
9508
- ]);
9509
- else this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?", [
9510
- status,
9511
- exitReason,
9512
- updatedAt,
9513
- pid
9514
- ]);
9540
+ const existing = this.store.findOne((doc) => doc.pid === pid);
9541
+ if (!existing) return;
9542
+ const patch = { status };
9543
+ if (extra?.exitReason !== void 0) patch.exitReason = extra.exitReason;
9544
+ if (extra?.exitCode !== void 0) patch.exitCode = extra.exitCode;
9545
+ await this.store.updateById(existing._id, patch);
9515
9546
  logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
9516
9547
  }
9517
9548
  getAllRecords() {
9518
- return this.db.query("SELECT * FROM pid_records");
9549
+ return this.store.getAll();
9519
9550
  }
9520
9551
  getLogDir() {
9521
9552
  return path.resolve(this.storeDir, "logs");
@@ -9525,21 +9556,22 @@ var PidStore = class PidStore {
9525
9556
  else return path.resolve(this.storeDir, "fifo", `${pid}.stdin`);
9526
9557
  }
9527
9558
  async cleanStaleRecords() {
9528
- const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
9559
+ const activeRecords = this.store.find((r) => r.status !== "exited");
9529
9560
  for (const record of activeRecords) if (!this.isProcessAlive(record.pid)) {
9530
- this.db.run("UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?", [Date.now(), record.pid]);
9561
+ await this.store.updateById(record._id, {
9562
+ status: "exited",
9563
+ exitReason: "stale-cleanup"
9564
+ });
9531
9565
  logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
9532
9566
  }
9533
9567
  }
9534
9568
  async close() {
9535
9569
  try {
9536
- this.db.run("PRAGMA optimize");
9537
- this.db.run("VACUUM");
9570
+ await this.store.compact();
9538
9571
  } catch (error) {
9539
- logger.warn("[pidStore] Failed to optimize database:", error);
9572
+ logger.debug("[pidStore] Compact on close failed:", error);
9540
9573
  }
9541
- this.db.close();
9542
- logger.debug("[pidStore] Database optimized and closed");
9574
+ logger.debug("[pidStore] Database compacted and closed");
9543
9575
  }
9544
9576
  isProcessAlive(pid) {
9545
9577
  try {
@@ -9555,6 +9587,10 @@ var PidStore = class PidStore {
9555
9587
  # Ignore all log files and runtime data
9556
9588
  logs/
9557
9589
  fifo/
9590
+ pid-db/
9591
+ *.jsonl
9592
+ *.jsonl~
9593
+ *.jsonl.lock
9558
9594
  *.sqlite
9559
9595
  *.sqlite-*
9560
9596
  *.log
@@ -9578,7 +9614,7 @@ fifo/
9578
9614
  try {
9579
9615
  const store = new PidStore(workingDir);
9580
9616
  await store.init();
9581
- const records = store.db.query("SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1");
9617
+ const records = store.store.find((r) => r.status !== "exited").sort((a, b) => b.startedAt - a.startedAt);
9582
9618
  await store.close();
9583
9619
  return records[0]?.fifoFile ?? null;
9584
9620
  } catch (error) {
@@ -10734,4 +10770,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
10734
10770
 
10735
10771
  //#endregion
10736
10772
  export { AgentContext as a, config as i, CLIS_CONFIG as n, PidStore as o, agentYes as r, removeControlCharacters as s, SUPPORTED_CLIS as t };
10737
- //# sourceMappingURL=SUPPORTED_CLIS-I-XMBksk.js.map
10773
+ //# sourceMappingURL=SUPPORTED_CLIS-Db5tikbx.js.map
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import { c as __toESM, i as __commonJSMin, r as require_ms, t as logger } from "./logger-DH1Rx9HI.js";
3
3
  import "./agent-yes.config-Dr2p5kBW.js";
4
- import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-I-XMBksk.js";
4
+ import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Db5tikbx.js";
5
5
  import { createRequire } from "node:module";
6
6
  import { argv } from "process";
7
7
  import { spawn } from "child_process";
@@ -4505,7 +4505,7 @@ const Yargs = YargsFactory(esm_default);
4505
4505
  //#endregion
4506
4506
  //#region package.json
4507
4507
  var name = "agent-yes";
4508
- var version = "1.53.1";
4508
+ var version = "1.54.0";
4509
4509
 
4510
4510
  //#endregion
4511
4511
  //#region ts/parseCliArgs.ts
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./logger-DH1Rx9HI.js";
2
- import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-I-XMBksk.js";
2
+ import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-Db5tikbx.js";
3
3
 
4
4
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.53.1",
3
+ "version": "1.54.0",
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",
@@ -83,9 +83,9 @@
83
83
  },
84
84
  "dependencies": {
85
85
  "@snomiao/bun-pty": "^0.3.4",
86
- "@snomiao/keyv-sqlite": "^5.0.4",
87
86
  "bun-pty": "^0.4.8",
88
87
  "from-node-stream": "^0.1.2",
88
+ "proper-lockfile": "^4.1.2",
89
89
  "yaml": "^2.8.2"
90
90
  },
91
91
  "devDependencies": {
@@ -95,6 +95,7 @@
95
95
  "@types/jest": "^30.0.0",
96
96
  "@types/ms": "^2.1.0",
97
97
  "@types/node": "^25.0.10",
98
+ "@types/proper-lockfile": "^4.1.4",
98
99
  "@types/yargs": "^17.0.35",
99
100
  "@typescript/native-preview": "^7.0.0-dev.20260124.1",
100
101
  "execa": "^9.6.1",