@opencode-manager/ocm-cli 0.1.3 → 0.1.4

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/ocm.js +19 -65
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -69,6 +69,6 @@ export default [ocm]
69
69
  ## Requirements
70
70
 
71
71
  - `opencode` available on `PATH`
72
- - `git` and `tar` available on `PATH`
72
+ - `git` and `tar` (with gzip support, i.e. the `-z` flag) available on `PATH`
73
73
  - macOS `security` CLI for Keychain-backed token storage
74
74
  - An OpenCode Manager URL and bearer token
package/dist/ocm.js CHANGED
@@ -165,12 +165,12 @@ class ManagerApi {
165
165
  if (!res.ok)
166
166
  throw await formatErrorResponse(res, `mirror part ${index}`);
167
167
  }
168
- async mirrorCommit(repoId, uploadId, totalParts) {
168
+ async mirrorCommit(repoId, uploadId, totalParts, gzip) {
169
169
  const url = `${this.baseUrl}/api/internal/repos/${repoId}/mirror/commit`;
170
170
  const res = await fetch(url, {
171
171
  method: "POST",
172
172
  headers: { ...this.headers(), "Content-Type": "application/json" },
173
- body: JSON.stringify({ uploadId, totalParts })
173
+ body: JSON.stringify({ uploadId, totalParts, gzip })
174
174
  });
175
175
  if (!res.ok)
176
176
  throw await formatErrorResponse(res, "mirror commit");
@@ -180,8 +180,9 @@ class ManagerApi {
180
180
  const url = `${this.baseUrl}/api/internal/repos/${repoId}/mirror/uploads/${uploadId}`;
181
181
  await fetch(url, { method: "DELETE", headers: this.headers() }).catch(() => {});
182
182
  }
183
- async mirrorDown(repoId) {
184
- const res = await fetch(`${this.baseUrl}/api/internal/repos/${repoId}/mirror`, {
183
+ async mirrorDown(repoId, gzip) {
184
+ const query = gzip ? "?compress=gzip" : "";
185
+ const res = await fetch(`${this.baseUrl}/api/internal/repos/${repoId}/mirror${query}`, {
185
186
  headers: this.headers()
186
187
  });
187
188
  if (!res.ok)
@@ -192,7 +193,7 @@ class ManagerApi {
192
193
 
193
194
  // src/mirror.ts
194
195
  import { spawnSync as spawnSync3, spawn } from "child_process";
195
- import { existsSync as existsSync2, statSync } from "fs";
196
+ import { existsSync as existsSync2 } from "fs";
196
197
  import * as fsp from "fs/promises";
197
198
  import { Readable } from "stream";
198
199
  import { join as join2, dirname as dirname2 } from "path";
@@ -242,6 +243,7 @@ function urlsEqual(a, b) {
242
243
  var HARDCODED_EXCLUDES = ["node_modules", "dist", ".next", ".venv", "__pycache__", ".turbo", ".DS_Store", "._*"];
243
244
  var PART_RETRIES = 3;
244
245
  var PART_BACKOFF_MS = [500, 2000, 8000];
246
+ var MIRROR_GZIP = true;
245
247
  function getGitignoreExclusions(repoRoot) {
246
248
  const res = spawnSync3("git", ["ls-files", "--others", "--ignored", "--exclude-standard", "--directory"], {
247
249
  cwd: repoRoot,
@@ -267,30 +269,6 @@ async function carryOverIgnored(fromDir, toDir) {
267
269
  await fsp.rename(src, dest).catch(() => {});
268
270
  }
269
271
  }
270
- function listIncludedFiles(repoRoot) {
271
- const tracked = spawnSync3("git", ["ls-files", "-z"], { cwd: repoRoot, encoding: "utf-8" });
272
- const untracked = spawnSync3("git", ["ls-files", "--others", "--exclude-standard", "-z"], { cwd: repoRoot, encoding: "utf-8" });
273
- const split = (out) => (out ?? "").split("\x00").filter((p) => p.length > 0);
274
- const all = [...split(tracked.stdout), ...split(untracked.stdout)];
275
- return all.filter((p) => {
276
- const top = p.split("/")[0] ?? p;
277
- return !HARDCODED_EXCLUDES.includes(top);
278
- });
279
- }
280
- function estimateTarSize(repoRoot) {
281
- const files = listIncludedFiles(repoRoot);
282
- let total = 0;
283
- for (const rel of files) {
284
- let size = 0;
285
- try {
286
- size = statSync(join2(repoRoot, rel)).size;
287
- } catch {
288
- continue;
289
- }
290
- total += 512 + Math.ceil(size / 512) * 512;
291
- }
292
- return total + 1024;
293
- }
294
272
 
295
273
  class MirrorAbort extends Error {
296
274
  constructor(message) {
@@ -370,9 +348,10 @@ function createPartFlusher(api, repoId, uploadId, chunkSize) {
370
348
  async function mirrorUp(plan, opts) {
371
349
  const repoId = opts.create ? 0 : plan.matched[0].repoId;
372
350
  const begin = await opts.api.mirrorBegin(repoId, { force: opts.force, create: opts.create });
373
- const totalBytes = estimateTarSize(plan.repoRoot);
374
351
  let bytesSent = 0;
375
352
  const tarArgs = ["-c", "-C", plan.repoRoot];
353
+ if (MIRROR_GZIP)
354
+ tarArgs.unshift("-z");
376
355
  for (const dir of HARDCODED_EXCLUDES)
377
356
  tarArgs.push("--exclude", dir);
378
357
  const ignoredPaths = getGitignoreExclusions(plan.repoRoot);
@@ -406,12 +385,12 @@ async function mirrorUp(plan, opts) {
406
385
  for await (const chunk of child.stdout) {
407
386
  await flusher.push(chunk);
408
387
  bytesSent += chunk.length;
409
- opts.onProgress?.({ phase: "uploading", bytesSent, totalBytes });
388
+ opts.onProgress?.({ bytesSent });
410
389
  }
411
390
  await tarExit;
412
391
  const totalParts = await flusher.finish();
413
- opts.onProgress?.({ phase: "committing", bytesSent, totalBytes });
414
- const result = await opts.api.mirrorCommit(begin.repoId, begin.uploadId, totalParts);
392
+ opts.onProgress?.({ bytesSent });
393
+ const result = await opts.api.mirrorCommit(begin.repoId, begin.uploadId, totalParts, MIRROR_GZIP);
415
394
  return result;
416
395
  } catch (err) {
417
396
  if (!child.killed)
@@ -431,8 +410,11 @@ async function mirrorDown(repoId, repoRoot, api, opts = { force: false }) {
431
410
  const staging = `${repoRoot}.ocm-recv-${Date.now()}`;
432
411
  await fsp.mkdir(staging, { recursive: true });
433
412
  try {
434
- const tarball = await api.mirrorDown(repoId);
435
- const child = spawn("tar", ["-x", "-f", "-", "-C", staging], { stdio: ["pipe", "pipe", "pipe"] });
413
+ const tarball = await api.mirrorDown(repoId, MIRROR_GZIP);
414
+ const tarArgs = ["-x", "-f", "-", "-C", staging];
415
+ if (MIRROR_GZIP)
416
+ tarArgs.unshift("-z");
417
+ const child = spawn("tar", tarArgs, { stdio: ["pipe", "pipe", "pipe"] });
436
418
  const stderrChunks = [];
437
419
  child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
438
420
  const tarDone = new Promise((resolve, reject) => {
@@ -501,31 +483,10 @@ function formatBytes(bytes) {
501
483
  function createProgressReporter(label, out = process.stderr, now = Date.now) {
502
484
  let finished = false;
503
485
  let lastRenderAt = -Infinity;
504
- let lastBucket = -1;
505
486
  let lastNonTtyTickAt = -Infinity;
506
487
  let frameIndex = 0;
507
488
  const isTTY = out.isTTY === true;
508
489
  return {
509
- update(current, total) {
510
- if (finished)
511
- return;
512
- if (isTTY) {
513
- const t = now();
514
- if (t - lastRenderAt < 80)
515
- return;
516
- lastRenderAt = t;
517
- const pct = total > 0 ? Math.min(99, Math.floor(current / total * 100)) : 0;
518
- out.write(`\r\x1B[K${label}: ${pct}% (${formatBytes(current)} / ${formatBytes(total)})`);
519
- } else {
520
- const pct = total > 0 ? Math.min(99, Math.floor(current / total * 100)) : 0;
521
- const bucket = Math.floor(pct / 10);
522
- if (bucket === lastBucket)
523
- return;
524
- lastBucket = bucket;
525
- out.write(`${label}: ${pct}% (${formatBytes(current)} / ${formatBytes(total)})
526
- `);
527
- }
528
- },
529
490
  tick(bytes) {
530
491
  if (finished)
531
492
  return;
@@ -585,7 +546,7 @@ function toTarget(last) {
585
546
  // package.json
586
547
  var package_default = {
587
548
  name: "@opencode-manager/ocm-cli",
588
- version: "0.1.3",
549
+ version: "0.1.4",
589
550
  description: "OpenCode Manager CLI: attach a local OpenCode TUI to a Manager-hosted repo.",
590
551
  license: "MIT",
591
552
  repository: {
@@ -882,14 +843,7 @@ async function cmdPush(args) {
882
843
  const api = new ManagerApi(state.managerUrl, token);
883
844
  const repos = await fetchRepos(state.managerUrl, token);
884
845
  const progress = createProgressReporter("push");
885
- const onProgress = (p) => {
886
- if (p.phase === "committing")
887
- progress.tick(p.bytesSent);
888
- else if (p.totalBytes > 0)
889
- progress.update(p.bytesSent, p.totalBytes);
890
- else
891
- progress.tick(p.bytesSent);
892
- };
846
+ const onProgress = (p) => progress.tick(p.bytesSent);
893
847
  const remotes = repos.map((r) => ({
894
848
  repoId: r.repoId,
895
849
  name: r.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencode-manager/ocm-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "OpenCode Manager CLI: attach a local OpenCode TUI to a Manager-hosted repo.",
5
5
  "license": "MIT",
6
6
  "repository": {