@aui.io/apollo 0.1.84 → 0.1.87

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 (84) hide show
  1. package/README.md +4 -4
  2. package/node_modules/@aui.io/apollo-cli/dist/src/commands/bundles.js +239 -33
  3. package/node_modules/@aui.io/apollo-cli/dist/src/commands/bundles.js.map +1 -1
  4. package/node_modules/@aui.io/apollo-cli/dist/src/commands/hierarchy.js +2 -2
  5. package/node_modules/@aui.io/apollo-cli/dist/src/commands/hierarchy.js.map +1 -1
  6. package/node_modules/@aui.io/apollo-cli/dist/src/commands/mockdb.js +9 -8
  7. package/node_modules/@aui.io/apollo-cli/dist/src/commands/mockdb.js.map +1 -1
  8. package/node_modules/@aui.io/apollo-cli/dist/src/commands/operate.js +31 -12
  9. package/node_modules/@aui.io/apollo-cli/dist/src/commands/operate.js.map +1 -1
  10. package/node_modules/@aui.io/apollo-cli/package.json +3 -3
  11. package/node_modules/@aui.io/apollo-core/dist/src/api/agent-settings.d.ts +17 -0
  12. package/node_modules/@aui.io/apollo-core/dist/src/api/agent-settings.js +4 -0
  13. package/node_modules/@aui.io/apollo-core/dist/src/api/agent-settings.js.map +1 -1
  14. package/node_modules/@aui.io/apollo-core/dist/src/api/builder-api.d.ts +36 -0
  15. package/node_modules/@aui.io/apollo-core/dist/src/api/builder-api.js +6 -0
  16. package/node_modules/@aui.io/apollo-core/dist/src/api/builder-api.js.map +1 -1
  17. package/node_modules/@aui.io/apollo-core/dist/src/api/management.d.ts +108 -4
  18. package/node_modules/@aui.io/apollo-core/dist/src/api/management.js +198 -18
  19. package/node_modules/@aui.io/apollo-core/dist/src/api/management.js.map +1 -1
  20. package/node_modules/@aui.io/apollo-core/dist/src/api/mock-db.d.ts +1 -5
  21. package/node_modules/@aui.io/apollo-core/dist/src/api/mock-db.js +4 -10
  22. package/node_modules/@aui.io/apollo-core/dist/src/api/mock-db.js.map +1 -1
  23. package/node_modules/@aui.io/apollo-core/dist/src/bundle/baseline.d.ts +12 -2
  24. package/node_modules/@aui.io/apollo-core/dist/src/bundle/baseline.js +67 -15
  25. package/node_modules/@aui.io/apollo-core/dist/src/bundle/baseline.js.map +1 -1
  26. package/node_modules/@aui.io/apollo-core/dist/src/bundle/files.d.ts +13 -0
  27. package/node_modules/@aui.io/apollo-core/dist/src/bundle/files.js +20 -0
  28. package/node_modules/@aui.io/apollo-core/dist/src/bundle/files.js.map +1 -1
  29. package/node_modules/@aui.io/apollo-core/dist/src/bundle/lineage.d.ts +29 -0
  30. package/node_modules/@aui.io/apollo-core/dist/src/bundle/lineage.js +65 -0
  31. package/node_modules/@aui.io/apollo-core/dist/src/bundle/lineage.js.map +1 -0
  32. package/node_modules/@aui.io/apollo-core/dist/src/bundle/materialize.d.ts +25 -4
  33. package/node_modules/@aui.io/apollo-core/dist/src/bundle/materialize.js +78 -8
  34. package/node_modules/@aui.io/apollo-core/dist/src/bundle/materialize.js.map +1 -1
  35. package/node_modules/@aui.io/apollo-core/dist/src/config/index.d.ts +2 -0
  36. package/node_modules/@aui.io/apollo-core/dist/src/config/index.js +7 -0
  37. package/node_modules/@aui.io/apollo-core/dist/src/config/index.js.map +1 -1
  38. package/node_modules/@aui.io/apollo-core/dist/src/config/mockdb-keys.d.ts +3 -0
  39. package/node_modules/@aui.io/apollo-core/dist/src/config/mockdb-keys.js +5 -0
  40. package/node_modules/@aui.io/apollo-core/dist/src/config/mockdb-keys.js.map +1 -1
  41. package/node_modules/@aui.io/apollo-core/dist/src/errors/index.d.ts +75 -0
  42. package/node_modules/@aui.io/apollo-core/dist/src/errors/index.js +109 -0
  43. package/node_modules/@aui.io/apollo-core/dist/src/errors/index.js.map +1 -1
  44. package/node_modules/@aui.io/apollo-core/dist/src/index.d.ts +1 -1
  45. package/node_modules/@aui.io/apollo-core/dist/src/index.js.map +1 -1
  46. package/node_modules/@aui.io/apollo-core/dist/src/output/index.d.ts +2 -0
  47. package/node_modules/@aui.io/apollo-core/dist/src/output/index.js +5 -1
  48. package/node_modules/@aui.io/apollo-core/dist/src/output/index.js.map +1 -1
  49. package/node_modules/@aui.io/apollo-core/dist/src/services/bundles.d.ts +76 -2
  50. package/node_modules/@aui.io/apollo-core/dist/src/services/bundles.js +225 -70
  51. package/node_modules/@aui.io/apollo-core/dist/src/services/bundles.js.map +1 -1
  52. package/node_modules/@aui.io/apollo-core/dist/src/services/doctor.js +4 -3
  53. package/node_modules/@aui.io/apollo-core/dist/src/services/doctor.js.map +1 -1
  54. package/node_modules/@aui.io/apollo-core/dist/src/services/hierarchy.d.ts +14 -21
  55. package/node_modules/@aui.io/apollo-core/dist/src/services/hierarchy.js +17 -27
  56. package/node_modules/@aui.io/apollo-core/dist/src/services/hierarchy.js.map +1 -1
  57. package/node_modules/@aui.io/apollo-core/dist/src/services/messaging.d.ts +15 -9
  58. package/node_modules/@aui.io/apollo-core/dist/src/services/messaging.js +41 -18
  59. package/node_modules/@aui.io/apollo-core/dist/src/services/messaging.js.map +1 -1
  60. package/node_modules/@aui.io/apollo-core/dist/src/services/mockdb.d.ts +4 -3
  61. package/node_modules/@aui.io/apollo-core/dist/src/services/mockdb.js +84 -117
  62. package/node_modules/@aui.io/apollo-core/dist/src/services/mockdb.js.map +1 -1
  63. package/node_modules/@aui.io/apollo-core/dist/src/services/versions.d.ts +88 -0
  64. package/node_modules/@aui.io/apollo-core/dist/src/services/versions.js +202 -0
  65. package/node_modules/@aui.io/apollo-core/dist/src/services/versions.js.map +1 -0
  66. package/node_modules/@aui.io/apollo-core/dist/src/skills/agents-page.js +2 -2
  67. package/node_modules/@aui.io/apollo-core/dist/src/skills/agents-page.js.map +1 -1
  68. package/node_modules/@aui.io/apollo-core/package.json +1 -1
  69. package/node_modules/@aui.io/apollo-tui/dist/src/actions/registry.js +13 -13
  70. package/node_modules/@aui.io/apollo-tui/dist/src/actions/registry.js.map +1 -1
  71. package/node_modules/@aui.io/apollo-tui/dist/src/activity/operationStore.js +2 -2
  72. package/node_modules/@aui.io/apollo-tui/dist/src/activity/operationStore.js.map +1 -1
  73. package/node_modules/@aui.io/apollo-tui/dist/src/author/data.js +3 -2
  74. package/node_modules/@aui.io/apollo-tui/dist/src/author/data.js.map +1 -1
  75. package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.d.ts +11 -19
  76. package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.js +90 -81
  77. package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.js.map +1 -1
  78. package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.d.ts +2 -0
  79. package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.js +6 -6
  80. package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.js.map +1 -1
  81. package/node_modules/@aui.io/apollo-tui/dist/src/opentui-app.js +21 -13
  82. package/node_modules/@aui.io/apollo-tui/dist/src/opentui-app.js.map +1 -1
  83. package/node_modules/@aui.io/apollo-tui/package.json +2 -2
  84. package/package.json +4 -4
package/README.md CHANGED
@@ -65,10 +65,10 @@ apollo validate
65
65
  # 5. Talk to it against the live runtime WITHOUT deploying, and read the trace
66
66
  apollo chat "I want to return an order" --local . --trace
67
67
 
68
- # 6. Push the change as a new version, then publish it when it is ready
68
+ # 6. Push the change, then make that SAVE the one customers get, when it is ready
69
69
  apollo push -m "Describe the change"
70
- apollo version list
71
- apollo version publish <tag>
70
+ apollo branch list
71
+ apollo branch live <tag> # `v3.7` ships exactly that save; `v3` ships its newest
72
72
  ```
73
73
 
74
74
  `apollo status` always tells you where you stand — which profile, organization, project,
@@ -87,7 +87,7 @@ to that agent.
87
87
  | **Project** | A container for agents | `project list / use / create` |
88
88
  | **Agent** | The program you edit and test | `agent list / create / import / use / unlink` |
89
89
  | **Checkout** | A local directory holding one agent: `.apollorc` + `bundle/src/` | `agent create / import` |
90
- | **Version** | A tagged snapshot of the program: draft → published → archived (publishing an older version is the rollback) | `version list / get / use / create / publish / archive`, `pull` / `push` |
90
+ | **Version** | A tagged snapshot of the program: draft → published → archived (publishing an older version is the rollback) | `branch list / get / use / create / publish / archive`, `pull` / `push` |
91
91
  | **Runtime** | The engine build the checkout is pinned to | `runtime list / show / use` |
92
92
 
93
93
  ## Commands
@@ -3,18 +3,24 @@ import { homedir } from "node:os";
3
3
  import path from "node:path";
4
4
  import { renderBundleAudit, renderValidation } from "@aui.io/apollo-core/bundle/validate.js";
5
5
  import { defaultCheckoutDir, listCheckouts, registerCheckout, } from "@aui.io/apollo-core/checkouts/registry.js";
6
+ import { branchName, branchState, continuedName, parseVersionTag } from "@aui.io/apollo-core/api/management.js";
7
+ import { requireUserId } from "@aui.io/apollo-core/config/index.js";
6
8
  import { ConfigError, ValidationError } from "@aui.io/apollo-core/errors/index.js";
7
9
  import { assertRuntimeAvailable } from "@aui.io/apollo-core/runtime-version/availability.js";
8
10
  import { adviseLocalBundle } from "@aui.io/apollo-core/services/advisor.js";
9
11
  import { buildAgent } from "@aui.io/apollo-core/services/builds.js";
10
12
  import { annotateRun, awaitRun, pullRun, receiptId, resolveRunId, startEvaluation, startRegression, } from "@aui.io/apollo-core/services/runs.js";
11
- import { importBundle, listVersions, localDiff, pullCheckout, pushBundle, remoteDiff, applyBundleFixes, bundleSourceRoot, validateLocalBundle, } from "@aui.io/apollo-core/services/bundles.js";
13
+ import { branchLog, importBundle, listVersions, localDiff, pullCheckout, pushBundle, remoteDiff, applyBundleFixes, bundleSourceRoot, validateLocalBundle, } from "@aui.io/apollo-core/services/bundles.js";
14
+ import { describeMerge, diffAgainst, fetchVersion, mergeFrom, } from "@aui.io/apollo-core/services/versions.js";
12
15
  import { checkoutSchemasDir, installSchemas, } from "@aui.io/apollo-core/schemas/install.js";
13
16
  import { createClients } from "@aui.io/apollo-core/services/clients.js";
14
17
  import { agentRuntimeVersion, listAgents, useVersion, } from "@aui.io/apollo-core/services/hierarchy.js";
15
18
  import { installAgentsPage } from "@aui.io/apollo-core/skills/agents-page.js";
16
19
  import { installSkills, } from "@aui.io/apollo-core/skills/install.js";
17
20
  import { resolvePickerId } from "./picker.js";
21
+ /** `branch log`'s default page, and apollo's internal page cap. */
22
+ const COMMIT_PAGE = 20;
23
+ const MAX_COMMIT_PAGE = 1000;
18
24
  function renderDiff(diff) {
19
25
  const rows = [
20
26
  ...diff.added.map((file) => `A ${file}`),
@@ -173,6 +179,16 @@ async function runAction(run, builder, kind, root, options) {
173
179
  }
174
180
  return true;
175
181
  }
182
+ /** A pulled or imported commit that still carries a secret file on the server
183
+ * (pushed before secret files stayed local): the file was not written and a
184
+ * local copy is untouched, but the values are stored with that commit. */
185
+ function warnServerSecrets(run, files, tag) {
186
+ if (!files?.length)
187
+ return;
188
+ run.output.warn(`${tag} still carries ${files.join(", ")} on the server, pushed before secret files stayed local. ` +
189
+ "It was not written here and any local copy is untouched, but its values are stored with that commit: " +
190
+ "rotate those credentials and keep them in the agent's vault (`apollo vault set <NAME>`).");
191
+ }
176
192
  function capitalize(word) {
177
193
  return word.charAt(0).toUpperCase() + word.slice(1);
178
194
  }
@@ -530,20 +546,46 @@ export function registerBundles(program, runtime) {
530
546
  });
531
547
  program
532
548
  .command("diff")
533
- .description("Compare local files with the last baseline")
549
+ .description("Compare local files with the last baseline, or with another version (a tag, a name, or live)")
550
+ .argument("[ref]", "a version to compare bundle/src with (fetched into bundle/tmp/versions/<tag>)")
534
551
  .option("--remote", "compare with the selected remote version")
535
- .action(async (options) => {
552
+ .action(async (ref, options) => {
536
553
  const run = runtime();
537
554
  const management = createClients(run.context).management;
555
+ if (ref) {
556
+ const result = await diffAgainst(run.context, management, ref);
557
+ run.output.result(result, () => [
558
+ `${result.fetched.tag} → bundle/src${result.fetched.relation ? ` (${result.fetched.relation}${result.fetched.base ? `; common base ${result.fetched.base}` : ""})` : ""}; their copy: bundle/tmp/versions/${result.fetched.tag}/`,
559
+ renderDiff(result.diff),
560
+ ].join("\n"));
561
+ return;
562
+ }
538
563
  const diff = options.remote
539
564
  ? await remoteDiff(run.context, management)
540
565
  : await localDiff(run.context);
541
566
  run.output.result(diff, (value) => renderDiff(value));
542
567
  });
568
+ program
569
+ .command("merge-from")
570
+ .description("Bring another branch's changes into bundle/src (three-way over the commit both came from); disputed regions keep your text and are listed")
571
+ .argument("<ref>", "the version to bring in — a tag, a line (v2), a name, or live")
572
+ .option("--only <paths...>", "bring in only these program paths (a cherry-pick)")
573
+ .option("--dry-run", "report what would change without writing")
574
+ .option("--resolved", "every disputed region is decided at your text: a newer commit of your own branch becomes the checkout's base")
575
+ .action(async (ref, options) => {
576
+ const run = runtime();
577
+ const clients = createClients(run.context);
578
+ const merged = await mergeFrom(run.context, clients, ref, {
579
+ ...(options.only ? { only: options.only } : {}),
580
+ dryRun: options.dryRun === true,
581
+ resolved: options.resolved === true,
582
+ });
583
+ run.output.result(merged, () => describeMerge(merged));
584
+ });
543
585
  program
544
586
  .command("pull")
545
587
  .description("Pull the selected agent version plus the schemas, skills and AGENTS.md its runtime serves")
546
- .option("--tag <tag>", "version tag")
588
+ .option("--tag <branch>", "switch to a branch and pull its newest commit (v2, its name, its newest tag, or live)")
547
589
  .option("--dir <directory>", "destination directory")
548
590
  .option("--force", "discard local bundle changes and match remote")
549
591
  .option("--skills <targets>", "comma-separated cursor,claude,opencode", "cursor,claude")
@@ -559,6 +601,7 @@ export function registerBundles(program, runtime) {
559
601
  skills: options.skills === false ? false : targets(String(options.skills)),
560
602
  schemas: options.schemas,
561
603
  });
604
+ warnServerSecrets(run, result.serverSecretFiles, result.pull.version_tag);
562
605
  run.output.result(result, () => {
563
606
  const removed = result.removed.length > 0
564
607
  ? ` Removed ${result.removed.length} local file(s) not in the remote bundle.`
@@ -566,9 +609,13 @@ export function registerBundles(program, runtime) {
566
609
  return [
567
610
  // Name the tag the pull landed on — a stale pin resolves to the
568
611
  // tip of its line, and this is where the checkout learns that.
569
- `Pulled ${Object.keys(result.files).length} files at ${result.pull.version_tag}${result.runtimeVersion ? ` on runtime ${result.runtimeVersion}` : ""}.${removed}`,
612
+ `Pulled ${Object.keys(result.files).length} files at ${result.pull.version_tag}${result.runtimeVersion ? ` on runtime ${result.runtimeVersion}` : ""}${result.live === result.pull.version_tag ? ", the commit customers get" : ""}.${removed}`,
613
+ ...liveGap(result.pull.version_tag, result.live),
570
614
  result.build
571
- ? `Build bundle v${result.build.version}: ${result.build.written.length} file(s) into bundle/build.`
615
+ ? `Build bundle v${result.build.version}: ${result.build.written.length} file(s) into bundle/build.` +
616
+ (result.build.askedFor
617
+ ? ` It belongs with ${result.build.programVersionTag ?? "another commit"}, not the ${result.build.askedFor} you pulled — nothing has been pushed for that commit yet.`
618
+ : "")
572
619
  : "",
573
620
  result.certificate
574
621
  ? `Certificate: ${result.certificate.revision ?? result.certificate.version} (${result.certificate.run}), ${result.certificate.cases.length} case(s) — bundle/build/certification.yaml.`
@@ -590,6 +637,7 @@ export function registerBundles(program, runtime) {
590
637
  .description("Validate and push local changes — a changed program mints a version, a changed bundle/build ships a build bundle")
591
638
  .option("-m, --message <message>", "commit message — recorded on the revision the push mints (not the version's label)")
592
639
  .option("--dry-run", "validate and show the diff without pushing")
640
+ .option("--new-version [name]", "push these changes as a new branch cut from the commit they are based on (the way out when the branch moved)")
593
641
  .action(async (options) => {
594
642
  const run = runtime();
595
643
  const clients = createClients(run.context);
@@ -598,6 +646,7 @@ export function registerBundles(program, runtime) {
598
646
  const result = await pushBundle(run.context, clients, {
599
647
  ...(options.message ? { commitMessage: options.message } : {}),
600
648
  dryRun: options.dryRun === true,
649
+ ...(options.newVersion !== undefined ? { newVersion: options.newVersion } : {}),
601
650
  });
602
651
  run.output.result(result, () => {
603
652
  // One report, both lanes: program paths bare, build paths prefixed —
@@ -623,10 +672,21 @@ export function registerBundles(program, runtime) {
623
672
  // one place the CLI shows it, since `version list` lists versions
624
673
  // (label, notes) and a revision's commit message lives elsewhere.
625
674
  `Pushed ${result.push.new_version_tag}${result.runtimeVersion ? ` on runtime ${result.runtimeVersion}` : ""}${result.push.commit_message ? ` — "${result.push.commit_message}"` : ""}.`,
675
+ ...(result.newBranch ? [newBranchLine(result.newBranch)] : []),
676
+ ...(result.live
677
+ ? [
678
+ `Customers still get ${result.live}; \`apollo branch live ${result.push.new_version_tag}\` ships this.`,
679
+ ]
680
+ : []),
626
681
  ]
627
682
  : []),
628
683
  ...(result.buildPush
629
- ? [`Pushed build bundle v${result.buildPush.version}.`]
684
+ ? [
685
+ `Pushed build bundle v${result.buildPush.version}` +
686
+ (result.buildPush.program_version_tag
687
+ ? ` — it belongs with ${result.buildPush.program_version_tag}.`
688
+ : "."),
689
+ ]
630
690
  : []),
631
691
  // Which gate cleared the pre-flight, when it was not the pinned one —
632
692
  // a push that rode the fallback says so rather than reading as the
@@ -651,7 +711,7 @@ export function registerBundles(program, runtime) {
651
711
  .description("Pull a cloud agent into a local checkout (interactive without an id; shell-init cds)")
652
712
  .argument("[agent-id]", "agent id")
653
713
  .option("--name <filter>", "filter the interactive picker by name")
654
- .option("--tag <tag>", "version tag (defaults to the live version)")
714
+ .option("--tag <branch>", "the branch to start on (v2, its name, its newest tag, or live); default: live, the branch customers are on")
655
715
  .option("--dir <directory>", "checkout directory (defaults to ~/apollo/<profile>/<env>/<name>-<id>)")
656
716
  .option("--skills <targets>", "comma-separated cursor,claude,opencode", "cursor,claude")
657
717
  .option("--force", "overwrite an existing local program")
@@ -705,6 +765,7 @@ export function registerBundles(program, runtime) {
705
765
  ...(options.tag ? { tag: options.tag } : {}),
706
766
  ...(runtimeVersion ? { runtimeVersion } : {}),
707
767
  });
768
+ warnServerSecrets(run, imported.serverSecretFiles, imported.pull.version_tag);
708
769
  // The public floor, filtered by the wire: the index the install
709
770
  // fetches carries only public skills, and its sweep deletes managed
710
771
  // packs the index no longer names (a re-import lands in the existing
@@ -757,8 +818,13 @@ export function registerBundles(program, runtime) {
757
818
  : {}),
758
819
  }, () => [
759
820
  `Imported ${Object.keys(imported.files).length} files into ${destination}${runtimeVersion ? ` on runtime ${runtimeVersion}` : ""}.`,
821
+ `On ${branchName(imported.pull)} at ${imported.pull.version_tag}${imported.live === imported.pull.version_tag ? ", the commit customers get" : ""}.`,
822
+ ...liveGap(imported.pull.version_tag, imported.live),
760
823
  imported.build
761
- ? `Build bundle v${imported.build.version}: ${imported.build.written.length} file(s) into bundle/build.`
824
+ ? `Build bundle v${imported.build.version}: ${imported.build.written.length} file(s) into bundle/build.` +
825
+ (imported.build.askedFor
826
+ ? ` It belongs with ${imported.build.programVersionTag ?? "another commit"}, not the ${imported.build.askedFor} you imported.`
827
+ : "")
762
828
  : "",
763
829
  authoringNote ?? "",
764
830
  removedSkills.length > 0
@@ -771,36 +837,152 @@ export function registerBundles(program, runtime) {
771
837
  registerVersions(program, runtime);
772
838
  }
773
839
  function registerVersions(program, runtime) {
774
- const version = program.command("version").description("Manage agent versions");
775
- version.command("list").action(async () => {
840
+ // A version IS a branch and a revision IS a commit — the model every
841
+ // surface speaks, so `branch` is the command. `version` stays as an alias:
842
+ // it is what this group was called, and anything already scripted against
843
+ // it keeps working.
844
+ const version = program
845
+ .command("branch")
846
+ .alias("version")
847
+ .description("Manage the agent's branches (lines of commits)");
848
+ version
849
+ .command("list")
850
+ .description("List the branches: name, state, newest commit, and the commit customers get")
851
+ .action(async () => {
776
852
  const run = runtime();
777
853
  const result = await listVersions(run.context, createClients(run.context).management);
854
+ const liveBranch = result.live_version_tag ? parseVersionTag(result.live_version_tag)?.major : undefined;
778
855
  run.output.result(result, (value) => value.results
779
- .map((item) => `${item.version_tag}\t${item.status}\t${item.label ?? ""}`)
780
- .join("\n") || "No versions.");
856
+ .map((item) => `${branchName(item)}\t${branchState(item.status)}\t${item.label ?? ""}\tnewest ${item.version_tag}${item.version_number === liveBranch ? `\tlive ${result.live_version_tag}` : ""}${item.parent_version_tag ? `\tfrom ${item.parent_version_tag}` : ""}`)
857
+ .join("\n") || "No branches.");
858
+ });
859
+ version
860
+ .command("log")
861
+ .argument("[ref]", "a branch (v2, its name, any of its commits, or live); default: the checkout's")
862
+ .option("--limit <n>", `how many commits, newest first (1 to ${MAX_COMMIT_PAGE})`)
863
+ .description("List a branch's commits, newest first, marking its newest and the live one")
864
+ .action(async (ref, options) => {
865
+ const run = runtime();
866
+ const limit = integer("--limit", options.limit) ?? COMMIT_PAGE;
867
+ if (limit < 1 || limit > MAX_COMMIT_PAGE) {
868
+ throw new ConfigError(`--limit takes 1 to ${MAX_COMMIT_PAGE} (got ${limit}).`);
869
+ }
870
+ const log = await branchLog(run.context, createClients(run.context).management, {
871
+ ...(ref ? { ref } : {}),
872
+ limit,
873
+ });
874
+ run.output.result(log, () => [
875
+ `${branchName(log.branch)}${log.branch.label ? ` (${log.branch.label})` : ""}: ${log.total} commit(s)${log.live ? `, live ${log.live}` : ""}`,
876
+ ...log.commits.map((commit) => `${commit.tag}\t${commit.created_at.slice(0, 16).replace("T", " ")} UTC\t${commit.author}\t${commit.message ?? ""}${commit.newest ? "\tnewest" : ""}${commit.live ? "\tlive" : ""}`),
877
+ log.total > log.commits.length
878
+ ? `${log.total - log.commits.length} older not shown (--limit ${Math.min(log.total, MAX_COMMIT_PAGE)} lists them).`
879
+ : "",
880
+ ]
881
+ .filter(Boolean)
882
+ .join("\n"));
883
+ });
884
+ version
885
+ .command("fetch")
886
+ .argument("<ref>", "a commit (v2.7), a branch (v2 = its newest commit), a branch name, or live")
887
+ .description("Pull another branch's program into bundle/tmp/versions/<tag>/ (scratch, never pushed)")
888
+ .action(async (ref) => {
889
+ const run = runtime();
890
+ const fetched = await fetchVersion(run.context, createClients(run.context).management, ref);
891
+ run.output.result(fetched, () => [
892
+ `Fetched ${fetched.tag}${fetched.version.label ? ` (${fetched.version.label})` : ""} — ${Object.keys(fetched.files).length} files into bundle/tmp/versions/${fetched.tag}/.`,
893
+ fetched.relation
894
+ ? `Relation to this checkout: ${fetched.relation}${fetched.base ? `; common base ${fetched.base}` : " — no common base, a merge would be two-way"}.`
895
+ : "",
896
+ ]
897
+ .filter(Boolean)
898
+ .join("\n"));
899
+ });
900
+ version
901
+ .command("label")
902
+ .argument("<ref>")
903
+ .argument("<name>", "the name people refer to the version by")
904
+ .description("Rename a branch — the name people refer to it by")
905
+ .action(async (ref, name) => {
906
+ const run = runtime();
907
+ const management = createClients(run.context).management;
908
+ const agentId = requiredAgent(run);
909
+ const selected = await management.resolveVersion(agentId, ref);
910
+ const result = await management.updateVersion(agentId, selected.id, { label: name });
911
+ run.output.result(result, () => `Renamed ${result.version_tag} to "${result.label ?? name}".`);
912
+ });
913
+ version
914
+ .command("lock")
915
+ .argument("<ref>")
916
+ .description("Close a branch to further commits — it says nothing about what customers get")
917
+ .action(async (ref) => {
918
+ const run = runtime();
919
+ const management = createClients(run.context).management;
920
+ const agentId = requiredAgent(run);
921
+ const selected = await management.resolveVersion(agentId, ref);
922
+ const result = await management.lockVersion(agentId, selected.id, run.context.userId ?? "apollo-cli");
923
+ run.output.result(result, () => `Locked ${result.version_tag} — no more commits land on it, and what customers get has not changed. ` +
924
+ `\`apollo branch live <ref>\` picks the commit they get; \`apollo branch unlock ${branchName(result)}\` reopens this one; ` +
925
+ `\`apollo branch create --from ${branchName(result)}\` continues the work in a new draft.`);
926
+ });
927
+ version
928
+ .command("unlock")
929
+ .argument("<ref>")
930
+ .description("Reopen a locked branch so commits land on it again")
931
+ .action(async (ref) => {
932
+ const run = runtime();
933
+ const management = createClients(run.context).management;
934
+ const agentId = requiredAgent(run);
935
+ const selected = await management.resolveVersion(agentId, ref);
936
+ const result = await management.unlockVersion(agentId, selected.id);
937
+ run.output.result(result, () => `Unlocked ${result.version_tag} — commits land on it again. ` +
938
+ `What customers get is unchanged: live is a commit, not a branch's status.`);
939
+ });
940
+ version
941
+ .command("live")
942
+ .argument("<ref>")
943
+ .description("Make one COMMIT live — the exact bundle customers get (also how you roll back)")
944
+ .action(async (ref) => {
945
+ const run = runtime();
946
+ const management = createClients(run.context).management;
947
+ const agentId = requiredAgent(run);
948
+ // A ref like `v3.7` names a COMMIT; `v3` or a label names the branch,
949
+ // and its newest commit goes live. `resolveRef` keeps the difference.
950
+ const { version: selected, tag } = await management.resolveRef(agentId, ref);
951
+ const live = await management.activateSave(agentId, selected.id, tag);
952
+ run.output.result(live, () => {
953
+ const got = live.active_version_tag ?? tag;
954
+ return got === tag
955
+ ? `Live: ${got}. Pushing to ${branchName(selected)} will not move it — \`apollo branch live\` does.`
956
+ : `Live: ${got} — NOT the ${tag} you asked for. This deployment ships a branch's newest commit only.`;
957
+ });
781
958
  });
782
959
  version
783
960
  .command("get")
784
- .argument("<tag>")
785
- .action(async (tag) => {
961
+ .argument("<ref>", "a commit (v2.7), a branch (v2 = its newest commit), a branch name, or live")
962
+ .description("Show one commit and its branch")
963
+ .action(async (ref) => {
786
964
  const run = runtime();
787
- const item = await createClients(run.context).management.resolveVersion(requiredAgent(run), tag);
788
- run.output.result(item, () => `${item.version_tag}\t${item.status}\t${item.label ?? ""}${item.notes ? `\n${item.notes}` : ""}`);
965
+ const { version: item, tag: commit } = await createClients(run.context).management.resolveRef(requiredAgent(run), ref);
966
+ run.output.result({ ...item, commit_tag: commit }, () => `${commit}\t${branchName(item)}\t${branchState(item.status)}\t${item.label ?? ""}${item.notes ? `\n${item.notes}` : ""}`);
789
967
  });
790
968
  version
791
969
  .command("use")
792
- .argument("<tag>")
793
- .description("Bind a version tag in this checkout (push base, pull default)")
794
- .action(async (tag) => {
970
+ .argument("<branch>", "v2, its name, its newest commit's tag, or live (the branch customers are on)")
971
+ .description("Work on a branch in this checkout: pushes land on it and pull follows its newest commit")
972
+ .action(async (ref) => {
795
973
  const run = runtime();
796
- const result = await useVersion(run.context, createClients(run.context).management, tag);
797
- run.output.result(result, () => result.version.version_tag === tag.trim()
798
- ? `Using ${result.version.version_tag} (${result.version.status}).`
799
- : `Using ${result.version.version_tag} (${result.version.status}) — resolved from ${tag}.`);
974
+ const previous = run.context.versionTag;
975
+ const result = await useVersion(run.context, createClients(run.context).management, ref);
976
+ const moved = previous !== result.tag;
977
+ run.output.result(result, () => [
978
+ `Using ${branchName(result.version)} (${branchState(result.version.status)}) at ${result.tag}${result.live === result.tag ? ", the commit customers get" : ""}.${moved ? " `apollo pull` brings its files." : ""}`,
979
+ ...liveGap(result.tag, result.live),
980
+ ].join("\n"));
800
981
  });
801
982
  version
802
983
  .command("create")
803
- .option("--from <tag>", "clone an existing version")
984
+ .description("Cut a new draft branch — from a branch's newest commit, from an exact commit (v3.7), from a template, or empty")
985
+ .option("--from <ref>", "cut from a branch or an exact commit (v3, v3.7, a branch name, or live)")
804
986
  .option("--template <id>", "create from a template")
805
987
  .option("--label <label>")
806
988
  .option("--notes <notes>")
@@ -808,28 +990,34 @@ function registerVersions(program, runtime) {
808
990
  const run = runtime();
809
991
  const management = createClients(run.context).management;
810
992
  const agentId = requiredAgent(run);
811
- const fromVersion = options.from
812
- ? (await management.resolveVersion(agentId, options.from)).id
813
- : undefined;
993
+ const from = options.from ? await management.resolveRef(agentId, options.from) : undefined;
814
994
  const created = await management.createVersion(agentId, {
815
- source: fromVersion ? "version" : options.template ? "template" : "empty",
816
- ...(fromVersion ? { fromVersion } : {}),
995
+ source: from ? "version" : options.template ? "template" : "empty",
996
+ createdBy: requireUserId(run.context, "branch"),
997
+ ...(from ? { fromVersion: from.version.id, fromVersionTag: from.tag } : {}),
817
998
  ...(options.template ? { fromTemplate: options.template } : {}),
818
- ...(options.label ? { label: options.label } : {}),
999
+ ...(options.label
1000
+ ? { label: options.label }
1001
+ : from
1002
+ ? { label: continuedName(from.version) }
1003
+ : {}),
819
1004
  ...(options.notes ? { notes: options.notes } : {}),
820
1005
  });
821
- run.output.result(created, () => `Created ${created.version_tag}.`);
1006
+ run.output.result(created, () => `Created ${created.version_tag}${from ? ` from ${from.tag}` : ""}.` +
1007
+ ` \`apollo branch use ${branchName(created)}\` then \`apollo pull\` to work on it.`);
822
1008
  });
823
1009
  version
824
1010
  .command("publish")
825
1011
  .argument("<tag>")
1012
+ .description("The old one-verb ladder: lock the branch AND ship its newest commit. Prefer `branch live`")
826
1013
  .action(async (tag) => {
827
1014
  const run = runtime();
828
1015
  const management = createClients(run.context).management;
829
1016
  const agentId = requiredAgent(run);
830
1017
  const selected = await management.resolveVersion(agentId, tag);
831
1018
  const result = await management.publishVersion(agentId, selected.id);
832
- run.output.result(result, () => `Published ${result.version_tag}.`);
1019
+ run.output.result(result, () => `Published ${result.version_tag} — locked the branch and shipped its newest commit. ` +
1020
+ `\`apollo branch live <ref>\` ships a commit without locking anything, and can ship an older one.`);
833
1021
  });
834
1022
  version
835
1023
  .command("archive")
@@ -848,4 +1036,22 @@ function requiredAgent(run) {
848
1036
  throw new ConfigError("Select or import an agent first.");
849
1037
  return run.context.agentId;
850
1038
  }
1039
+ /** What a push that started a branch says: which branch, cut from where, and why. */
1040
+ function newBranchLine(started) {
1041
+ const why = started.closed
1042
+ ? `${started.closed.branch} is ${branchState(started.closed.status)}, so the push started`
1043
+ : "The push started";
1044
+ return `${why} a new branch: ${started.branch} "${started.label}", cut from ${started.from}. This checkout now works on ${started.branch}.`;
1045
+ }
1046
+ /** When a command followed `live` past the commit customers get: how far behind that is, and how to reach it. */
1047
+ function liveGap(at, live) {
1048
+ if (live === undefined || live === at)
1049
+ return [];
1050
+ const behind = (parseVersionTag(at)?.revision ?? 0) - (parseVersionTag(live)?.revision ?? 0);
1051
+ return [
1052
+ `Customers get ${live}, ${behind} ${behind === 1 ? "commit" : "commits"} behind:`,
1053
+ ` apollo chat --at live "…" talk to it`,
1054
+ ` apollo branch create --from live start a fix from it`,
1055
+ ];
1056
+ }
851
1057
  //# sourceMappingURL=bundles.js.map