@prover-coder-ai/docker-git 1.0.9 → 1.0.11

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prover-coder-ai/docker-git",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Minimal Vite-powered TypeScript console starter using Effect",
5
5
  "main": "dist/src/docker-git/main.js",
6
6
  "bin": {
@@ -15,9 +15,9 @@
15
15
  "build:app": "vite build --ssr src/app/main.ts",
16
16
  "dev": "vite build --watch --ssr src/app/main.ts",
17
17
  "prelint": "pnpm -C ../lib build",
18
- "lint": "npx @ton-ai-core/vibecode-linter src/",
19
- "lint:tests": "npx @ton-ai-core/vibecode-linter tests/",
20
- "lint:effect": "npx eslint --config eslint.effect-ts-check.config.mjs .",
18
+ "lint": "PATH=../../scripts:$PATH vibecode-linter src/",
19
+ "lint:tests": "PATH=../../scripts:$PATH vibecode-linter tests/",
20
+ "lint:effect": "PATH=../../scripts:$PATH eslint --config eslint.effect-ts-check.config.mjs .",
21
21
  "prebuild:docker-git": "pnpm -C ../lib build",
22
22
  "build:docker-git": "tsc -p tsconfig.build.json",
23
23
  "check": "pnpm run typecheck",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @prover-coder-ai/docker-git
2
2
 
3
+ ## 1.0.11
4
+
5
+ ### Patch Changes
6
+
7
+ - chore: automated version bump
8
+
9
+ ## 1.0.10
10
+
11
+ ### Patch Changes
12
+
13
+ - chore: automated version bump
14
+
3
15
  ## 1.0.9
4
16
 
5
17
  ### Patch Changes
package/dist/main.js CHANGED
@@ -182,6 +182,14 @@ class CommandFailedError extends Data.TaggedError("CommandFailedError") {
182
182
  }
183
183
  class AuthError extends Data.TaggedError("AuthError") {
184
184
  }
185
+ class ScrapArchiveNotFoundError extends Data.TaggedError("ScrapArchiveNotFoundError") {
186
+ }
187
+ class ScrapArchiveInvalidError extends Data.TaggedError("ScrapArchiveInvalidError") {
188
+ }
189
+ class ScrapTargetDirUnsupportedError extends Data.TaggedError("ScrapTargetDirUnsupportedError") {
190
+ }
191
+ class ScrapWipeRefusedError extends Data.TaggedError("ScrapWipeRefusedError") {
192
+ }
185
193
  const successExitCode$1 = Number(ExitCode(0));
186
194
  const readCloneRequest = Effect.sync(() => resolveCloneRequest(process.argv.slice(2), process.env["npm_lifecycle_event"]));
187
195
  const runDockerGitClone = (args) => Effect.gen(function* (_) {
@@ -265,6 +273,51 @@ const runDockerPsNames = (cwd) => pipe(runCommandCapture({
265
273
  command: "docker",
266
274
  args: ["ps", "--format", "{{.Names}}"]
267
275
  }, [Number(ExitCode(0))], (exitCode) => new CommandFailedError({ command: "docker ps", exitCode })), Effect.map((output) => output.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0)));
276
+ const isParseError = (error) => error._tag === "UnknownCommand" || error._tag === "UnknownOption" || error._tag === "MissingOptionValue" || error._tag === "MissingRequiredOption" || error._tag === "InvalidOption" || error._tag === "UnexpectedArgument";
277
+ const renderDockerAccessHeadline = (issue) => issue === "PermissionDenied" ? "Cannot access Docker daemon socket: permission denied." : "Cannot connect to Docker daemon.";
278
+ const renderPrimaryError = (error) => Match.value(error).pipe(Match.when({ _tag: "FileExistsError" }, ({ path }) => `File already exists: ${path} (use --force to overwrite)`), Match.when({ _tag: "DockerCommandError" }, ({ exitCode }) => [
279
+ `docker compose failed with exit code ${exitCode}`,
280
+ "Hint: ensure Docker daemon is running and current user can access /var/run/docker.sock (for example via the docker group)."
281
+ ].join("\n")), Match.when({ _tag: "DockerAccessError" }, ({ details, issue }) => [
282
+ renderDockerAccessHeadline(issue),
283
+ "Hint: ensure Docker daemon is running and current user can access the docker socket.",
284
+ "Hint: if you use rootless Docker, set DOCKER_HOST to your user socket (for example unix:///run/user/$UID/docker.sock).",
285
+ `Details: ${details}`
286
+ ].join("\n")), Match.when({ _tag: "CloneFailedError" }, ({ repoRef, repoUrl, targetDir }) => `Clone failed for ${repoUrl} (${repoRef}) into ${targetDir}`), Match.when({ _tag: "PortProbeError" }, ({ message, port }) => `SSH port check failed for ${port}: ${message}`), Match.when({ _tag: "CommandFailedError" }, ({ command, exitCode }) => `${command} failed with exit code ${exitCode}`), Match.when({ _tag: "ScrapArchiveNotFoundError" }, ({ path }) => `Scrap archive not found: ${path} (run docker-git scrap export first)`), Match.when({ _tag: "ScrapArchiveInvalidError" }, ({ message, path }) => `Invalid scrap archive: ${path}
287
+ Details: ${message}`), Match.when({ _tag: "ScrapTargetDirUnsupportedError" }, ({ reason, sshUser, targetDir }) => [
288
+ `Cannot use scrap with targetDir ${targetDir}.`,
289
+ `Reason: ${reason}`,
290
+ `Hint: scrap currently supports workspaces under /home/${sshUser}/... only.`
291
+ ].join("\n")), Match.when({ _tag: "ScrapWipeRefusedError" }, ({ reason, targetDir }) => [
292
+ `Refusing to wipe workspace for scrap import (targetDir ${targetDir}).`,
293
+ `Reason: ${reason}`,
294
+ "Hint: re-run with --no-wipe, or set a narrower --target-dir when creating the project."
295
+ ].join("\n")), Match.when({ _tag: "AuthError" }, ({ message }) => message), Match.orElse(() => null));
296
+ const renderConfigError = (error) => {
297
+ if (error._tag === "ConfigNotFoundError") {
298
+ return `docker-git.json not found: ${error.path} (run docker-git create in that directory)`;
299
+ }
300
+ if (error._tag === "ConfigDecodeError") {
301
+ return `Invalid docker-git.json at ${error.path}: ${error.message}`;
302
+ }
303
+ return null;
304
+ };
305
+ const renderInputError = (error) => {
306
+ if (error._tag === "InputCancelledError") {
307
+ return "Input cancelled.";
308
+ }
309
+ if (error._tag === "InputReadError") {
310
+ return `Input error: ${error.message}`;
311
+ }
312
+ return null;
313
+ };
314
+ const renderNonParseError = (error) => renderPrimaryError(error) ?? renderConfigError(error) ?? renderInputError(error) ?? error.message;
315
+ const renderError = (error) => {
316
+ if (isParseError(error)) {
317
+ return formatParseError(error);
318
+ }
319
+ return renderNonParseError(error);
320
+ };
268
321
  const splitLines = (input) => input.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
269
322
  const isAlpha = (char) => {
270
323
  const code = char.codePointAt(0) ?? 0;
@@ -448,85 +501,6 @@ Authorized keys: ${authorizedKeysPath}${authorizedKeysExists ? "" : " (missing)"
448
501
  Env global: ${config.template.envGlobalPath}
449
502
  Env project: ${config.template.envProjectPath}
450
503
  Codex auth: ${config.template.codexAuthPath} -> ${config.template.codexHome}`;
451
- const successExitCode = Number(ExitCode(0));
452
- const gitBaseEnv = {
453
- // Avoid blocking on interactive credential prompts in CI / TUI contexts.
454
- GIT_TERMINAL_PROMPT: "0"
455
- };
456
- const git = (cwd, args, env = gitBaseEnv) => runCommandWithExitCodes({ cwd, command: "git", args, env }, [successExitCode], (exitCode) => new CommandFailedError({ command: `git ${args[0] ?? ""}`, exitCode }));
457
- const gitExitCode = (cwd, args, env = gitBaseEnv) => runCommandExitCode({ cwd, command: "git", args, env });
458
- const gitCapture = (cwd, args, env = gitBaseEnv) => runCommandCapture({ cwd, command: "git", args, env }, [successExitCode], (exitCode) => new CommandFailedError({ command: `git ${args[0] ?? ""}`, exitCode }));
459
- const githubTokenKey = "GITHUB_TOKEN";
460
- const isGithubHttpsRemote = (url) => /^https:\/\/github\.com\//.test(url.trim());
461
- const resolveTokenFromProcessEnv = () => {
462
- const github = process.env["GITHUB_TOKEN"];
463
- if (github !== void 0) {
464
- const trimmed = github.trim();
465
- if (trimmed.length > 0) {
466
- return trimmed;
467
- }
468
- }
469
- const gh = process.env["GH_TOKEN"];
470
- if (gh !== void 0) {
471
- const trimmed = gh.trim();
472
- if (trimmed.length > 0) {
473
- return trimmed;
474
- }
475
- }
476
- return null;
477
- };
478
- const findTokenInEnvEntries = (entries) => {
479
- const directEntry = entries.find((e) => e.key === githubTokenKey);
480
- if (directEntry !== void 0) {
481
- const direct = directEntry.value.trim();
482
- if (direct.length > 0) {
483
- return direct;
484
- }
485
- }
486
- const labeledEntry = entries.find((e) => e.key.startsWith("GITHUB_TOKEN__"));
487
- if (labeledEntry !== void 0) {
488
- const labeled = labeledEntry.value.trim();
489
- if (labeled.length > 0) {
490
- return labeled;
491
- }
492
- }
493
- return null;
494
- };
495
- const resolveGithubToken = (fs, path, root) => Effect.gen(function* (_) {
496
- const fromEnv = resolveTokenFromProcessEnv();
497
- if (fromEnv !== null) {
498
- return fromEnv;
499
- }
500
- const envPath = path.join(root, ".orch", "env", "global.env");
501
- const exists = yield* _(fs.exists(envPath));
502
- if (!exists) {
503
- return null;
504
- }
505
- const text = yield* _(fs.readFileString(envPath));
506
- return findTokenInEnvEntries(parseEnvEntries(text));
507
- });
508
- const withGithubAskpassEnv = (token, use) => Effect.scoped(Effect.gen(function* (_) {
509
- const fs = yield* _(FileSystem.FileSystem);
510
- const askpassPath = yield* _(fs.makeTempFileScoped({ prefix: "docker-git-askpass-" }));
511
- const contents = [
512
- "#!/bin/sh",
513
- 'case "$1" in',
514
- ' *Username*) echo "x-access-token" ;;',
515
- ' *Password*) echo "${DOCKER_GIT_GITHUB_TOKEN}" ;;',
516
- ' *) echo "${DOCKER_GIT_GITHUB_TOKEN}" ;;',
517
- "esac",
518
- ""
519
- ].join("\n");
520
- yield* _(fs.writeFileString(askpassPath, contents));
521
- yield* _(fs.chmod(askpassPath, 448));
522
- const env = {
523
- ...gitBaseEnv,
524
- DOCKER_GIT_GITHUB_TOKEN: token,
525
- GIT_ASKPASS: askpassPath,
526
- GIT_ASKPASS_REQUIRE: "force"
527
- };
528
- return yield* _(use(env));
529
- }));
530
504
  const isDockerGitConfig = (entry) => entry.endsWith("docker-git.json");
531
505
  const shouldSkipDir = (entry) => entry === ".git" || entry === ".orch";
532
506
  const processDockerGitEntry = (fs, path, dir, entry, state) => Effect.gen(function* (_) {
@@ -563,106 +537,6 @@ const findDockerGitConfigPaths = (fs, path, rootDir) => Effect.gen(function* (_)
563
537
  }
564
538
  return results;
565
539
  });
566
- const resolveStateRoot = (path, cwd) => path.resolve(defaultProjectsRoot(cwd));
567
- Effect.gen(function* (_) {
568
- const path = yield* _(Path.Path);
569
- const cwd = process.cwd();
570
- const root = resolveStateRoot(path, cwd);
571
- yield* _(Effect.log(root));
572
- }).pipe(Effect.asVoid);
573
- Effect.gen(function* (_) {
574
- const path = yield* _(Path.Path);
575
- const root = resolveStateRoot(path, process.cwd());
576
- const output = yield* _(gitCapture(root, ["status", "-sb", "--porcelain=v1"], gitBaseEnv));
577
- yield* _(Effect.log(output.trim().length > 0 ? output.trimEnd() : "(clean)"));
578
- }).pipe(Effect.asVoid);
579
- Effect.gen(function* (_) {
580
- const fs = yield* _(FileSystem.FileSystem);
581
- const path = yield* _(Path.Path);
582
- const root = resolveStateRoot(path, process.cwd());
583
- const originUrlExit = yield* _(gitExitCode(root, ["remote", "get-url", "origin"], gitBaseEnv));
584
- if (originUrlExit !== successExitCode) {
585
- yield* _(git(root, ["pull", "--rebase"], gitBaseEnv));
586
- return;
587
- }
588
- const originUrl = yield* _(gitCapture(root, ["remote", "get-url", "origin"], gitBaseEnv).pipe(Effect.map((value) => value.trim())));
589
- const token = yield* _(resolveGithubToken(fs, path, root));
590
- const effect = token && token.length > 0 && isGithubHttpsRemote(originUrl) ? withGithubAskpassEnv(token, (env) => git(root, ["pull", "--rebase"], env)) : git(root, ["pull", "--rebase"], gitBaseEnv);
591
- yield* _(effect);
592
- }).pipe(Effect.asVoid);
593
- Effect.gen(function* (_) {
594
- const fs = yield* _(FileSystem.FileSystem);
595
- const path = yield* _(Path.Path);
596
- const root = resolveStateRoot(path, process.cwd());
597
- const originUrlExit = yield* _(gitExitCode(root, ["remote", "get-url", "origin"], gitBaseEnv));
598
- if (originUrlExit !== successExitCode) {
599
- yield* _(git(root, ["push", "-u", "origin", "HEAD"], gitBaseEnv));
600
- return;
601
- }
602
- const originUrl = yield* _(gitCapture(root, ["remote", "get-url", "origin"], gitBaseEnv).pipe(Effect.map((value) => value.trim())));
603
- const token = yield* _(resolveGithubToken(fs, path, root));
604
- const effect = token && token.length > 0 && isGithubHttpsRemote(originUrl) ? withGithubAskpassEnv(token, (env) => git(root, ["push", "-u", "origin", "HEAD"], env)) : git(root, ["push", "-u", "origin", "HEAD"], gitBaseEnv);
605
- yield* _(effect);
606
- }).pipe(Effect.asVoid);
607
- const isParseError = (error) => error._tag === "UnknownCommand" || error._tag === "UnknownOption" || error._tag === "MissingOptionValue" || error._tag === "MissingRequiredOption" || error._tag === "InvalidOption" || error._tag === "UnexpectedArgument";
608
- const renderDockerAccessHeadline = (issue) => issue === "PermissionDenied" ? "Cannot access Docker daemon socket: permission denied." : "Cannot connect to Docker daemon.";
609
- const renderPrimaryError = (error) => {
610
- if (error._tag === "FileExistsError") {
611
- return `File already exists: ${error.path} (use --force to overwrite)`;
612
- }
613
- if (error._tag === "DockerCommandError") {
614
- return [
615
- `docker compose failed with exit code ${error.exitCode}`,
616
- "Hint: ensure Docker daemon is running and current user can access /var/run/docker.sock (for example via the docker group)."
617
- ].join("\n");
618
- }
619
- if (error._tag === "DockerAccessError") {
620
- return [
621
- renderDockerAccessHeadline(error.issue),
622
- "Hint: ensure Docker daemon is running and current user can access the docker socket.",
623
- "Hint: if you use rootless Docker, set DOCKER_HOST to your user socket (for example unix:///run/user/$UID/docker.sock).",
624
- `Details: ${error.details}`
625
- ].join("\n");
626
- }
627
- if (error._tag === "CloneFailedError") {
628
- return `Clone failed for ${error.repoUrl} (${error.repoRef}) into ${error.targetDir}`;
629
- }
630
- if (error._tag === "PortProbeError") {
631
- return `SSH port check failed for ${error.port}: ${error.message}`;
632
- }
633
- if (error._tag === "CommandFailedError") {
634
- return `${error.command} failed with exit code ${error.exitCode}`;
635
- }
636
- if (error._tag === "AuthError") {
637
- return error.message;
638
- }
639
- return null;
640
- };
641
- const renderConfigError = (error) => {
642
- if (error._tag === "ConfigNotFoundError") {
643
- return `docker-git.json not found: ${error.path} (run docker-git create in that directory)`;
644
- }
645
- if (error._tag === "ConfigDecodeError") {
646
- return `Invalid docker-git.json at ${error.path}: ${error.message}`;
647
- }
648
- return null;
649
- };
650
- const renderInputError = (error) => {
651
- if (error._tag === "InputCancelledError") {
652
- return "Input cancelled.";
653
- }
654
- if (error._tag === "InputReadError") {
655
- return `Input error: ${error.message}`;
656
- }
657
- return null;
658
- };
659
- const renderNonParseError = (error) => renderPrimaryError(error) ?? renderConfigError(error) ?? renderInputError(error) ?? error.message;
660
- const renderError = (error) => {
661
- if (isParseError(error)) {
662
- return formatParseError(error);
663
- }
664
- return renderNonParseError(error);
665
- };
666
540
  const sshOptions = "-tt -Y -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
667
541
  const buildSshCommand = (config, sshKey) => sshKey === null ? `ssh ${sshOptions} -p ${config.sshPort} ${config.sshUser}@localhost` : `ssh -i ${sshKey} ${sshOptions} -p ${config.sshPort} ${config.sshUser}@localhost`;
668
542
  const loadProjectBase = (configPath) => Effect.gen(function* (_) {
@@ -779,6 +653,126 @@ const withProjectIndexAndSsh = (run) => pipe(loadProjectIndex(), Effect.flatMap(
779
653
  const sshKey = yield* _(findSshPrivateKey(fs, path, process.cwd()));
780
654
  return yield* _(run(index, sshKey));
781
655
  })));
656
+ const successExitCode = Number(ExitCode(0));
657
+ const gitBaseEnv = {
658
+ // Avoid blocking on interactive credential prompts in CI / TUI contexts.
659
+ GIT_TERMINAL_PROMPT: "0"
660
+ };
661
+ const git = (cwd, args, env = gitBaseEnv) => runCommandWithExitCodes({ cwd, command: "git", args, env }, [successExitCode], (exitCode) => new CommandFailedError({ command: `git ${args[0] ?? ""}`, exitCode }));
662
+ const gitExitCode = (cwd, args, env = gitBaseEnv) => runCommandExitCode({ cwd, command: "git", args, env });
663
+ const gitCapture = (cwd, args, env = gitBaseEnv) => runCommandCapture({ cwd, command: "git", args, env }, [successExitCode], (exitCode) => new CommandFailedError({ command: `git ${args[0] ?? ""}`, exitCode }));
664
+ const githubTokenKey = "GITHUB_TOKEN";
665
+ const isGithubHttpsRemote = (url) => /^https:\/\/github\.com\//.test(url.trim());
666
+ const resolveTokenFromProcessEnv = () => {
667
+ const github = process.env["GITHUB_TOKEN"];
668
+ if (github !== void 0) {
669
+ const trimmed = github.trim();
670
+ if (trimmed.length > 0) {
671
+ return trimmed;
672
+ }
673
+ }
674
+ const gh = process.env["GH_TOKEN"];
675
+ if (gh !== void 0) {
676
+ const trimmed = gh.trim();
677
+ if (trimmed.length > 0) {
678
+ return trimmed;
679
+ }
680
+ }
681
+ return null;
682
+ };
683
+ const findTokenInEnvEntries = (entries) => {
684
+ const directEntry = entries.find((e) => e.key === githubTokenKey);
685
+ if (directEntry !== void 0) {
686
+ const direct = directEntry.value.trim();
687
+ if (direct.length > 0) {
688
+ return direct;
689
+ }
690
+ }
691
+ const labeledEntry = entries.find((e) => e.key.startsWith("GITHUB_TOKEN__"));
692
+ if (labeledEntry !== void 0) {
693
+ const labeled = labeledEntry.value.trim();
694
+ if (labeled.length > 0) {
695
+ return labeled;
696
+ }
697
+ }
698
+ return null;
699
+ };
700
+ const resolveGithubToken = (fs, path, root) => Effect.gen(function* (_) {
701
+ const fromEnv = resolveTokenFromProcessEnv();
702
+ if (fromEnv !== null) {
703
+ return fromEnv;
704
+ }
705
+ const envPath = path.join(root, ".orch", "env", "global.env");
706
+ const exists = yield* _(fs.exists(envPath));
707
+ if (!exists) {
708
+ return null;
709
+ }
710
+ const text = yield* _(fs.readFileString(envPath));
711
+ return findTokenInEnvEntries(parseEnvEntries(text));
712
+ });
713
+ const withGithubAskpassEnv = (token, use) => Effect.scoped(Effect.gen(function* (_) {
714
+ const fs = yield* _(FileSystem.FileSystem);
715
+ const askpassPath = yield* _(fs.makeTempFileScoped({ prefix: "docker-git-askpass-" }));
716
+ const contents = [
717
+ "#!/bin/sh",
718
+ 'case "$1" in',
719
+ ' *Username*) echo "x-access-token" ;;',
720
+ ' *Password*) echo "${DOCKER_GIT_GITHUB_TOKEN}" ;;',
721
+ ' *) echo "${DOCKER_GIT_GITHUB_TOKEN}" ;;',
722
+ "esac",
723
+ ""
724
+ ].join("\n");
725
+ yield* _(fs.writeFileString(askpassPath, contents));
726
+ yield* _(fs.chmod(askpassPath, 448));
727
+ const env = {
728
+ ...gitBaseEnv,
729
+ DOCKER_GIT_GITHUB_TOKEN: token,
730
+ GIT_ASKPASS: askpassPath,
731
+ GIT_ASKPASS_REQUIRE: "force"
732
+ };
733
+ return yield* _(use(env));
734
+ }));
735
+ const resolveStateRoot = (path, cwd) => path.resolve(defaultProjectsRoot(cwd));
736
+ Effect.gen(function* (_) {
737
+ const path = yield* _(Path.Path);
738
+ const cwd = process.cwd();
739
+ const root = resolveStateRoot(path, cwd);
740
+ yield* _(Effect.log(root));
741
+ }).pipe(Effect.asVoid);
742
+ Effect.gen(function* (_) {
743
+ const path = yield* _(Path.Path);
744
+ const root = resolveStateRoot(path, process.cwd());
745
+ const output = yield* _(gitCapture(root, ["status", "-sb", "--porcelain=v1"], gitBaseEnv));
746
+ yield* _(Effect.log(output.trim().length > 0 ? output.trimEnd() : "(clean)"));
747
+ }).pipe(Effect.asVoid);
748
+ Effect.gen(function* (_) {
749
+ const fs = yield* _(FileSystem.FileSystem);
750
+ const path = yield* _(Path.Path);
751
+ const root = resolveStateRoot(path, process.cwd());
752
+ const originUrlExit = yield* _(gitExitCode(root, ["remote", "get-url", "origin"], gitBaseEnv));
753
+ if (originUrlExit !== successExitCode) {
754
+ yield* _(git(root, ["pull", "--rebase"], gitBaseEnv));
755
+ return;
756
+ }
757
+ const originUrl = yield* _(gitCapture(root, ["remote", "get-url", "origin"], gitBaseEnv).pipe(Effect.map((value) => value.trim())));
758
+ const token = yield* _(resolveGithubToken(fs, path, root));
759
+ const effect = token && token.length > 0 && isGithubHttpsRemote(originUrl) ? withGithubAskpassEnv(token, (env) => git(root, ["pull", "--rebase"], env)) : git(root, ["pull", "--rebase"], gitBaseEnv);
760
+ yield* _(effect);
761
+ }).pipe(Effect.asVoid);
762
+ Effect.gen(function* (_) {
763
+ const fs = yield* _(FileSystem.FileSystem);
764
+ const path = yield* _(Path.Path);
765
+ const root = resolveStateRoot(path, process.cwd());
766
+ const originUrlExit = yield* _(gitExitCode(root, ["remote", "get-url", "origin"], gitBaseEnv));
767
+ if (originUrlExit !== successExitCode) {
768
+ yield* _(git(root, ["push", "-u", "origin", "HEAD"], gitBaseEnv));
769
+ return;
770
+ }
771
+ const originUrl = yield* _(gitCapture(root, ["remote", "get-url", "origin"], gitBaseEnv).pipe(Effect.map((value) => value.trim())));
772
+ const token = yield* _(resolveGithubToken(fs, path, root));
773
+ const effect = token && token.length > 0 && isGithubHttpsRemote(originUrl) ? withGithubAskpassEnv(token, (env) => git(root, ["push", "-u", "origin", "HEAD"], env)) : git(root, ["push", "-u", "origin", "HEAD"], gitBaseEnv);
774
+ yield* _(effect);
775
+ }).pipe(Effect.asVoid);
782
776
  pipe(loadProjectIndex(), Effect.flatMap((index) => index === null ? Effect.void : forEachProjectStatus(index.configPaths, (status) => pipe(Effect.log(renderProjectStatusHeader(status)), Effect.zipRight(runDockerComposeDown(status.projectDir).pipe(Effect.catchTag("DockerCommandError", (error) => Effect.logWarning(`docker compose down failed for ${status.projectDir}: ${renderError(error)}`))))))), Effect.asVoid);
783
777
  const listProjects = pipe(withProjectIndexAndSsh((index, sshKey) => Effect.gen(function* (_) {
784
778
  const available = [];
@@ -824,6 +818,37 @@ Effect.asVoid(withProjectIndexAndSsh((index, sshKey) => forEachProjectStatus(ind
824
818
  onSuccess: () => Effect.void
825
819
  })))))));
826
820
  Duration.seconds(1);
821
+ const ChunkManifestSchema = Schema.Struct({
822
+ original: Schema.String,
823
+ originalSize: Schema.Number,
824
+ parts: Schema.Array(Schema.String),
825
+ splitAt: Schema.Number,
826
+ partsCount: Schema.Number,
827
+ createdAt: Schema.String
828
+ });
829
+ Schema.parseJson(ChunkManifestSchema);
830
+ const SessionManifestSchema = Schema.Struct({
831
+ schemaVersion: Schema.Literal(1),
832
+ mode: Schema.Literal("session"),
833
+ snapshotId: Schema.String,
834
+ createdAtUtc: Schema.String,
835
+ repo: Schema.Struct({
836
+ originUrl: Schema.String,
837
+ head: Schema.String,
838
+ branch: Schema.String
839
+ }),
840
+ artifacts: Schema.Struct({
841
+ worktreePatchChunks: Schema.String,
842
+ codexChunks: Schema.String,
843
+ codexSharedChunks: Schema.String,
844
+ envGlobalFile: Schema.optionalWith(Schema.Union(Schema.String, Schema.Null), { default: () => null }),
845
+ envProjectFile: Schema.optionalWith(Schema.Union(Schema.String, Schema.Null), { default: () => null })
846
+ }),
847
+ rebuild: Schema.optionalWith(Schema.Struct({
848
+ commands: Schema.Array(Schema.String)
849
+ }), { default: () => ({ commands: [] }) })
850
+ });
851
+ Schema.parseJson(SessionManifestSchema);
827
852
  const usageText = [
828
853
  "Usage:",
829
854
  " pnpm docker-git",