bisync-cli 0.0.9 → 0.0.10

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.
Files changed (2) hide show
  1. package/dist/bisync.js +79 -47
  2. package/package.json +1 -1
package/dist/bisync.js CHANGED
@@ -21,8 +21,6 @@ var CLAUDE_PROJECTS_DIR = join(homedir(), ".claude", "projects");
21
21
  var DEBUG_LOG_PATH = join(CONFIG_DIR, "debug.log");
22
22
  var CLIENT_ID = "bisync-cli";
23
23
  var MAX_LINES_PER_BATCH = 200;
24
- var MAX_STATE_CHUNKS_PER_BATCH = 50;
25
- var MAX_STATE_CHUNK_SIZE = 256 * 1024;
26
24
  var parseArgs = () => {
27
25
  const parsed = parseArgsUtil({
28
26
  args: process.argv.slice(2),
@@ -377,30 +375,67 @@ var collectSessionStateFiles = async (sessionDir) => {
377
375
  await walk(sessionDir);
378
376
  return files;
379
377
  };
380
- var buildStateChunks = async (sessionDir) => {
378
+ var getStateUploadUrls = async (siteUrl, token, count) => {
379
+ const response = await fetch(`${siteUrl}/api/storage/upload-url`, {
380
+ method: "POST",
381
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
382
+ body: JSON.stringify({ count })
383
+ });
384
+ if (!response.ok) {
385
+ const errorText = await response.text();
386
+ const error = new Error(`Upload URL request failed: ${response.status} ${errorText}`);
387
+ error.status = response.status;
388
+ throw error;
389
+ }
390
+ const data = await response.json();
391
+ if (!Array.isArray(data.uploadUrls) || data.uploadUrls.length !== count) {
392
+ throw new Error("Upload URL response missing uploadUrls");
393
+ }
394
+ return data.uploadUrls;
395
+ };
396
+ var uploadStateFileToUrl = async (uploadUrl, filePath) => {
397
+ const response = await fetch(uploadUrl, {
398
+ method: "POST",
399
+ headers: { "Content-Type": "application/octet-stream" },
400
+ body: Bun.file(filePath)
401
+ });
402
+ if (!response.ok) {
403
+ const errorText = await response.text();
404
+ const error = new Error(`State upload failed: ${response.status} ${errorText}`);
405
+ error.status = response.status;
406
+ throw error;
407
+ }
408
+ const data = await response.json();
409
+ if (!data.storageId) {
410
+ throw new Error("State upload response missing storageId");
411
+ }
412
+ return data.storageId;
413
+ };
414
+ var buildStateFiles = async (sessionDir, siteUrl, token) => {
381
415
  const files = await collectSessionStateFiles(sessionDir);
382
- const chunks = [];
383
- for (const file of files) {
416
+ if (files.length === 0) {
417
+ return [];
418
+ }
419
+ const uploadUrls = await getStateUploadUrls(siteUrl, token, files.length);
420
+ const entries = [];
421
+ for (let index = 0;index < files.length; index += 1) {
422
+ const file = files[index];
423
+ const uploadUrl = uploadUrls[index];
424
+ if (!file || !uploadUrl) {
425
+ throw new Error("Upload URL list did not match file list");
426
+ }
384
427
  const data = new Uint8Array(await Bun.file(file.fullPath).arrayBuffer());
385
428
  const fileHash = createHash("sha256").update(data).digest("hex");
386
- const encoded = Buffer.from(data).toString("base64");
387
- const partCount = Math.max(1, Math.ceil(encoded.length / MAX_STATE_CHUNK_SIZE));
388
- for (let partIndex = 0;partIndex < partCount; partIndex += 1) {
389
- const start = partIndex * MAX_STATE_CHUNK_SIZE;
390
- const end = start + MAX_STATE_CHUNK_SIZE;
391
- chunks.push({
392
- path: file.relativePath,
393
- content: encoded.slice(start, end),
394
- encoding: "base64",
395
- fileHash,
396
- partIndex,
397
- partCount,
398
- size: file.size,
399
- mtimeMs: file.mtimeMs
400
- });
401
- }
429
+ const storageId = await uploadStateFileToUrl(uploadUrl, file.fullPath);
430
+ entries.push({
431
+ path: file.relativePath,
432
+ fileHash,
433
+ storageId,
434
+ size: file.size,
435
+ mtimeMs: file.mtimeMs
436
+ });
402
437
  }
403
- return chunks;
438
+ return entries;
404
439
  };
405
440
  var buildLogLines = (raw) => {
406
441
  const lines = raw.split(`
@@ -443,27 +478,24 @@ var uploadLogs = async (siteUrl, token, sessionId, raw) => {
443
478
  }
444
479
  }
445
480
  };
446
- var uploadStateFiles = async (siteUrl, token, sessionId, chunks) => {
447
- for (let i = 0;i < chunks.length; i += MAX_STATE_CHUNKS_PER_BATCH) {
448
- const batch = chunks.slice(i, i + MAX_STATE_CHUNKS_PER_BATCH);
449
- const response = await fetch(`${siteUrl}/api/ingest/state`, {
450
- method: "POST",
451
- headers: {
452
- "Content-Type": "application/json",
453
- Authorization: `Bearer ${token}`,
454
- ...process.env.BISYNC_SESSION_ID ? { "X-Bisync-Session-Id": process.env.BISYNC_SESSION_ID } : {}
455
- },
456
- body: JSON.stringify({
457
- session_id: sessionId,
458
- files: batch
459
- })
460
- });
461
- if (!response.ok) {
462
- const errorText = await response.text();
463
- const error = new Error(`State ingestion failed: ${response.status} ${errorText}`);
464
- error.status = response.status;
465
- throw error;
466
- }
481
+ var uploadStateFiles = async (siteUrl, token, sessionId, files) => {
482
+ const response = await fetch(`${siteUrl}/api/ingest/state`, {
483
+ method: "POST",
484
+ headers: {
485
+ "Content-Type": "application/json",
486
+ Authorization: `Bearer ${token}`,
487
+ ...process.env.BISYNC_SESSION_ID ? { "X-Bisync-Session-Id": process.env.BISYNC_SESSION_ID } : {}
488
+ },
489
+ body: JSON.stringify({
490
+ session_id: sessionId,
491
+ files
492
+ })
493
+ });
494
+ if (!response.ok) {
495
+ const errorText = await response.text();
496
+ const error = new Error(`State ingestion failed: ${response.status} ${errorText}`);
497
+ error.status = response.status;
498
+ throw error;
467
499
  }
468
500
  };
469
501
  var signOutRemote = async (siteUrl, token) => {
@@ -676,15 +708,15 @@ var runHook = async (hookName) => {
676
708
  }
677
709
  }
678
710
  }
679
- if (sessionDir) {
711
+ if (sessionDir && hookName === "SessionEnd") {
680
712
  log("debug", `runHook`, { sessionDir });
681
713
  try {
682
- const stateChunks = await buildStateChunks(sessionDir);
683
- if (stateChunks.length === 0) {
714
+ const stateFiles = await buildStateFiles(sessionDir, siteUrl, token);
715
+ if (stateFiles.length === 0) {
684
716
  log("debug", `runHook`, { sessionId, error: "no-state-files" });
685
717
  return;
686
718
  }
687
- await uploadStateFiles(siteUrl, token, sessionId, stateChunks);
719
+ await uploadStateFiles(siteUrl, token, sessionId, stateFiles);
688
720
  log("debug", `runHook`, { sessionId, error: "state-upload-ok" });
689
721
  } catch (error) {
690
722
  log("debug", `runHook`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bisync-cli",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "bin": {
5
5
  "bisync": "dist/bisync.js"
6
6
  },