@wspc/cli 0.0.23 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command65 } from "commander";
4
+ import { Command as Command66 } from "commander";
5
5
  import { realpathSync } from "fs";
6
6
  import { fileURLToPath } from "url";
7
7
 
@@ -37,7 +37,7 @@ function createSseClient({
37
37
  ...options
38
38
  }) {
39
39
  let lastEventId;
40
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
40
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
41
41
  const createStream = async function* () {
42
42
  let retryDelay = sseDefaultRetryDelay ?? 3e3;
43
43
  let attempt = 0;
@@ -1432,9 +1432,9 @@ function createConsistencyFetch(opts) {
1432
1432
  }
1433
1433
 
1434
1434
  // src/version.ts
1435
- var VERSION = "0.0.23";
1435
+ var VERSION = "0.1.1";
1436
1436
  var SPEC_SHA = "51d0418e";
1437
- var SPEC_FETCHED_AT = "2026-06-21T04:09:00.455Z";
1437
+ var SPEC_FETCHED_AT = "2026-06-21T12:30:00.342Z";
1438
1438
  var API_BASE = "https://api.wspc.ai";
1439
1439
 
1440
1440
  // src/index.ts
@@ -4746,14 +4746,14 @@ async function hashDriveFile(path) {
4746
4746
  }
4747
4747
  }
4748
4748
  }
4749
- await new Promise((resolve4, reject) => {
4749
+ await new Promise((resolve5, reject) => {
4750
4750
  const stream = fileHandle.createReadStream();
4751
4751
  stream.on("error", reject);
4752
4752
  stream.on("data", (chunk) => {
4753
4753
  hash.update(chunk);
4754
4754
  sizeBytes += chunk.length;
4755
4755
  });
4756
- stream.on("end", () => resolve4());
4756
+ stream.on("end", () => resolve5());
4757
4757
  });
4758
4758
  return {
4759
4759
  sizeBytes,
@@ -5233,11 +5233,170 @@ function isAlreadyExistsError(error) {
5233
5233
  return error instanceof Error && "code" in error && error.code === "EEXIST";
5234
5234
  }
5235
5235
 
5236
+ // src/handwritten/commands/drive/watch.ts
5237
+ import { Command as Command65 } from "commander";
5238
+ import chokidar from "chokidar";
5239
+ import { relative as relative2, resolve as resolve4 } from "path";
5240
+ async function runDriveWatch(root, options = {}) {
5241
+ const runSync = options.runSync ?? runDriveSyncOnce;
5242
+ const debounceMs = options.debounceMs ?? 500;
5243
+ const emit = options.onEvent ?? ((event) => render({ kind: "drive_watch", display: { shape: "object" } }, event));
5244
+ let debounceTimer;
5245
+ let retryTimer;
5246
+ let resolveRetryTimer;
5247
+ let running = false;
5248
+ let rerunRequested = false;
5249
+ let backoffMs = 1e3;
5250
+ let stopped = false;
5251
+ let stopWatch;
5252
+ let stopError;
5253
+ let cleanupSignalListeners = () => {
5254
+ };
5255
+ async function requestSync() {
5256
+ if (stopped) return;
5257
+ if (running) {
5258
+ rerunRequested = true;
5259
+ return;
5260
+ }
5261
+ running = true;
5262
+ try {
5263
+ do {
5264
+ rerunRequested = false;
5265
+ try {
5266
+ const summary = await runSync(root);
5267
+ emit({ kind: "drive_sync_once", ...summary });
5268
+ backoffMs = 1e3;
5269
+ } catch (error) {
5270
+ if (isAuthError(error) || isFatalWatchError(error) || !isRetryableWatchError(error)) throw error;
5271
+ emit({ kind: "drive_watch_retry", delay_ms: backoffMs, error: errorMessage2(error) });
5272
+ await waitForManagedTimer(backoffMs);
5273
+ if (stopped) return;
5274
+ backoffMs = Math.min(backoffMs * 2, 6e4);
5275
+ rerunRequested = true;
5276
+ }
5277
+ } while (rerunRequested && !stopped);
5278
+ } finally {
5279
+ running = false;
5280
+ }
5281
+ }
5282
+ function clearDebounceTimer() {
5283
+ if (debounceTimer === void 0) return;
5284
+ clearTimeout(debounceTimer);
5285
+ debounceTimer = void 0;
5286
+ }
5287
+ function clearRetryTimer() {
5288
+ if (retryTimer === void 0) return;
5289
+ clearTimeout(retryTimer);
5290
+ retryTimer = void 0;
5291
+ resolveRetryTimer?.();
5292
+ resolveRetryTimer = void 0;
5293
+ }
5294
+ function waitForManagedTimer(ms) {
5295
+ clearRetryTimer();
5296
+ return new Promise((resolve5) => {
5297
+ resolveRetryTimer = resolve5;
5298
+ retryTimer = setTimeout(() => {
5299
+ retryTimer = void 0;
5300
+ resolveRetryTimer = void 0;
5301
+ resolve5();
5302
+ }, ms);
5303
+ });
5304
+ }
5305
+ function stopWithError(error) {
5306
+ stopError = error;
5307
+ clearDebounceTimer();
5308
+ clearRetryTimer();
5309
+ stopWatch?.();
5310
+ }
5311
+ const state = await (options.readState ?? readDriveState)(root);
5312
+ const source = options.source ?? createChokidarSource(root);
5313
+ try {
5314
+ source.onChange((path) => {
5315
+ if (isDriveInternalPath(root, path)) return;
5316
+ if (running) {
5317
+ rerunRequested = true;
5318
+ return;
5319
+ }
5320
+ clearDebounceTimer();
5321
+ debounceTimer = setTimeout(() => {
5322
+ debounceTimer = void 0;
5323
+ requestSync().catch(stopWithError);
5324
+ }, debounceMs);
5325
+ });
5326
+ emit({ kind: "drive_watch_started", root, library_id: state.library_id });
5327
+ await requestSync();
5328
+ if (options.once) return;
5329
+ if (stopError !== void 0) throw stopError;
5330
+ await waitForStopSignal((stop, removeListeners) => {
5331
+ stopWatch = stop;
5332
+ cleanupSignalListeners = removeListeners;
5333
+ });
5334
+ if (stopError !== void 0) throw stopError;
5335
+ } finally {
5336
+ stopped = true;
5337
+ clearDebounceTimer();
5338
+ clearRetryTimer();
5339
+ cleanupSignalListeners();
5340
+ await source.close();
5341
+ }
5342
+ }
5343
+ function driveWatchCommand(options = {}) {
5344
+ return new Command65("watch").description("Watch a bound Drive folder and sync local changes").argument("[path]", "local folder path", ".").action(async (path) => {
5345
+ await runDriveWatch(resolve4(path), options);
5346
+ });
5347
+ }
5348
+ function createChokidarSource(root) {
5349
+ const watcher = chokidar.watch(root, {
5350
+ ignoreInitial: true,
5351
+ ignored: (path) => isDriveInternalPath(root, path)
5352
+ });
5353
+ return {
5354
+ onChange(handler) {
5355
+ watcher.on("all", (_event, path) => handler(path));
5356
+ },
5357
+ async close() {
5358
+ await watcher.close();
5359
+ }
5360
+ };
5361
+ }
5362
+ function isDriveInternalPath(root, path) {
5363
+ const rel = relative2(root, path);
5364
+ return rel === DRIVE_DIR || rel.startsWith(`${DRIVE_DIR}/`) || rel.startsWith(`${DRIVE_DIR}\\`);
5365
+ }
5366
+ function isAuthError(error) {
5367
+ const code = typeof error === "object" && error !== null ? error.code : void 0;
5368
+ const message = errorMessage2(error);
5369
+ return code === "WSPC_AUTH_EXPIRED" || /\b(401|403|auth|authorization)\b/i.test(message);
5370
+ }
5371
+ function isFatalWatchError(error) {
5372
+ return /unsupported .*state\.json schema|sync lock already exists/i.test(errorMessage2(error));
5373
+ }
5374
+ function isRetryableWatchError(error) {
5375
+ const status = typeof error === "object" && error !== null ? error.status : void 0;
5376
+ const message = errorMessage2(error);
5377
+ return status === 429 || typeof status === "number" && status >= 500 || /\b(429|5\d\d|network|temporary|fetch)\b/i.test(message);
5378
+ }
5379
+ function errorMessage2(error) {
5380
+ return error instanceof Error ? error.message : String(error);
5381
+ }
5382
+ function waitForStopSignal(onRegistered) {
5383
+ return new Promise((resolveStop) => {
5384
+ const stop = () => resolveStop();
5385
+ const cleanup = () => {
5386
+ process.off("SIGINT", stop);
5387
+ process.off("SIGTERM", stop);
5388
+ };
5389
+ onRegistered(stop, cleanup);
5390
+ process.once("SIGINT", stop);
5391
+ process.once("SIGTERM", stop);
5392
+ });
5393
+ }
5394
+
5236
5395
  // src/cli.ts
5237
5396
  function mountDriveCommands(program) {
5238
5397
  let drive = program.commands.find((c) => c.name() === "drive");
5239
5398
  if (!drive) {
5240
- drive = new Command65("drive").description("Drive commands");
5399
+ drive = new Command66("drive").description("Drive commands");
5241
5400
  program.addCommand(drive);
5242
5401
  }
5243
5402
  if (!drive.commands.some((c) => c.name() === "bind")) {
@@ -5250,6 +5409,9 @@ function mountDriveCommands(program) {
5250
5409
  const once = driveSyncCommand().commands.find((c) => c.name() === "once");
5251
5410
  if (once) sync.addCommand(once);
5252
5411
  }
5412
+ if (!drive.commands.some((c) => c.name() === "watch")) {
5413
+ drive.addCommand(driveWatchCommand());
5414
+ }
5253
5415
  }
5254
5416
  function isCliEntrypoint(argv = process.argv, metaUrl = import.meta.url) {
5255
5417
  if (!argv[1]) return false;
@@ -5260,7 +5422,7 @@ function isCliEntrypoint(argv = process.argv, metaUrl = import.meta.url) {
5260
5422
  }
5261
5423
  }
5262
5424
  function buildProgram() {
5263
- const program = new Command65().name("wspc").description("Official CLI for wspc.ai").version(`wspc ${VERSION} (spec ${SPEC_SHA}, fetched ${SPEC_FETCHED_AT})`).option("--json", "Output raw JSON (machine-readable)").option("--account <email>", "Run as a specific account (overrides the active account)").hook("preAction", (_thisCommand, actionCommand) => {
5425
+ const program = new Command66().name("wspc").description("Official CLI for wspc.ai").version(`wspc ${VERSION} (spec ${SPEC_SHA}, fetched ${SPEC_FETCHED_AT})`).option("--json", "Output raw JSON (machine-readable)").option("--account <email>", "Run as a specific account (overrides the active account)").hook("preAction", (_thisCommand, actionCommand) => {
5264
5426
  const globals = actionCommand.optsWithGlobals();
5265
5427
  if (globals.json) process.env.WSPC_OUTPUT = "json";
5266
5428
  if (globals.account) process.env.WSPC_ACCOUNT = String(globals.account);