@kybernesis/create 0.23.3 → 0.24.1

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.
package/README.md CHANGED
@@ -116,6 +116,12 @@ run and upgrade exits with status 2, prints that the gate can only run on the
116
116
  host, and gives the exact ssh command. It does not print "Do NOT deploy" because
117
117
  no eval exercised the code.
118
118
 
119
+ `kyb upgrade --check` plans only: it prints what would change as one
120
+ `KYB_UPGRADE_PLAN {"ok":true,"changes":[{"name","from","to"}],"resetsConversations":false}`
121
+ line and installs, repairs and renews nothing. `@kybernesis/manage` reads it for
122
+ the owner's Update card, and runs the real upgrade (with its eval gate, then a
123
+ rebuild and a safe restart, or everything put back) from that card.
124
+
119
125
  Use `kyb upgrade --host` to run the gate over ssh after the upgrade. The target
120
126
  uses the same defaults as `kyb deploy`: `EVE_SSH_HOST`, then
121
127
  `$EXE_VM_NAME.exe.xyz`; `--host=<ssh-target>` overrides them. The remote project
package/dist/cli.js CHANGED
@@ -84,7 +84,7 @@ const COMMANDS = {
84
84
  credential: "Write the agent credential onto a host (--local to stay here).",
85
85
  register: "Register this agent with the control plane (--name, --url).",
86
86
  deploy: "Deploy this agent to its host (--no-env to leave the env file alone).",
87
- upgrade: "Bring @kybernesis packages and eve to certified versions (--host, --skip-eval, --allow-stale, --yes).",
87
+ upgrade: "Bring @kybernesis packages and eve to certified versions (--check, --host, --skip-eval, --allow-stale, --yes).",
88
88
  tui: "Talk to your agents in the terminal.",
89
89
  version: "Print the version of this tool.",
90
90
  };
@@ -116,6 +116,7 @@ function usage(command) {
116
116
  --host[=<target>] ${dim("run the eval gate over ssh; defaults like kyb deploy")}
117
117
  --skip-eval ${dim("skip the eval gate")}
118
118
  --allow-stale ${dim("continue even when this kyb is behind the published version")}
119
+ --check ${dim("print what would change (one KYB_UPGRADE_PLAN json line) and change nothing")}
119
120
  --yes, -y ${dim("confirm an eve version change noninteractively")}
120
121
  `);
121
122
  }
@@ -179,6 +180,7 @@ switch (command) {
179
180
  case "upgrade":
180
181
  await upgrade({
181
182
  allowStale: rest.includes("--allow-stale"),
183
+ check: rest.includes("--check"),
182
184
  host: optionalValueFlag(rest, "host"),
183
185
  skipEval: rest.includes("--skip-eval"),
184
186
  yes: rest.includes("--yes") || rest.includes("-y"),
package/dist/upgrade.d.ts CHANGED
@@ -29,6 +29,12 @@ export type GithubToolsMountRepair = {
29
29
  export declare function repairGithubToolsMount(cwd: string): GithubToolsMountRepair;
30
30
  export interface UpgradeOptions {
31
31
  allowStale?: boolean;
32
+ /**
33
+ * Plan only: print what an upgrade would change as one `KYB_UPGRADE_PLAN <json>`
34
+ * line and touch nothing (no install, no repair, no renewal of kyb itself).
35
+ * The owner's Update card reads it through @kybernesis/manage.
36
+ */
37
+ check?: boolean;
32
38
  host?: true | string;
33
39
  skipEval?: boolean;
34
40
  yes?: boolean;
@@ -40,5 +46,7 @@ type EvalGatePreflight = {
40
46
  reasons: string[];
41
47
  };
42
48
  export declare function preflightEvalGate(cwd: string): Promise<EvalGatePreflight>;
49
+ /** The one line `kyb upgrade --check` prints for a program to read; everything else it prints is for people. */
50
+ export declare const UPGRADE_PLAN_PREFIX = "KYB_UPGRADE_PLAN ";
43
51
  export declare function upgrade(options?: UpgradeOptions): Promise<void>;
44
52
  export {};
package/dist/upgrade.js CHANGED
@@ -861,6 +861,11 @@ function startStoppedBridge(cwd, unit) {
861
861
  ` systemctl status ${unit}`);
862
862
  return false;
863
863
  }
864
+ /** The one line `kyb upgrade --check` prints for a program to read; everything else it prints is for people. */
865
+ export const UPGRADE_PLAN_PREFIX = "KYB_UPGRADE_PLAN ";
866
+ function printPlan(plan) {
867
+ console.log(`${UPGRADE_PLAN_PREFIX}${JSON.stringify(plan)}`);
868
+ }
864
869
  export async function upgrade(options = {}) {
865
870
  console.log(bold("\nkyb upgrade — checking @kybernesis/* and eve against npm\n"));
866
871
  const cwd = process.cwd();
@@ -871,7 +876,7 @@ export async function upgrade(options = {}) {
871
876
  ...(pkg.devDependencies ?? {}),
872
877
  };
873
878
  const selfVersion = checkSelfVersion();
874
- if (selfVersion.kind === "stale" && !options.allowStale) {
879
+ if (selfVersion.kind === "stale" && !options.allowStale && !options.check) {
875
880
  /**
876
881
  * A stale kyb that is the PROJECT's own dependency renews itself.
877
882
  *
@@ -957,6 +962,8 @@ export async function upgrade(options = {}) {
957
962
  console.log(dim(` note: eve@${eveLatest} exists upstream; ${EVE_VERSION} is the newest Kybernesis-certified version.`));
958
963
  }
959
964
  if (unresolved.size > 0) {
965
+ if (options.check)
966
+ printPlan({ ok: false, unresolved: [...unresolved].sort(), changes: [], resetsConversations: false });
960
967
  fail(`\nUpgrade planning failed for ${[...unresolved].sort().join(", ")}. ` +
961
968
  `No manifest, dependency tree, or service state was changed.`);
962
969
  return;
@@ -982,15 +989,39 @@ export async function upgrade(options = {}) {
982
989
  installedMajorMinor &&
983
990
  targetMajorMinor &&
984
991
  (installedMajorMinor[0] !== targetMajorMinor[0] || installedMajorMinor[1] !== targetMajorMinor[1]));
992
+ // eve already at the pin but saved as a range (an agent scaffolded before
993
+ // the pin was exact, or installed by hand): pin it in place. The version
994
+ // does not move, so this is not a change to plan or approve.
995
+ const loosePin = Boolean(eveTarget && eveInstalled === eveTarget && targets.some((target) => target.name === "eve" && target.currentRange !== eveTarget));
985
996
  const changed = [...new Set(targets
986
997
  .filter((target) => target.installedVersion !== target.targetVersion && !(target.name === "eve" && versionLt(target.targetVersion, target.installedVersion)))
987
998
  .map((target) => `${target.name}@${target.targetVersion}`))];
999
+ if (options.check) {
1000
+ printPlan({
1001
+ ok: true,
1002
+ eve: { installed: eveInstalled ?? null, certified: EVE_VERSION },
1003
+ changes: [...new Map(targets
1004
+ .filter((target) => changed.includes(`${target.name}@${target.targetVersion}`))
1005
+ .map((target) => [target.name, { name: target.name, from: target.installedVersion, to: target.targetVersion }])).values()],
1006
+ resetsConversations: eveChanged,
1007
+ });
1008
+ return;
1009
+ }
988
1010
  if (eveChanged && !(await approveEveChange(cwd, options.yes === true, Boolean(deps["@kybernesis/buzz"])))) {
989
1011
  process.exitCode = 1;
990
1012
  return;
991
1013
  }
992
1014
  repairLocalQueueTimeouts(cwd, deps);
993
1015
  if (changed.length === 0) {
1016
+ if (loosePin) {
1017
+ const pinned = commandResult("npm", ["install", "--save-exact", `eve@${eveTarget}`], cwd);
1018
+ if (pinned.status !== 0) {
1019
+ console.log(red(`\nCould not pin eve exactly to ${eveTarget}:\n${pinned.stderr.trim()}`));
1020
+ process.exitCode = 1;
1021
+ return;
1022
+ }
1023
+ console.log(` ${green("✓")} eve pinned exactly to ${eveTarget} ${dim("(was a range)")}`);
1024
+ }
994
1025
  console.log(`\n${green("Everything is at latest certified versions.")}\n`);
995
1026
  repairBuzzSetup(cwd, deps);
996
1027
  repairRemovedDefaultTools(cwd);
@@ -1025,7 +1056,7 @@ export async function upgrade(options = {}) {
1025
1056
  }
1026
1057
  // Outside a clean install, eve goes in with --save-exact for the same
1027
1058
  // reason rewriteManifest pins it: npm would save a caret by default.
1028
- const eveChange = changed.find((spec) => spec.startsWith("eve@"));
1059
+ const eveChange = changed.find((spec) => spec.startsWith("eve@")) ?? (loosePin ? `eve@${eveTarget}` : undefined);
1029
1060
  const others = changed.filter((spec) => spec !== eveChange);
1030
1061
  /**
1031
1062
  * A third-party extension whose declared peer range trails the certified
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.23.3",
3
+ "version": "0.24.1",
4
4
  "description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent \u2014 plus doctor and upgrade.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",