@horizon36596/zenith-cli 0.1.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +18 -0
  3. package/README.md +207 -0
  4. package/dist/autoLoad.d.ts +54 -0
  5. package/dist/autoLoad.d.ts.map +1 -0
  6. package/dist/autoLoad.js +139 -0
  7. package/dist/autoLoad.js.map +1 -0
  8. package/dist/calibrateFit.d.ts +38 -0
  9. package/dist/calibrateFit.d.ts.map +1 -0
  10. package/dist/calibrateFit.js +69 -0
  11. package/dist/calibrateFit.js.map +1 -0
  12. package/dist/commandLibrary.d.ts +19 -0
  13. package/dist/commandLibrary.d.ts.map +1 -0
  14. package/dist/commandLibrary.js +27 -0
  15. package/dist/commandLibrary.js.map +1 -0
  16. package/dist/commands/calibrate.d.ts +18 -0
  17. package/dist/commands/calibrate.d.ts.map +1 -0
  18. package/dist/commands/calibrate.js +116 -0
  19. package/dist/commands/calibrate.js.map +1 -0
  20. package/dist/commands/codegen.d.ts +12 -0
  21. package/dist/commands/codegen.d.ts.map +1 -0
  22. package/dist/commands/codegen.js +63 -0
  23. package/dist/commands/codegen.js.map +1 -0
  24. package/dist/commands/deploy.d.ts +63 -0
  25. package/dist/commands/deploy.d.ts.map +1 -0
  26. package/dist/commands/deploy.js +181 -0
  27. package/dist/commands/deploy.js.map +1 -0
  28. package/dist/commands/diff.d.ts +11 -0
  29. package/dist/commands/diff.d.ts.map +1 -0
  30. package/dist/commands/diff.js +68 -0
  31. package/dist/commands/diff.js.map +1 -0
  32. package/dist/commands/estimate.d.ts +9 -0
  33. package/dist/commands/estimate.d.ts.map +1 -0
  34. package/dist/commands/estimate.js +81 -0
  35. package/dist/commands/estimate.js.map +1 -0
  36. package/dist/commands/init.d.ts +10 -0
  37. package/dist/commands/init.d.ts.map +1 -0
  38. package/dist/commands/init.js +67 -0
  39. package/dist/commands/init.js.map +1 -0
  40. package/dist/commands/new.d.ts +10 -0
  41. package/dist/commands/new.d.ts.map +1 -0
  42. package/dist/commands/new.js +28 -0
  43. package/dist/commands/new.js.map +1 -0
  44. package/dist/commands/propose.d.ts +15 -0
  45. package/dist/commands/propose.d.ts.map +1 -0
  46. package/dist/commands/propose.js +67 -0
  47. package/dist/commands/propose.js.map +1 -0
  48. package/dist/commands/render.d.ts +16 -0
  49. package/dist/commands/render.d.ts.map +1 -0
  50. package/dist/commands/render.js +90 -0
  51. package/dist/commands/render.js.map +1 -0
  52. package/dist/commands/sim.d.ts +31 -0
  53. package/dist/commands/sim.d.ts.map +1 -0
  54. package/dist/commands/sim.js +213 -0
  55. package/dist/commands/sim.js.map +1 -0
  56. package/dist/commands/validate.d.ts +19 -0
  57. package/dist/commands/validate.d.ts.map +1 -0
  58. package/dist/commands/validate.js +97 -0
  59. package/dist/commands/validate.js.map +1 -0
  60. package/dist/coreGate.d.ts +19 -0
  61. package/dist/coreGate.d.ts.map +1 -0
  62. package/dist/coreGate.js +23 -0
  63. package/dist/coreGate.js.map +1 -0
  64. package/dist/fieldImage.d.ts +19 -0
  65. package/dist/fieldImage.d.ts.map +1 -0
  66. package/dist/fieldImage.js +63 -0
  67. package/dist/fieldImage.js.map +1 -0
  68. package/dist/findings.d.ts +20 -0
  69. package/dist/findings.d.ts.map +1 -0
  70. package/dist/findings.js +57 -0
  71. package/dist/findings.js.map +1 -0
  72. package/dist/index.d.ts +32 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +194 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/javaCodegen.d.ts +44 -0
  77. package/dist/javaCodegen.d.ts.map +1 -0
  78. package/dist/javaCodegen.js +537 -0
  79. package/dist/javaCodegen.js.map +1 -0
  80. package/dist/launcher.d.ts +39 -0
  81. package/dist/launcher.d.ts.map +1 -0
  82. package/dist/launcher.js +153 -0
  83. package/dist/launcher.js.map +1 -0
  84. package/dist/lib.d.ts +13 -0
  85. package/dist/lib.d.ts.map +1 -0
  86. package/dist/lib.js +13 -0
  87. package/dist/lib.js.map +1 -0
  88. package/dist/project.d.ts +33 -0
  89. package/dist/project.d.ts.map +1 -0
  90. package/dist/project.js +104 -0
  91. package/dist/project.js.map +1 -0
  92. package/dist/skeletons.d.ts +16 -0
  93. package/dist/skeletons.d.ts.map +1 -0
  94. package/dist/skeletons.js +120 -0
  95. package/dist/skeletons.js.map +1 -0
  96. package/dist/traceReport.d.ts +75 -0
  97. package/dist/traceReport.d.ts.map +1 -0
  98. package/dist/traceReport.js +91 -0
  99. package/dist/traceReport.js.map +1 -0
  100. package/dist/version.d.ts +3 -0
  101. package/dist/version.d.ts.map +1 -0
  102. package/dist/version.js +4 -0
  103. package/dist/version.js.map +1 -0
  104. package/package.json +71 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Turns `zenith.json`'s `sim.command`, already split into argv, into an executable and arguments
3
+ * that `spawn` can start with `shell: false` on this platform. `zenith sim` and the desktop app's
4
+ * High-fidelity sim button both launch through this, so a command that runs in one runs in the other.
5
+ *
6
+ * The argv safety rule holds throughout:
7
+ * nothing here ever builds a shell string from the command's words, except for the one case Node
8
+ * leaves no choice about, a Windows batch file, and that is refused unless every word is one cmd.exe
9
+ * reads literally.
10
+ *
11
+ * Windows is the platform that needs this. `./gradlew` is a POSIX script and Node refuses to spawn a
12
+ * `.bat` or `.cmd` file without a shell, so on Windows a Gradle-wrapper command runs the wrapper the
13
+ * way `gradlew.bat` itself does, `java -classpath gradle/wrapper/gradle-wrapper.jar
14
+ * org.gradle.wrapper.GradleWrapperMain`, which needs no shell and lets a missing Java be reported in
15
+ * plain words before anything starts.
16
+ */
17
+ import { existsSync } from "node:fs";
18
+ import { posix, win32 } from "node:path";
19
+ import { splitShellWords } from "./commands/sim.js";
20
+ /** A sim command that cannot be launched, with a message written for the person running it. */
21
+ export class LauncherError extends Error {
22
+ constructor(message) {
23
+ super(message);
24
+ this.name = "LauncherError";
25
+ }
26
+ }
27
+ /** What a missing tool is, so the message can say what to install. */
28
+ export class MissingToolError extends LauncherError {
29
+ tool;
30
+ constructor(tool, message) {
31
+ super(message);
32
+ this.tool = tool;
33
+ this.name = "MissingToolError";
34
+ }
35
+ }
36
+ export const hostPlatform = () => ({ platform: process.platform, env: process.env, exists: existsSync });
37
+ const JAVA_MISSING = "High-fidelity sim runs the robot code's own simulator, which needs Java, and no Java was found on this computer. Install a Java 17 or newer JDK (Eclipse Temurin is free), then try again.";
38
+ /** The `java` executable Gradle would use: `JAVA_HOME` first, then the `PATH`. */
39
+ export function findJava(host = hostPlatform()) {
40
+ const path = host.platform === "win32" ? win32 : posix;
41
+ const exe = host.platform === "win32" ? "java.exe" : "java";
42
+ const javaHome = host.env["JAVA_HOME"];
43
+ if (javaHome !== undefined && javaHome.trim() !== "") {
44
+ const candidate = path.join(javaHome.trim().replace(/^"|"$/g, ""), "bin", exe);
45
+ if (host.exists(candidate))
46
+ return candidate;
47
+ throw new MissingToolError("java", `JAVA_HOME is set to ${javaHome}, but there is no ${exe} in its bin folder. Point JAVA_HOME at a Java 17 or newer JDK, or remove it, then try again.`);
48
+ }
49
+ const pathVar = host.env["PATH"] ?? host.env["Path"] ?? "";
50
+ for (const dir of pathVar.split(host.platform === "win32" ? ";" : ":")) {
51
+ if (dir.trim() === "")
52
+ continue;
53
+ const candidate = path.join(dir.trim().replace(/^"|"$/g, ""), exe);
54
+ if (host.exists(candidate))
55
+ return candidate;
56
+ }
57
+ throw new MissingToolError("java", JAVA_MISSING);
58
+ }
59
+ const GRADLEW = /^(?:\.[\\/])?gradlew(?:\.bat)?$/;
60
+ /** A word cmd.exe passes through untouched: no spaces, quotes, `%`, `!`, `^`, `&`, `|`, `<`, `>` or parentheses. */
61
+ const CMD_SAFE = /^[A-Za-z0-9_.:=/\\*+,@-]+$/;
62
+ /** Finds `name` on the PATH the way Windows does, trying each PATHEXT extension. */
63
+ function whichWindows(name, host) {
64
+ const exts = (host.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD").split(";").filter((ext) => ext !== "");
65
+ const hasExt = /\.[A-Za-z0-9]+$/.test(name);
66
+ const dirs = (host.env["PATH"] ?? host.env["Path"] ?? "").split(";").filter((dir) => dir.trim() !== "");
67
+ for (const dir of dirs) {
68
+ for (const ext of hasExt ? [""] : exts) {
69
+ const candidate = win32.join(dir.trim().replace(/^"|"$/g, ""), name + ext.toLowerCase());
70
+ if (host.exists(candidate))
71
+ return candidate;
72
+ }
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Gradle skips a test task whose inputs have not changed since it last passed, and then no trace is
78
+ * written: running the sim again on an unchanged auto would only ever report a stale trace. Gradle's
79
+ * built-in `--rerun` task option (Gradle 7.6 and later) runs the task regardless. It is added last,
80
+ * where it binds to the command's last task, unless the command already forces a rerun itself.
81
+ */
82
+ export function withRerun(args) {
83
+ return args.some((arg) => arg === "--rerun" || arg === "--rerun-tasks") ? [...args] : [...args, "--rerun"];
84
+ }
85
+ /**
86
+ * Turns the command's argv into something `spawn` can start with `shell: false` on this platform,
87
+ * and fails with a plain-language `LauncherError` when the tool it needs is missing or the command
88
+ * cannot be started safely.
89
+ */
90
+ export function resolveLauncher(argv, root, host = hostPlatform()) {
91
+ const [command, ...rest] = argv;
92
+ if (command === undefined)
93
+ throw new LauncherError('zenith.json "sim.command" is empty.');
94
+ const display = argv.join(" ");
95
+ if (GRADLEW.test(command)) {
96
+ const gradleArgs = withRerun(rest);
97
+ const path = host.platform === "win32" ? win32 : posix;
98
+ const jar = path.join(root, "gradle", "wrapper", "gradle-wrapper.jar");
99
+ if (!host.exists(jar)) {
100
+ throw new MissingToolError("gradle", "This robot repository has no Gradle wrapper (gradle/wrapper/gradle-wrapper.jar), so the sim cannot start. Check that the whole repository was cloned, or open it once in Android Studio.");
101
+ }
102
+ const java = findJava(host);
103
+ if (host.platform !== "win32") {
104
+ const script = path.join(root, "gradlew");
105
+ if (!host.exists(script)) {
106
+ throw new MissingToolError("gradle", "This robot repository has no gradlew script, so the sim cannot start.");
107
+ }
108
+ return { file: script, args: gradleArgs, display };
109
+ }
110
+ // The same launch gradlew.bat performs, minus cmd.exe: its default JVM options, the app name
111
+ // Gradle prints in its help, the wrapper jar on the classpath, then the command's own arguments.
112
+ const extra = [host.env["JAVA_OPTS"], host.env["GRADLE_OPTS"]]
113
+ .filter((value) => value !== undefined && value.trim() !== "")
114
+ .flatMap((value) => splitShellWords(value));
115
+ return {
116
+ file: java,
117
+ args: [
118
+ "-Xmx64m",
119
+ "-Xms64m",
120
+ ...extra,
121
+ "-Dorg.gradle.appname=gradlew",
122
+ "-classpath",
123
+ jar,
124
+ "org.gradle.wrapper.GradleWrapperMain",
125
+ ...gradleArgs,
126
+ ],
127
+ display,
128
+ };
129
+ }
130
+ if (host.platform !== "win32")
131
+ return { file: command, args: rest, display };
132
+ const resolved = /[\\/]/.test(command) ? win32.resolve(root, command) : whichWindows(command, host);
133
+ if (resolved === null || !host.exists(resolved)) {
134
+ throw new MissingToolError(command.toLowerCase().startsWith("gradle") ? "gradle" : "command", `The sim command starts with "${command}", which is not installed or not on the PATH.${command.toLowerCase().startsWith("gradle")
135
+ ? " Install Gradle, or change zenith.json's sim.command to use the repository's ./gradlew."
136
+ : ""}`);
137
+ }
138
+ if (/\.(?:bat|cmd)$/i.test(resolved)) {
139
+ // Node cannot start a batch file without cmd.exe. Allow it only when every word is one cmd.exe
140
+ // reads literally, so nothing in the command can be taken as an operator.
141
+ const bad = rest.find((word) => !CMD_SAFE.test(word));
142
+ if (bad !== undefined || /[\s"%!^&|<>()]/.test(resolved)) {
143
+ throw new LauncherError(`The sim command runs a batch file, and "${bad ?? resolved}" is not safe to pass through cmd.exe. Use ./gradlew in zenith.json's sim.command instead.`);
144
+ }
145
+ return {
146
+ file: host.env["ComSpec"] ?? "cmd.exe",
147
+ args: ["/d", "/s", "/c", [resolved, ...rest].join(" ")],
148
+ display,
149
+ };
150
+ }
151
+ return { file: resolved, args: rest, display };
152
+ }
153
+ //# sourceMappingURL=launcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher.js","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":"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"}
package/dist/lib.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The CLI's reusable pieces, for another host of the same logic (the desktop app's main process).
3
+ * This module has no side effects, unlike `index.ts`, which runs the
4
+ * program when it is the entry point; import it as `@horizon36596/zenith-cli/lib` from a bundle that cannot take
5
+ * the entry module's top-level await. `index.ts` re-exports all of it.
6
+ */
7
+ export { confinePath, loadProject, ProjectError, type Project } from "./project.js";
8
+ export { splitShellWords } from "./commands/sim.js";
9
+ export { findJava, hostPlatform, LauncherError, MissingToolError, resolveLauncher, withRerun, type Platform, type SimLaunch, } from "./launcher.js";
10
+ export { applyDeploy, planDeploy, type DeployAction, type DeployActionKind, type DeployPlan, } from "./commands/deploy.js";
11
+ export { AUTO_NAME_PATTERN, loadAutoAndPlan, type LoadAutoResult, type LoadedAuto } from "./autoLoad.js";
12
+ export { tryCore, type CoreResult } from "./coreGate.js";
13
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,KAAK,QAAQ,EACb,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC"}
package/dist/lib.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The CLI's reusable pieces, for another host of the same logic (the desktop app's main process).
3
+ * This module has no side effects, unlike `index.ts`, which runs the
4
+ * program when it is the entry point; import it as `@horizon36596/zenith-cli/lib` from a bundle that cannot take
5
+ * the entry module's top-level await. `index.ts` re-exports all of it.
6
+ */
7
+ export { confinePath, loadProject, ProjectError } from "./project.js";
8
+ export { splitShellWords } from "./commands/sim.js";
9
+ export { findJava, hostPlatform, LauncherError, MissingToolError, resolveLauncher, withRerun, } from "./launcher.js";
10
+ export { applyDeploy, planDeploy, } from "./commands/deploy.js";
11
+ export { AUTO_NAME_PATTERN, loadAutoAndPlan } from "./autoLoad.js";
12
+ export { tryCore } from "./coreGate.js";
13
+ //# sourceMappingURL=lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAgB,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,SAAS,GAGV,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,UAAU,GAIX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAwC,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,OAAO,EAAmB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { Field, Link, Robot, Waypoints } from "@horizon36596/zenith-schema";
2
+ export declare const LINK_FILE = "zenith.json";
3
+ export declare class ProjectError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ /**
7
+ * Resolves `target` (a path from `zenith.json`, absolute or relative to `root`) and asserts it
8
+ * lands inside `root`. Every directory or file the CLI writes to or deletes from must go through
9
+ * this, so a `zenith.json` value like `"../ESCAPED-DEPLOY"` (or an absolute path outside the repo)
10
+ * is refused before any filesystem call rather than followed.
11
+ */
12
+ export declare function confinePath(root: string, target: string): string;
13
+ export interface Project {
14
+ /** The directory holding `zenith.json`. Every path in the link file is relative to it. */
15
+ root: string;
16
+ link: Link;
17
+ robot: Robot;
18
+ field: Field;
19
+ waypoints: Waypoints | undefined;
20
+ }
21
+ /** Walks up from a directory looking for `zenith.json`, the way git looks for `.git`. */
22
+ export declare function findProjectRoot(from: string): string | null;
23
+ export declare function readJsonFile(path: string): unknown;
24
+ /**
25
+ * Loads `zenith.json` and the robot, field and waypoints files it points at.
26
+ *
27
+ * `autoHint` is a verb's raw `<auto>` argument (before any resolution against `autosDir`), tried
28
+ * as a last resort when neither `projectDir` nor walking up from `from` finds a `zenith.json`: the
29
+ * same "walk up from the file you were given" fallback every verb should have, not just
30
+ * `validate`.
31
+ */
32
+ export declare function loadProject(from: string, projectDir?: string, autoHint?: string): Project;
33
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAEjF,eAAO,MAAM,SAAS,gBAAgB,CAAC;AAEvC,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOhE;AAED,MAAM,WAAW,OAAO;IACtB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;CAClC;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ3D;AAkBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYlD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CA4BzF"}
@@ -0,0 +1,104 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, relative, resolve as resolvePath } from "node:path";
3
+ import { loadField, loadLink, loadRobot, loadWaypoints } from "@horizon36596/zenith-core";
4
+ export const LINK_FILE = "zenith.json";
5
+ export class ProjectError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "ProjectError";
9
+ }
10
+ }
11
+ /**
12
+ * Resolves `target` (a path from `zenith.json`, absolute or relative to `root`) and asserts it
13
+ * lands inside `root`. Every directory or file the CLI writes to or deletes from must go through
14
+ * this, so a `zenith.json` value like `"../ESCAPED-DEPLOY"` (or an absolute path outside the repo)
15
+ * is refused before any filesystem call rather than followed.
16
+ */
17
+ export function confinePath(root, target) {
18
+ const resolved = isAbsolute(target) ? resolvePath(target) : resolvePath(root, target);
19
+ const rel = relative(root, resolved);
20
+ if (rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute(rel)) {
21
+ throw new ProjectError(`"${target}" resolves outside the project root (${root}); refusing to use it.`);
22
+ }
23
+ return resolved;
24
+ }
25
+ /** Walks up from a directory looking for `zenith.json`, the way git looks for `.git`. */
26
+ export function findProjectRoot(from) {
27
+ let current = resolvePath(from);
28
+ for (;;) {
29
+ if (existsSync(join(current, LINK_FILE)))
30
+ return current;
31
+ const parent = dirname(current);
32
+ if (parent === current)
33
+ return null;
34
+ current = parent;
35
+ }
36
+ }
37
+ /**
38
+ * `loadProject`'s root-finding: an explicit `--project` wins outright; otherwise walk up from
39
+ * `from` (normally the working directory); and when that finds nothing, walk up from `autoHint`
40
+ * (a verb's raw auto argument) instead, so `zenith <verb> examples/starter/autos/x.auto.json` (run
41
+ * from the repository root, no `zenith.json` there) finds `examples/starter`'s project the same
42
+ * way `zenith validate` always has.
43
+ */
44
+ function findProjectRootFor(from, projectDir, autoHint) {
45
+ if (projectDir !== undefined)
46
+ return resolvePath(projectDir);
47
+ const direct = findProjectRoot(from);
48
+ if (direct !== null)
49
+ return direct;
50
+ if (autoHint === undefined)
51
+ return null;
52
+ const hintPath = isAbsolute(autoHint) ? autoHint : resolvePath(from, autoHint);
53
+ return findProjectRoot(dirname(hintPath));
54
+ }
55
+ export function readJsonFile(path) {
56
+ let text;
57
+ try {
58
+ text = readFileSync(path, "utf8");
59
+ }
60
+ catch {
61
+ throw new ProjectError(`Cannot read ${path}.`);
62
+ }
63
+ try {
64
+ return JSON.parse(text);
65
+ }
66
+ catch (error) {
67
+ throw new ProjectError(`${path} is not valid JSON: ${error.message}`);
68
+ }
69
+ }
70
+ /**
71
+ * Loads `zenith.json` and the robot, field and waypoints files it points at.
72
+ *
73
+ * `autoHint` is a verb's raw `<auto>` argument (before any resolution against `autosDir`), tried
74
+ * as a last resort when neither `projectDir` nor walking up from `from` finds a `zenith.json`: the
75
+ * same "walk up from the file you were given" fallback every verb should have, not just
76
+ * `validate`.
77
+ */
78
+ export function loadProject(from, projectDir, autoHint) {
79
+ const root = findProjectRootFor(from, projectDir, autoHint);
80
+ if (root === null) {
81
+ throw new ProjectError(`No ${LINK_FILE} here or in any directory above. Run "zenith init" or pass --project <dir>.`);
82
+ }
83
+ const linkPath = join(root, LINK_FILE);
84
+ if (!existsSync(linkPath))
85
+ throw new ProjectError(`No ${LINK_FILE} in ${root}.`);
86
+ const link = loadLink(readJsonFile(linkPath));
87
+ const inRoot = (relativePath) => isAbsolute(relativePath) ? relativePath : join(root, relativePath);
88
+ // Every directory zenith.json names for writing (or deleting from) must resolve inside the
89
+ // project root; a "../ESCAPED-DEPLOY" or an absolute path is refused here, once, rather than
90
+ // trusted by every command that later joins a file name onto it (finding 13).
91
+ confinePath(root, link.autosDir);
92
+ if (link.deploy !== undefined)
93
+ confinePath(root, link.deploy.dir);
94
+ if (link.codegen !== undefined)
95
+ confinePath(root, link.codegen.dir);
96
+ return {
97
+ root,
98
+ link,
99
+ robot: loadRobot(readJsonFile(inRoot(link.robot))),
100
+ field: loadField(readJsonFile(inRoot(link.field))),
101
+ waypoints: link.waypoints === undefined ? undefined : loadWaypoints(readJsonFile(inRoot(link.waypoints))),
102
+ };
103
+ }
104
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type MirrorMode } from "@horizon36596/zenith-core";
2
+ import type { CommandLibrary } from "./commandLibrary.js";
3
+ /**
4
+ * `zenith.json` for `zenith init`. The command library is written only when the person named one;
5
+ * Zenith never picks it (site/docs/command-libraries.md).
6
+ */
7
+ export declare function linkSkeleton(commandLibrary?: CommandLibrary): string;
8
+ export declare function robotSkeleton(name: string): string;
9
+ export declare function waypointsSkeleton(): string;
10
+ /**
11
+ * The skeleton auto for `zenith new`. It is laid out for RED; a BLUE one is the same routine
12
+ * mirrored by the field's own symmetry, `mirror`, so it starts on BLUE's half where the robot run as
13
+ * BLUE starts it.
14
+ */
15
+ export declare function autoSkeleton(name: string, alliance?: "RED" | "BLUE", mirror?: MirrorMode): string;
16
+ //# sourceMappingURL=skeletons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skeletons.d.ts","sourceRoot":"","sources":["../src/skeletons.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAgC1D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,MAAM,CAiBpE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6BlD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,KAAK,GAAG,MAAc,EAAE,MAAM,GAAE,UAA4B,GAAG,MAAM,CAkCzH"}
@@ -0,0 +1,120 @@
1
+ import { canonicalize, mirrorAuto } from "@horizon36596/zenith-core";
2
+ import { FORMAT_VERSIONS, parseAuto, parseLink, parseRobot, parseWaypoints, SCHEMA_ID, } from "@horizon36596/zenith-schema";
3
+ /**
4
+ * The files `zenith init` and `zenith new` write. Each one is parsed before it is canonicalised, so
5
+ * a skeleton that would not validate cannot be written in the first place.
6
+ *
7
+ * Every number here carries a provenance label, because a skeleton is exactly where an invented
8
+ * number would otherwise slip in (CLAUDE.md rule 4).
9
+ */
10
+ /**
11
+ * Where the skeleton auto starts, in RED's frame. A BLUE skeleton is this one mirrored, because a
12
+ * file's poses are in its own alliance's frame (site/docs/file-format.md). NEEDS MEASUREMENT: it is chosen to pass the BIOBUZZ field's `startRules` for
13
+ * `robotSkeleton`'s 18 x 14 in footprint, not measured off a real start. Facing +y, the robot's
14
+ * 18 in length runs along y, so y = -63 puts its back edge on the south wall (y = -72). x = -12
15
+ * keeps its 14 in width (x from -19 to -5) in RED's own half, clear of loadingZoneRed (on the
16
+ * x = -72 wall at y from +24 to +48) and of every flower (the nearest, flower3, stands on the same
17
+ * wall at x = +24, 29 in east of the robot's edge).
18
+ */
19
+ const SKELETON_START = { xIn: -12, yIn: -63, headingRad: 1.5708 };
20
+ /** How far the skeleton's one leg drives straight out from the wall. PLACEHOLDER. */
21
+ const SKELETON_LEG_IN = 24;
22
+ /**
23
+ * `zenith.json` for `zenith init`. The command library is written only when the person named one;
24
+ * Zenith never picks it (site/docs/command-libraries.md).
25
+ */
26
+ export function linkSkeleton(commandLibrary) {
27
+ return canonicalize("link", parseLink({
28
+ $schema: SCHEMA_ID.link,
29
+ formatVersion: FORMAT_VERSIONS.link,
30
+ autosDir: "autos",
31
+ robot: "autos/robot.json",
32
+ field: "autos/field/biobuzz.field.json",
33
+ waypoints: "autos/waypoints.json",
34
+ deploy: {
35
+ kind: "androidAssets",
36
+ dir: "TeamCode/src/main/assets/autos",
37
+ ...(commandLibrary === undefined ? {} : { commandLibrary }),
38
+ },
39
+ }));
40
+ }
41
+ export function robotSkeleton(name) {
42
+ const placeholder = "NEEDS MEASUREMENT: written by zenith init";
43
+ return canonicalize("robot", parseRobot({
44
+ $schema: SCHEMA_ID.robot,
45
+ formatVersion: FORMAT_VERSIONS.robot,
46
+ name,
47
+ frame: { forward: "+x", left: "+y", headingZero: "+x", headingPositive: "ccw" },
48
+ footprint: {
49
+ startIn: { lengthIn: 18, widthIn: 14, provenance: placeholder },
50
+ expandedIn: { lengthIn: 18, widthIn: 14, provenance: placeholder },
51
+ centreOfRotationIn: { xIn: 0, yIn: 0, provenance: placeholder },
52
+ },
53
+ heightIn: { value: 18, provenance: placeholder },
54
+ kinematics: {
55
+ maxForwardVelInPerS: { value: 60, provenance: placeholder },
56
+ maxStrafeVelInPerS: { value: 50, provenance: placeholder },
57
+ forwardDecelInPerS2: { value: 60, provenance: placeholder },
58
+ strafeDecelInPerS2: { value: 30, provenance: placeholder },
59
+ accelInPerS2: { value: 60, provenance: placeholder },
60
+ maxAngularVelRadPerS: { value: 6, provenance: placeholder },
61
+ defaultPathSpeedFraction: { value: 0.8, provenance: placeholder },
62
+ follower: { library: "pedro", version: "3.0.0-20260828.185437-17", holdEnd: true },
63
+ },
64
+ commands: [],
65
+ conditions: [],
66
+ }));
67
+ }
68
+ export function waypointsSkeleton() {
69
+ return canonicalize("waypoints", parseWaypoints({
70
+ $schema: SCHEMA_ID.waypoints,
71
+ formatVersion: FORMAT_VERSIONS.waypoints,
72
+ waypoints: {
73
+ start: {
74
+ ...SKELETON_START,
75
+ provenance: "NEEDS MEASUREMENT: written by zenith init, a legal BIOBUZZ start for the skeleton robot",
76
+ },
77
+ },
78
+ }));
79
+ }
80
+ /**
81
+ * The skeleton auto for `zenith new`. It is laid out for RED; a BLUE one is the same routine
82
+ * mirrored by the field's own symmetry, `mirror`, so it starts on BLUE's half where the robot run as
83
+ * BLUE starts it.
84
+ */
85
+ export function autoSkeleton(name, alliance = "RED", mirror = "pointSymmetry") {
86
+ const red = parseAuto({
87
+ $schema: SCHEMA_ID.auto,
88
+ formatVersion: FORMAT_VERSIONS.auto,
89
+ name,
90
+ title: name,
91
+ alliance: "RED",
92
+ start: {
93
+ pose: {
94
+ ...SKELETON_START,
95
+ provenance: "NEEDS MEASUREMENT: written by zenith new, a legal BIOBUZZ start for the skeleton robot",
96
+ },
97
+ },
98
+ steps: [
99
+ {
100
+ id: "leg1",
101
+ kind: "path",
102
+ segments: [
103
+ {
104
+ kind: "line",
105
+ from: "current",
106
+ to: {
107
+ xIn: SKELETON_START.xIn,
108
+ yIn: SKELETON_START.yIn + SKELETON_LEG_IN,
109
+ provenance: "PLACEHOLDER: written by zenith new, 24 in straight out from the start",
110
+ },
111
+ },
112
+ ],
113
+ heading: { mode: "tangent" },
114
+ notes: "Replace this leg with the real opening move.",
115
+ },
116
+ ],
117
+ });
118
+ return canonicalize("auto", alliance === "RED" ? red : mirrorAuto(red, mirror));
119
+ }
120
+ //# sourceMappingURL=skeletons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skeletons.js","sourceRoot":"","sources":["../src/skeletons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { type Estimate, type Plan } from "@horizon36596/zenith-core";
2
+ /**
3
+ * The trace file `AutoTraceWriter` writes (site/docs/simulation.md). There
4
+ * is no zod schema for it in `@horizon36596/zenith-schema` - it is not one of the five file kinds the schema
5
+ * package owns - so this module does its own light validation, matching the writer's actual output
6
+ * (`robot/auto-runtime/AutoTraceWriter.java`) rather than the full spec shape, which the writer does
7
+ * not populate yet (`structureContacts`, `ledger` and `events` are empty until later sim batches).
8
+ */
9
+ export interface TraceStep {
10
+ id: string;
11
+ startS: number;
12
+ endS: number;
13
+ interrupted: boolean;
14
+ }
15
+ /** `[timeS, xIn, yIn, headingRad]`. */
16
+ export type TracePoseRow = readonly [number, number, number, number];
17
+ export interface TraceLedgerRow {
18
+ timeS: number;
19
+ held?: number;
20
+ launches?: number;
21
+ tips?: Record<string, number>;
22
+ }
23
+ export interface TraceEvent {
24
+ timeS: number;
25
+ kind: string;
26
+ alliance?: string;
27
+ }
28
+ export interface TraceFile {
29
+ formatVersion: number;
30
+ auto: string;
31
+ simTimeS: number;
32
+ tickS: number;
33
+ capabilities: string[];
34
+ steps: TraceStep[];
35
+ poses: TracePoseRow[];
36
+ truthPoses: TracePoseRow[];
37
+ structureContacts: {
38
+ timeS: number;
39
+ obstacle: string;
40
+ penetrationIn: number;
41
+ }[];
42
+ ledger: TraceLedgerRow[];
43
+ events: TraceEvent[];
44
+ }
45
+ export declare class TraceParseError extends Error {
46
+ }
47
+ /** Parses and shape-checks a trace file, without assuming fields later sim batches have not filled. */
48
+ export declare function parseTrace(json: unknown): TraceFile;
49
+ export interface StepReport {
50
+ id: string;
51
+ estimateNominalS: number | null;
52
+ actualS: number;
53
+ deltaS: number | null;
54
+ interrupted: boolean;
55
+ /** Largest distance from a recorded pose to the nearest planned sample, inches. `null` for a
56
+ * non-path step or a trace with no poses in the step's time window. */
57
+ maxCrossTrackIn: number | null;
58
+ }
59
+ export interface SimReport {
60
+ auto: string;
61
+ simTimeS: number;
62
+ steps: StepReport[];
63
+ totalEstimateS: number | null;
64
+ totalActualS: number;
65
+ structureContacts: number;
66
+ launches: number;
67
+ tips: Record<string, number>;
68
+ heldAtEnd: number | null;
69
+ capabilities: string[];
70
+ /** Report sections the sim build does not measure yet, read from the trace's own capability list. */
71
+ inert: string[];
72
+ }
73
+ /** Builds the estimate-vs-actual report `zenith sim` prints, from a trace and the auto's plan. */
74
+ export declare function buildSimReport(trace: TraceFile, plan: Plan, estimateResult: Estimate | null): SimReport;
75
+ //# sourceMappingURL=traceReport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceReport.d.ts","sourceRoot":"","sources":["../src/traceReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAE7F;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,uCAAuC;AACvC,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAErE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,iBAAiB,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChF,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAMD,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAE7C,uGAAuG;AACvG,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAuBnD;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB;2EACuE;IACvE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,qGAAqG;IACrG,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAID,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,CAuDvG"}