@parall/cli 1.18.0 → 1.20.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.
@@ -17,9 +17,9 @@ export function registerMessageCommands(program) {
17
17
  const { client, orgId } = resolveCredentials();
18
18
  const params = { limit: Number(opts.limit) };
19
19
  if (opts.before !== undefined)
20
- params.before = opts.before;
20
+ params.before = stripPrllScheme(opts.before);
21
21
  if (opts.after !== undefined)
22
- params.after = opts.after;
22
+ params.after = stripPrllScheme(opts.after);
23
23
  if (opts.threadRootId !== undefined)
24
24
  params.thread_root_id = stripPrllScheme(opts.threadRootId);
25
25
  if (opts.topLevel)
@@ -112,9 +112,9 @@ export function registerMessageCommands(program) {
112
112
  const { client } = resolveCredentials();
113
113
  const params = { limit: Number(opts.limit) };
114
114
  if (opts.before !== undefined)
115
- params.before = opts.before;
115
+ params.before = stripPrllScheme(opts.before);
116
116
  if (opts.after !== undefined)
117
- params.after = opts.after;
117
+ params.after = stripPrllScheme(opts.after);
118
118
  const result = await client.getMessageReplies(stripPrllScheme(messageId), params);
119
119
  printJson(result);
120
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/commands/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QA6FvD"}
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/commands/projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QA8FvD"}
@@ -1,5 +1,5 @@
1
1
  import { resolveCredentials } from '../lib/client.js';
2
- import { printJson, printError } from '../lib/output.js';
2
+ import { printJson, printError, stripPrllScheme } from '../lib/output.js';
3
3
  export function registerProjectCommands(program) {
4
4
  const projects = program.command('projects').description('Manage projects');
5
5
  projects
@@ -22,7 +22,7 @@ export function registerProjectCommands(program) {
22
22
  .action(async (projectId) => {
23
23
  try {
24
24
  const { client, orgId } = resolveCredentials();
25
- const result = await client.getProject(orgId, projectId);
25
+ const result = await client.getProject(orgId, stripPrllScheme(projectId));
26
26
  printJson(result);
27
27
  }
28
28
  catch (err) {
@@ -80,7 +80,7 @@ export function registerProjectCommands(program) {
80
80
  if (Object.keys(data).length === 0) {
81
81
  printError(new Error('No fields to update. Provide at least one option.'));
82
82
  }
83
- const result = await client.updateProject(orgId, projectId, data);
83
+ const result = await client.updateProject(orgId, stripPrllScheme(projectId), data);
84
84
  printJson(result);
85
85
  }
86
86
  catch (err) {
@@ -94,8 +94,9 @@ export function registerProjectCommands(program) {
94
94
  .action(async (projectId) => {
95
95
  try {
96
96
  const { client, orgId } = resolveCredentials();
97
- await client.deleteProject(orgId, projectId);
98
- printJson({ deleted: projectId });
97
+ const id = stripPrllScheme(projectId);
98
+ await client.deleteProject(orgId, id);
99
+ printJson({ deleted: id });
99
100
  }
100
101
  catch (err) {
101
102
  printError(err);
@@ -1,5 +1,5 @@
1
1
  import { resolveCredentials } from '../lib/client.js';
2
- import { printJson, printError } from '../lib/output.js';
2
+ import { printJson, printError, stripPrllScheme } from '../lib/output.js';
3
3
  export function registerUserCommands(program) {
4
4
  // whoami — registered directly on program
5
5
  program
@@ -24,7 +24,7 @@ export function registerUserCommands(program) {
24
24
  .action(async (userId) => {
25
25
  try {
26
26
  const { client } = resolveCredentials();
27
- const result = await client.getUser(userId);
27
+ const result = await client.getUser(stripPrllScheme(userId));
28
28
  printJson(result);
29
29
  }
30
30
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAwwBpD"}
1
+ {"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA8wBpD"}
@@ -41,6 +41,12 @@ export function registerWikiCommands(program) {
41
41
  process.stderr.write(`wiki ${entry.slug} synced → ${entry.path}\n`);
42
42
  }
43
43
  printJson(result);
44
+ // Conflicts are an expected, agent-resolvable state; failures (download
45
+ // error, shape conflict, etc.) are not. Reflect that in the exit code
46
+ // so CI / scripts that check status see partial sync as failure.
47
+ if (!result.ok) {
48
+ process.exitCode = 1;
49
+ }
44
50
  }
45
51
  catch (error) {
46
52
  printError(error);
@@ -245,11 +245,19 @@ export type AfcsLogResult = {
245
245
  };
246
246
  export declare function getWikiLog(ctx: ParallContext, wikiRef?: string, filePath?: string): Promise<AfcsLogResult>;
247
247
  export type SyncResult = {
248
- ok: true;
248
+ /** True iff every wiki's per-path action ran without failure. Conflicts do
249
+ * NOT clear this flag — they are an expected, structured outcome the agent
250
+ * is responsible for resolving. Only `failed[]` (download error,
251
+ * shape-conflict, etc.) flips ok to false. */
252
+ ok: boolean;
249
253
  mounts: number;
250
254
  synced: {
251
255
  slug: string;
252
256
  path: string;
257
+ applied: number;
258
+ fastForwarded: number;
259
+ conflicts: SyncConflict[];
260
+ failed: SyncFailure[];
253
261
  }[];
254
262
  };
255
263
  /**
@@ -265,5 +273,96 @@ export declare function syncAllMounts(ctx: ParallContext): Promise<SyncResult>;
265
273
  export declare function watchMounts(ctx: ParallContext, intervalSec: number): {
266
274
  stop: () => void;
267
275
  };
276
+ /**
277
+ * Sync a single wiki:
278
+ * 1. Fetch server manifest (file listing with SHAs)
279
+ * 2. Compare with local manifest
280
+ * 3. Download new/modified files via bulk download
281
+ * 4. Delete removed files
282
+ * 5. Save updated local manifest
283
+ */
284
+ /** Per-path sync action computed by three-way comparison. */
285
+ export type SyncAction = {
286
+ kind: 'noop';
287
+ path: string;
288
+ } | {
289
+ kind: 'apply-write';
290
+ path: string;
291
+ remoteSha: string;
292
+ remoteSize: number;
293
+ } | {
294
+ kind: 'apply-delete';
295
+ path: string;
296
+ } | {
297
+ kind: 'fast-forward';
298
+ path: string;
299
+ remoteSha: string;
300
+ remoteSize: number;
301
+ } | {
302
+ kind: 'fast-forward-delete';
303
+ path: string;
304
+ } | {
305
+ kind: 'conflict-write';
306
+ path: string;
307
+ remoteSha: string;
308
+ remoteSize: number;
309
+ baseSha: string | null;
310
+ localSha: string | null;
311
+ } | {
312
+ kind: 'conflict-delete';
313
+ path: string;
314
+ baseSha: string;
315
+ };
316
+ /**
317
+ * Pure three-way sync planner. Decides what to do per-path given the base
318
+ * (manifest entry from last successful sync), the current local working tree
319
+ * sha, and the latest remote sha. No I/O.
320
+ */
321
+ export declare function planSyncActions(input: {
322
+ paths: Iterable<string>;
323
+ baseSha: (path: string) => string | null;
324
+ localSha: (path: string) => string | null;
325
+ remote: (path: string) => {
326
+ sha: string;
327
+ size: number;
328
+ } | null;
329
+ }): SyncAction[];
330
+ export type SyncConflict = {
331
+ path: string;
332
+ /** Where the upstream copy was written (or null for remote-deleted markers). */
333
+ remoteCopyPath: string | null;
334
+ /** Type of conflict for logging / UI surface. */
335
+ reason: 'remote-changed-local-edited' | 'remote-deleted-local-edited' | 'remote-added-local-collides' | 'remote-changed-local-deleted';
336
+ };
337
+ export type SyncFailure = {
338
+ path: string;
339
+ /**
340
+ * Reason classification:
341
+ * - 'download' — bulk-download endpoint surfaced a per-file error.
342
+ * - 'shape-conflict' — local working tree has an incompatible file/directory
343
+ * shape at this path (e.g. local `foo` is a directory
344
+ * while remote treats `foo` as a file, or vice versa
345
+ * when remote is `foo/bar.md` but local has `foo` as
346
+ * a regular file).
347
+ */
348
+ reason: 'download' | 'shape-conflict';
349
+ /** Server-supplied error string or local errno, when available. */
350
+ detail?: string;
351
+ };
352
+ export type WikiSyncOutcome = {
353
+ applied: number;
354
+ fastForwarded: number;
355
+ conflicts: SyncConflict[];
356
+ /** Per-path failures that prevented a planned action from running. Manifest is left unchanged for these — next sync retries. */
357
+ failed: SyncFailure[];
358
+ };
359
+ export declare function syncSingleWiki(ctx: ParallContext, wiki: import('@parall/sdk').Wiki, mountPath: string): Promise<WikiSyncOutcome>;
360
+ /**
361
+ * Write a sync conflict marker under .parall-wiki/conflicts/, mirroring the
362
+ * source path so the agent (or user) can locate the upstream copy next to the
363
+ * local edit. Returns the absolute path written.
364
+ */
365
+ export declare function writeConflictMarker(mountRoot: string, sourcePath: string, suffix: string, content: Buffer): Promise<string>;
366
+ export declare function computeGitBlobSHA(content: Buffer): string;
268
367
  export {};
269
368
  //# sourceMappingURL=wiki.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/lib/wiki.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/lib/wiki.ts"],"names":[],"mappings":"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"}
package/dist/lib/wiki.js CHANGED
@@ -10,6 +10,9 @@ const TRAILING_FENCE_RE = /\s+#+\s*$/;
10
10
  const LOCAL_NODE_SECTION_ARTIFACT_VERSION = 2;
11
11
  const LOCAL_MANIFEST_FILE = 'manifest.json';
12
12
  const PRLL_WIKI_DIR = '.parall-wiki';
13
+ const CONFLICTS_DIR = 'conflicts';
14
+ const REMOTE_CONFLICT_SUFFIX = '.remote';
15
+ const REMOTE_DELETED_MARKER_SUFFIX = '.remote-deleted';
13
16
  // ---------------------------------------------------------------------------
14
17
  // Wiki resolution helpers
15
18
  // ---------------------------------------------------------------------------
@@ -365,10 +368,9 @@ export async function resetWikiWorkspace(ctx, wikiRef) {
365
368
  return { wiki_id: wiki.id, wiki_slug: wiki.slug, files_restored: 0 };
366
369
  }
367
370
  // Re-download all manifest files and overwrite local state
368
- const token = resolveApiToken();
369
371
  const manifestPaths = Object.keys(manifest.files);
370
372
  if (manifestPaths.length > 0) {
371
- const downloaded = await bulkDownloadFiles(mount.bulkDownloadUrl, token, manifestPaths);
373
+ const downloaded = await bulkDownloadFiles(mount.bulkDownloadUrl, requireMountToken(mount), manifestPaths);
372
374
  for (const entry of downloaded) {
373
375
  const fullPath = resolvePathInsideRoot(mount.root, entry.path);
374
376
  await fs.mkdir(path.dirname(fullPath), { recursive: true });
@@ -474,11 +476,19 @@ export async function syncAllMounts(ctx) {
474
476
  for (const wiki of wikis) {
475
477
  const mountPath = resolveMountPath(wiki.slug, mountRoot);
476
478
  activeMountPaths.add(mountPath);
477
- await syncSingleWiki(ctx, wiki, mountPath);
478
- synced.push({ slug: wiki.slug, path: mountPath });
479
+ const outcome = await syncSingleWiki(ctx, wiki, mountPath);
480
+ synced.push({
481
+ slug: wiki.slug,
482
+ path: mountPath,
483
+ applied: outcome.applied,
484
+ fastForwarded: outcome.fastForwarded,
485
+ conflicts: outcome.conflicts,
486
+ failed: outcome.failed,
487
+ });
479
488
  }
480
489
  await pruneStaleMounts(mountRoot, activeMountPaths);
481
- return { ok: true, mounts: wikis.length, synced };
490
+ const ok = synced.every((s) => s.failed.length === 0);
491
+ return { ok, mounts: wikis.length, synced };
482
492
  }
483
493
  /**
484
494
  * Run {@link syncAllMounts} in a loop, sleeping `intervalSec` between
@@ -506,73 +516,357 @@ export function watchMounts(ctx, intervalSec) {
506
516
  process.on('SIGTERM', stop);
507
517
  return { stop };
508
518
  }
509
- // ---------------------------------------------------------------------------
510
- // Sync internals (REST-based)
511
- // ---------------------------------------------------------------------------
512
519
  /**
513
- * Sync a single wiki:
514
- * 1. Fetch server manifest (file listing with SHAs)
515
- * 2. Compare with local manifest
516
- * 3. Download new/modified files via bulk download
517
- * 4. Delete removed files
518
- * 5. Save updated local manifest
520
+ * Pure three-way sync planner. Decides what to do per-path given the base
521
+ * (manifest entry from last successful sync), the current local working tree
522
+ * sha, and the latest remote sha. No I/O.
519
523
  */
520
- async function syncSingleWiki(ctx, wiki, mountPath) {
524
+ export function planSyncActions(input) {
525
+ const actions = [];
526
+ for (const p of input.paths) {
527
+ const baseSha = input.baseSha(p);
528
+ const remote = input.remote(p);
529
+ const remoteSha = remote?.sha ?? null;
530
+ const localSha = input.localSha(p);
531
+ if (baseSha === remoteSha) {
532
+ actions.push({ kind: 'noop', path: p });
533
+ continue;
534
+ }
535
+ const cleanLocal = localSha === baseSha;
536
+ if (cleanLocal) {
537
+ if (remoteSha === null) {
538
+ actions.push({ kind: 'apply-delete', path: p });
539
+ }
540
+ else {
541
+ actions.push({ kind: 'apply-write', path: p, remoteSha, remoteSize: remote.size });
542
+ }
543
+ continue;
544
+ }
545
+ if (localSha !== null && localSha === remoteSha) {
546
+ actions.push({ kind: 'fast-forward', path: p, remoteSha, remoteSize: remote.size });
547
+ continue;
548
+ }
549
+ if (localSha === null && remoteSha === null) {
550
+ actions.push({ kind: 'fast-forward-delete', path: p });
551
+ continue;
552
+ }
553
+ if (remoteSha === null) {
554
+ actions.push({ kind: 'conflict-delete', path: p, baseSha: baseSha });
555
+ }
556
+ else {
557
+ actions.push({ kind: 'conflict-write', path: p, remoteSha, remoteSize: remote.size, baseSha, localSha });
558
+ }
559
+ }
560
+ return actions;
561
+ }
562
+ export async function syncSingleWiki(ctx, wiki, mountPath) {
521
563
  await fs.mkdir(mountPath, { recursive: true });
522
564
  const metaDir = path.join(mountPath, PRLL_WIKI_DIR);
523
565
  await fs.mkdir(metaDir, { recursive: true });
524
- const token = resolveApiToken();
566
+ const token = resolveApiToken(ctx);
525
567
  const baseUrl = (ctx.baseUrl ?? process.env.PRLL_WIKI_URL ?? process.env.PRLL_API_URL ?? '').replace(/\/+$/, '');
526
568
  const manifestUrl = `${baseUrl}/wiki/v1/orgs/${ctx.orgId}/wikis/${wiki.id}/manifest`;
527
569
  const bulkDownloadUrl = `${baseUrl}/wiki/v1/orgs/${ctx.orgId}/wikis/${wiki.id}/files/bulk-download`;
528
- // 1. Fetch server manifest
570
+ // 1. Fetch server manifest + read local manifest.
529
571
  const manifestResp = await wikiServiceFetch(manifestUrl, token);
530
572
  const serverEntries = manifestResp.data;
531
- // 2. Read local manifest
532
- const localManifest = await readLocalManifest(mountPath);
533
- // 3. Compare: find new, modified, deleted files
534
573
  const serverByPath = new Map(serverEntries.map((e) => [e.path, e]));
535
- const toDownload = [];
536
- const toDelete = [];
537
- for (const entry of serverEntries) {
538
- const local = localManifest.files[entry.path];
539
- if (!local || local.sha !== entry.sha) {
540
- toDownload.push(entry.path);
574
+ const localManifest = await readLocalManifest(mountPath);
575
+ // 2. Build the union of paths from server + local manifest + working tree.
576
+ const allPaths = new Set();
577
+ for (const e of serverEntries)
578
+ allPaths.add(e.path);
579
+ for (const p of Object.keys(localManifest.files))
580
+ allPaths.add(p);
581
+ await walkLocalFiles(mountPath, mountPath, async (_full, rel) => {
582
+ allPaths.add(rel);
583
+ });
584
+ // 3. Snapshot working-tree shas so the planner stays pure. Paths whose local
585
+ // shape is incompatible with a regular-file read (e.g. local has a directory
586
+ // where remote treats this as a file, or a parent path is a regular file
587
+ // while remote nests under it) are routed to a 'shape-conflict' failure
588
+ // instead of crashing the whole sync — they need human/agent intervention.
589
+ const localShaCache = new Map();
590
+ const shapeConflictByPath = new Map();
591
+ for (const p of allPaths) {
592
+ const probe = await probeLocalGitBlob(mountPath, p);
593
+ if (probe.kind === 'shape-conflict') {
594
+ shapeConflictByPath.set(p, probe.detail);
595
+ localShaCache.set(p, null);
541
596
  }
542
- }
543
- for (const localPath of Object.keys(localManifest.files)) {
544
- if (!serverByPath.has(localPath)) {
545
- toDelete.push(localPath);
597
+ else {
598
+ localShaCache.set(p, probe.sha);
546
599
  }
547
600
  }
548
- // 4. Bulk download changed files
549
- if (toDownload.length > 0) {
550
- const downloaded = await bulkDownloadFiles(bulkDownloadUrl, token, toDownload);
601
+ const actions = planSyncActions({
602
+ paths: allPaths,
603
+ baseSha: (p) => localManifest.files[p]?.sha ?? null,
604
+ localSha: (p) => localShaCache.get(p) ?? null,
605
+ remote: (p) => {
606
+ const r = serverByPath.get(p);
607
+ return r ? { sha: r.sha, size: r.size } : null;
608
+ },
609
+ });
610
+ // 4. Fetch all bytes we need (apply-write + conflict-write) in one bulk call.
611
+ const fetchPaths = actions
612
+ .filter((a) => a.kind === 'apply-write' || a.kind === 'conflict-write')
613
+ .map((a) => a.path);
614
+ const contentByPath = new Map();
615
+ const downloadErrorByPath = new Map();
616
+ if (fetchPaths.length > 0) {
617
+ const downloaded = await bulkDownloadFiles(bulkDownloadUrl, token, fetchPaths);
618
+ const returnedPaths = new Set();
551
619
  for (const entry of downloaded) {
552
- const fullPath = resolvePathInsideRoot(mountPath, entry.path);
553
- await fs.mkdir(path.dirname(fullPath), { recursive: true });
554
- await fs.writeFile(fullPath, Buffer.from(entry.content_base64, 'base64'));
620
+ returnedPaths.add(entry.path);
621
+ if (entry.error) {
622
+ downloadErrorByPath.set(entry.path, entry.error);
623
+ continue;
624
+ }
625
+ contentByPath.set(entry.path, Buffer.from(entry.content_base64, 'base64'));
626
+ }
627
+ // Anything we asked for but the server omitted entirely is also a failure.
628
+ for (const p of fetchPaths) {
629
+ if (!returnedPaths.has(p)) {
630
+ downloadErrorByPath.set(p, 'missing from bulk-download response');
631
+ }
555
632
  }
556
633
  }
557
- // 5. Delete removed files
558
- for (const deletePath of toDelete) {
559
- const fullPath = resolvePathInsideRoot(mountPath, deletePath);
560
- await fs.rm(fullPath, { force: true });
561
- await removeEmptyParents(mountPath, path.dirname(fullPath));
634
+ // 5. Apply actions and start with the previous manifest, mutating per action.
635
+ const newFiles = { ...localManifest.files };
636
+ const conflicts = [];
637
+ const failed = [];
638
+ let applied = 0;
639
+ let fastForwarded = 0;
640
+ // Surface shape conflicts as sync failures up front so we don't try to write
641
+ // through them. Manifest stays pinned (the path's manifest entry is left as-is).
642
+ for (const [p, detail] of shapeConflictByPath) {
643
+ failed.push({ path: p, reason: 'shape-conflict', detail });
644
+ }
645
+ for (const action of actions) {
646
+ if (shapeConflictByPath.has(action.path)) {
647
+ // Already reported. Don't attempt apply / write — would crash on EISDIR/ENOTDIR.
648
+ continue;
649
+ }
650
+ switch (action.kind) {
651
+ case 'noop':
652
+ // Preserve manifest entry unchanged.
653
+ break;
654
+ case 'apply-write': {
655
+ const buf = contentByPath.get(action.path);
656
+ if (!buf) {
657
+ failed.push({
658
+ path: action.path,
659
+ reason: 'download',
660
+ detail: downloadErrorByPath.get(action.path),
661
+ });
662
+ break;
663
+ }
664
+ // TOCTOU re-check: the snapshot was taken before the bulk-download
665
+ // network round-trip. If the user/agent edited (or deleted) this path
666
+ // in that window, demote to a conflict so we don't silently overwrite.
667
+ const snapshotSha = localShaCache.get(action.path) ?? null;
668
+ const reprobe = await probeLocalGitBlob(mountPath, action.path);
669
+ if (reprobe.kind === 'shape-conflict') {
670
+ failed.push({ path: action.path, reason: 'shape-conflict', detail: reprobe.detail });
671
+ break;
672
+ }
673
+ const currentSha = reprobe.sha;
674
+ if (currentSha === action.remoteSha) {
675
+ // Local converged on remote during the window — manifest-only fast-forward.
676
+ newFiles[action.path] = { sha: action.remoteSha, size: action.remoteSize };
677
+ fastForwarded++;
678
+ break;
679
+ }
680
+ if (currentSha !== snapshotSha) {
681
+ // Local diverged after snapshot. Surface as a conflict instead of overwriting.
682
+ const remoteCopy = await writeConflictMarker(mountPath, action.path, REMOTE_CONFLICT_SUFFIX, buf);
683
+ let reason;
684
+ // Defense-in-depth: planSyncActions only emits apply-write when
685
+ // localSha === baseSha (both non-null), so snapshotSha should never
686
+ // be null here. The branch is kept to safely classify any race that
687
+ // creates a file between snapshot and apply.
688
+ if (snapshotSha === null && currentSha !== null) {
689
+ reason = 'remote-added-local-collides';
690
+ }
691
+ else if (currentSha === null) {
692
+ reason = 'remote-changed-local-deleted';
693
+ }
694
+ else {
695
+ reason = 'remote-changed-local-edited';
696
+ }
697
+ conflicts.push({ path: action.path, remoteCopyPath: remoteCopy, reason });
698
+ break;
699
+ }
700
+ const fullPath = resolvePathInsideRoot(mountPath, action.path);
701
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
702
+ await fs.writeFile(fullPath, buf);
703
+ newFiles[action.path] = { sha: action.remoteSha, size: action.remoteSize };
704
+ applied++;
705
+ break;
706
+ }
707
+ case 'apply-delete': {
708
+ // TOCTOU re-check before deleting. The action only fires when the
709
+ // snapshot saw clean local matching the (now-deleted) base; if the
710
+ // user re-created or edited the file after the snapshot, demote to
711
+ // conflict-delete instead of dropping their work.
712
+ const snapshotSha = localShaCache.get(action.path) ?? null;
713
+ const reprobe = await probeLocalGitBlob(mountPath, action.path);
714
+ if (reprobe.kind === 'shape-conflict') {
715
+ failed.push({ path: action.path, reason: 'shape-conflict', detail: reprobe.detail });
716
+ break;
717
+ }
718
+ const currentSha = reprobe.sha;
719
+ if (currentSha === null) {
720
+ // Convergence: user also deleted the file during the TOCTOU window.
721
+ // Both sides agree — fast-forward the manifest delete.
722
+ delete newFiles[action.path];
723
+ fastForwarded++;
724
+ break;
725
+ }
726
+ if (currentSha !== snapshotSha) {
727
+ await writeConflictMarker(mountPath, action.path, REMOTE_DELETED_MARKER_SUFFIX, Buffer.alloc(0));
728
+ conflicts.push({
729
+ path: action.path,
730
+ remoteCopyPath: null,
731
+ reason: 'remote-deleted-local-edited',
732
+ });
733
+ break;
734
+ }
735
+ const fullPath = resolvePathInsideRoot(mountPath, action.path);
736
+ await fs.rm(fullPath, { force: true });
737
+ await removeEmptyParents(mountPath, path.dirname(fullPath));
738
+ delete newFiles[action.path];
739
+ applied++;
740
+ break;
741
+ }
742
+ case 'fast-forward':
743
+ // Local already matches remote — only sync the manifest pointer.
744
+ newFiles[action.path] = { sha: action.remoteSha, size: action.remoteSize };
745
+ fastForwarded++;
746
+ break;
747
+ case 'fast-forward-delete':
748
+ delete newFiles[action.path];
749
+ fastForwarded++;
750
+ break;
751
+ case 'conflict-write': {
752
+ const buf = contentByPath.get(action.path);
753
+ if (!buf) {
754
+ failed.push({
755
+ path: action.path,
756
+ reason: 'download',
757
+ detail: downloadErrorByPath.get(action.path),
758
+ });
759
+ break;
760
+ }
761
+ const remoteCopy = await writeConflictMarker(mountPath, action.path, REMOTE_CONFLICT_SUFFIX, buf);
762
+ // Reason classification:
763
+ // - base null + local present → both sides added the same path with different bytes
764
+ // - base present + local null → user deleted locally, server changed it
765
+ // - base present + local present (and ≠ both) → genuine concurrent edit
766
+ let reason;
767
+ if (action.baseSha === null) {
768
+ reason = 'remote-added-local-collides';
769
+ }
770
+ else if (action.localSha === null) {
771
+ reason = 'remote-changed-local-deleted';
772
+ }
773
+ else {
774
+ reason = 'remote-changed-local-edited';
775
+ }
776
+ conflicts.push({ path: action.path, remoteCopyPath: remoteCopy, reason });
777
+ // Manifest entry stays at its pre-sync value so the next sync still
778
+ // recognises the local edit and re-tries the merge.
779
+ break;
780
+ }
781
+ case 'conflict-delete': {
782
+ // Server deleted a file the user has local edits for. Drop a marker
783
+ // so the agent surfaces the divergence without overwriting local.
784
+ await writeConflictMarker(mountPath, action.path, REMOTE_DELETED_MARKER_SUFFIX, Buffer.alloc(0));
785
+ conflicts.push({
786
+ path: action.path,
787
+ remoteCopyPath: null,
788
+ reason: 'remote-deleted-local-edited',
789
+ });
790
+ break;
791
+ }
792
+ }
562
793
  }
563
- // 6. Save updated local manifest
794
+ // 6. Persist new manifest.
564
795
  const newManifest = {
565
796
  wiki_id: wiki.id,
566
797
  synced_at: new Date().toISOString(),
567
- files: {},
798
+ files: newFiles,
568
799
  };
569
- for (const entry of serverEntries) {
570
- newManifest.files[entry.path] = {
571
- sha: entry.sha,
572
- size: entry.size,
573
- };
574
- }
575
800
  await writeLocalManifest(mountPath, newManifest);
801
+ if (conflicts.length > 0) {
802
+ for (const c of conflicts) {
803
+ const where = c.remoteCopyPath ? ` → ${path.relative(mountPath, c.remoteCopyPath)}` : '';
804
+ console.warn(`[wiki sync] conflict on ${c.path} (${c.reason})${where}`);
805
+ }
806
+ }
807
+ if (failed.length > 0) {
808
+ for (const f of failed) {
809
+ const detail = f.detail ? ` — ${f.detail}` : '';
810
+ console.warn(`[wiki sync] failed to ${f.reason} ${f.path}${detail} — manifest unchanged, will retry next sync`);
811
+ }
812
+ }
813
+ return { applied, fastForwarded, conflicts, failed };
814
+ }
815
+ /** Compute the git blob SHA of a working-tree file, or null if absent. */
816
+ async function readLocalGitBlobSha(mountRoot, relPath) {
817
+ const probe = await probeLocalGitBlob(mountRoot, relPath);
818
+ if (probe.kind === 'shape-conflict') {
819
+ // Surface as throw at this entry point — the caller (computeLocalChanges /
820
+ // computeLocalDiff) doesn't currently model shape conflicts. syncSingleWiki
821
+ // uses probeLocalGitBlob directly so it can route the failure structurally.
822
+ throw new Error(`shape conflict at ${relPath}: ${probe.detail}`);
823
+ }
824
+ return probe.sha;
825
+ }
826
+ /**
827
+ * Read a working-tree file as a git blob, returning a structured shape so
828
+ * callers can distinguish "absent" from "shape mismatch" (directory where a
829
+ * file is expected, or a parent path is a file blocking nested reads).
830
+ */
831
+ async function probeLocalGitBlob(mountRoot, relPath) {
832
+ let fullPath;
833
+ try {
834
+ fullPath = resolvePathInsideRoot(mountRoot, relPath);
835
+ }
836
+ catch {
837
+ return { kind: 'absent', sha: null };
838
+ }
839
+ try {
840
+ const buf = await fs.readFile(fullPath);
841
+ return { kind: 'file', sha: computeGitBlobSHA(buf) };
842
+ }
843
+ catch (err) {
844
+ const code = err.code;
845
+ if (code === 'ENOENT')
846
+ return { kind: 'absent', sha: null };
847
+ if (code === 'EISDIR' || code === 'ENOTDIR') {
848
+ return { kind: 'shape-conflict', detail: code };
849
+ }
850
+ throw err;
851
+ }
852
+ }
853
+ /**
854
+ * Write a sync conflict marker under .parall-wiki/conflicts/, mirroring the
855
+ * source path so the agent (or user) can locate the upstream copy next to the
856
+ * local edit. Returns the absolute path written.
857
+ */
858
+ export async function writeConflictMarker(mountRoot, sourcePath, suffix, content) {
859
+ // Validate sourcePath under the mount root before we use it as a key under
860
+ // .parall-wiki/conflicts. The planner only feeds us paths the server returned
861
+ // (so traversal segments are not expected today), but the rest of the sync
862
+ // pipeline always goes through resolvePathInsideRoot and we want this branch
863
+ // to fail closed too — defense in depth, not exploitation today.
864
+ resolvePathInsideRoot(mountRoot, sourcePath);
865
+ const conflictsRoot = path.join(mountRoot, PRLL_WIKI_DIR, CONFLICTS_DIR);
866
+ const target = resolvePathInsideRoot(conflictsRoot, sourcePath + suffix);
867
+ await fs.mkdir(path.dirname(target), { recursive: true });
868
+ await fs.writeFile(target, content);
869
+ return target;
576
870
  }
577
871
  // ---------------------------------------------------------------------------
578
872
  // Local manifest I/O
@@ -595,13 +889,22 @@ async function writeLocalManifest(mountRoot, manifest) {
595
889
  // ---------------------------------------------------------------------------
596
890
  // REST helpers for wiki-service
597
891
  // ---------------------------------------------------------------------------
598
- function resolveApiToken() {
599
- const token = process.env.PRLL_API_KEY?.trim();
892
+ function resolveApiToken(ctx) {
893
+ const token = ctx?.client.getToken()?.trim() || process.env.PRLL_API_KEY?.trim();
600
894
  if (!token) {
601
- throw new Error('PRLL_API_KEY is required for wiki operations');
895
+ throw new Error('API token is required via ctx.client.getToken() or PRLL_API_KEY for wiki operations');
602
896
  }
603
897
  return token;
604
898
  }
899
+ function tryResolveApiToken(ctx) {
900
+ return ctx?.client.getToken()?.trim() || process.env.PRLL_API_KEY?.trim() || undefined;
901
+ }
902
+ function requireMountToken(mount) {
903
+ if (!mount.token) {
904
+ throw new Error('API token is required for wiki-service operations on a local mount');
905
+ }
906
+ return mount.token;
907
+ }
605
908
  async function wikiServiceFetch(url, token, options = {}) {
606
909
  const method = options.method ?? 'GET';
607
910
  const headers = {
@@ -639,8 +942,12 @@ async function bulkDownloadFiles(bulkDownloadUrl, token, paths) {
639
942
  // ---------------------------------------------------------------------------
640
943
  // Local change detection (manifest-based)
641
944
  // ---------------------------------------------------------------------------
642
- function computeSHA256(content) {
643
- return createHash('sha256').update(content).digest('hex');
945
+ export function computeGitBlobSHA(content) {
946
+ // Manifest SHAs come from Gitea's Git blob object IDs, not raw content hashes.
947
+ // This matches Git's current SHA-1 blob format; if the server ever switches to
948
+ // SHA-256 object IDs, this helper must change in lockstep.
949
+ const header = Buffer.from(`blob ${content.length}\0`, 'utf8');
950
+ return createHash('sha1').update(header).update(content).digest('hex');
644
951
  }
645
952
  /** Compute list of changed files by comparing local disk to local manifest. */
646
953
  async function computeLocalChanges(mount) {
@@ -654,7 +961,7 @@ async function computeLocalChanges(mount) {
654
961
  }
655
962
  visited.add(relativePath);
656
963
  const localContent = await fs.readFile(fullPath);
657
- const localSha = computeSHA256(localContent);
964
+ const localSha = computeGitBlobSHA(localContent);
658
965
  const manifestEntry = manifest.files[relativePath];
659
966
  if (!manifestEntry) {
660
967
  // New file
@@ -684,7 +991,7 @@ async function computeLocalDiff(mount, manifest) {
684
991
  const diffFiles = [];
685
992
  const patchParts = [];
686
993
  const visited = new Set();
687
- const token = resolveApiToken();
994
+ const token = requireMountToken(mount);
688
995
  // Walk local files to find modified/added
689
996
  await walkLocalFiles(mount.root, mount.root, async (fullPath, relativePath) => {
690
997
  if (!matchesAllowedPrefix(relativePath, [])) {
@@ -692,7 +999,7 @@ async function computeLocalDiff(mount, manifest) {
692
999
  }
693
1000
  visited.add(relativePath);
694
1001
  const localContent = await fs.readFile(fullPath);
695
- const localSha = computeSHA256(localContent);
1002
+ const localSha = computeGitBlobSHA(localContent);
696
1003
  const manifestEntry = manifest.files[relativePath];
697
1004
  if (!manifestEntry) {
698
1005
  // New file
@@ -867,6 +1174,7 @@ async function resolveLocalWikiMount(ctx, wiki) {
867
1174
  return {
868
1175
  root,
869
1176
  wikiId: wiki.id,
1177
+ token: tryResolveApiToken(ctx),
870
1178
  manifestUrl: `${wikiBase}/manifest`,
871
1179
  blobUrl: `${wikiBase}/files`,
872
1180
  bulkDownloadUrl: `${wikiBase}/files/bulk-download`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/cli",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "description": "CLI client for Parall — universal agent & human access to Parall API",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,7 +30,7 @@
30
30
  "@modelcontextprotocol/sdk": "1.27.1",
31
31
  "commander": "^13.0.0",
32
32
  "zod": "^4.3.6",
33
- "@parall/sdk": "1.18.0"
33
+ "@parall/sdk": "1.20.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^22.0.0",
@@ -38,6 +38,7 @@
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsc",
41
- "dev": "tsc --watch"
41
+ "dev": "tsc --watch",
42
+ "test": "pnpm run build && node --test test/*.test.mjs"
42
43
  }
43
44
  }