@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 +170 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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
|
|
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((
|
|
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.
|
|
1435
|
+
var VERSION = "0.1.1";
|
|
1436
1436
|
var SPEC_SHA = "51d0418e";
|
|
1437
|
-
var SPEC_FETCHED_AT = "2026-06-
|
|
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((
|
|
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", () =>
|
|
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
|
|
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
|
|
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);
|