@jevitate/cli 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/anchor-baselines.d.ts +26 -0
  2. package/dist/anchor-baselines.js +25 -0
  3. package/dist/anchor-baselines.js.map +1 -0
  4. package/dist/approval-provenance.d.ts +32 -2
  5. package/dist/approval-provenance.js +92 -5
  6. package/dist/approval-provenance.js.map +1 -1
  7. package/dist/approved-demo.d.ts +86 -0
  8. package/dist/approved-demo.js +184 -0
  9. package/dist/approved-demo.js.map +1 -0
  10. package/dist/bin.js +13352 -7050
  11. package/dist/build-info.generated.js +2 -2
  12. package/dist/campaign-api.d.ts +15 -0
  13. package/dist/campaign-api.js +67 -6
  14. package/dist/campaign-api.js.map +1 -1
  15. package/dist/campaign-cli.js +7 -2
  16. package/dist/campaign-cli.js.map +1 -1
  17. package/dist/campaign-preflight.d.ts +76 -0
  18. package/dist/campaign-preflight.js +117 -0
  19. package/dist/campaign-preflight.js.map +1 -0
  20. package/dist/catalog-analysis.js +14 -0
  21. package/dist/catalog-analysis.js.map +1 -1
  22. package/dist/catalog-api.js +12 -4
  23. package/dist/catalog-api.js.map +1 -1
  24. package/dist/catalog-bundle-api.d.ts +83 -0
  25. package/dist/catalog-bundle-api.js +309 -0
  26. package/dist/catalog-bundle-api.js.map +1 -0
  27. package/dist/catalog-bundle-findings.d.ts +64 -0
  28. package/dist/catalog-bundle-findings.js +326 -0
  29. package/dist/catalog-bundle-findings.js.map +1 -0
  30. package/dist/catalog-bundle.d.ts +296 -0
  31. package/dist/catalog-bundle.js +777 -0
  32. package/dist/catalog-bundle.js.map +1 -0
  33. package/dist/catalog-cli.d.ts +6 -0
  34. package/dist/catalog-cli.js +181 -5
  35. package/dist/catalog-cli.js.map +1 -1
  36. package/dist/catalog-refs.d.ts +42 -0
  37. package/dist/catalog-refs.js +188 -0
  38. package/dist/catalog-refs.js.map +1 -0
  39. package/dist/catalog-review.d.ts +3 -1
  40. package/dist/catalog-review.js +61 -0
  41. package/dist/catalog-review.js.map +1 -1
  42. package/dist/catalog.d.ts +27 -3
  43. package/dist/catalog.js +46 -2
  44. package/dist/catalog.js.map +1 -1
  45. package/dist/check-api.js +180 -9
  46. package/dist/check-api.js.map +1 -1
  47. package/dist/check-cli.d.ts +2 -0
  48. package/dist/check-cli.js +96 -2
  49. package/dist/check-cli.js.map +1 -1
  50. package/dist/check-execute.d.ts +18 -0
  51. package/dist/check-execute.js +56 -3
  52. package/dist/check-execute.js.map +1 -1
  53. package/dist/check-types.d.ts +105 -0
  54. package/dist/check-types.js.map +1 -1
  55. package/dist/cli-output.d.ts +6 -0
  56. package/dist/cli-output.js +38 -1
  57. package/dist/cli-output.js.map +1 -1
  58. package/dist/cli-shared.d.ts +10 -0
  59. package/dist/cli-shared.js +0 -4
  60. package/dist/cli-shared.js.map +1 -1
  61. package/dist/cold-start-report.d.ts +32 -0
  62. package/dist/cold-start-report.js +31 -0
  63. package/dist/cold-start-report.js.map +1 -0
  64. package/dist/defect-evidence.js +10 -5
  65. package/dist/defect-evidence.js.map +1 -1
  66. package/dist/demo-aspect-api.d.ts +11 -0
  67. package/dist/demo-aspect-api.js +27 -2
  68. package/dist/demo-aspect-api.js.map +1 -1
  69. package/dist/demo-aspect-cli.js +5 -0
  70. package/dist/demo-aspect-cli.js.map +1 -1
  71. package/dist/demo-capture.d.ts +79 -2
  72. package/dist/demo-capture.js +504 -12
  73. package/dist/demo-capture.js.map +1 -1
  74. package/dist/environments.d.ts +8 -1
  75. package/dist/environments.js +14 -3
  76. package/dist/environments.js.map +1 -1
  77. package/dist/exit-codes.js +16 -0
  78. package/dist/exit-codes.js.map +1 -1
  79. package/dist/explore-adversarial.js +2 -2
  80. package/dist/explore-adversarial.js.map +1 -1
  81. package/dist/explore-shared.d.ts +11 -0
  82. package/dist/explore-shared.js +9 -0
  83. package/dist/explore-shared.js.map +1 -1
  84. package/dist/findings-filing.js +7 -3
  85. package/dist/findings-filing.js.map +1 -1
  86. package/dist/forge-verify.d.ts +224 -0
  87. package/dist/forge-verify.js +649 -0
  88. package/dist/forge-verify.js.map +1 -0
  89. package/dist/github-issue-filer.js +15 -9
  90. package/dist/github-issue-filer.js.map +1 -1
  91. package/dist/init-codeowners.d.ts +19 -0
  92. package/dist/init-codeowners.js +66 -0
  93. package/dist/init-codeowners.js.map +1 -1
  94. package/dist/job-draft-api.d.ts +52 -0
  95. package/dist/job-draft-api.js +110 -0
  96. package/dist/job-draft-api.js.map +1 -0
  97. package/dist/journeeze-api.d.ts +77 -0
  98. package/dist/journeeze-api.js +371 -0
  99. package/dist/journeeze-api.js.map +1 -0
  100. package/dist/journeeze-connect.d.ts +156 -0
  101. package/dist/journeeze-connect.js +357 -0
  102. package/dist/journeeze-connect.js.map +1 -0
  103. package/dist/journey-anchor-api.d.ts +37 -0
  104. package/dist/journey-anchor-api.js +58 -0
  105. package/dist/journey-anchor-api.js.map +1 -0
  106. package/dist/journey-anchor-cli.d.ts +17 -0
  107. package/dist/journey-anchor-cli.js +104 -0
  108. package/dist/journey-anchor-cli.js.map +1 -0
  109. package/dist/journey-anchor-gate.d.ts +52 -0
  110. package/dist/journey-anchor-gate.js +101 -0
  111. package/dist/journey-anchor-gate.js.map +1 -0
  112. package/dist/journey-api.d.ts +30 -1
  113. package/dist/journey-api.js +118 -16
  114. package/dist/journey-api.js.map +1 -1
  115. package/dist/journey-cli.js +93 -10
  116. package/dist/journey-cli.js.map +1 -1
  117. package/dist/journey-demo-api.d.ts +29 -0
  118. package/dist/journey-demo-api.js +29 -7
  119. package/dist/journey-demo-api.js.map +1 -1
  120. package/dist/journey-heal.js +1 -1
  121. package/dist/journey-heal.js.map +1 -1
  122. package/dist/journey-lint-sarif.d.ts +3 -2
  123. package/dist/journey-lint-sarif.js +11 -2
  124. package/dist/journey-lint-sarif.js.map +1 -1
  125. package/dist/journey-migrate-api.d.ts +54 -0
  126. package/dist/journey-migrate-api.js +64 -0
  127. package/dist/journey-migrate-api.js.map +1 -0
  128. package/dist/journey-network-checks.d.ts +11 -0
  129. package/dist/journey-network-checks.js +36 -13
  130. package/dist/journey-network-checks.js.map +1 -1
  131. package/dist/journey-proposal-store.d.ts +9 -0
  132. package/dist/journey-proposal-store.js +23 -0
  133. package/dist/journey-proposal-store.js.map +1 -1
  134. package/dist/journey-result-record.d.ts +2 -0
  135. package/dist/journey-result-record.js +5 -0
  136. package/dist/journey-result-record.js.map +1 -1
  137. package/dist/journey-review-api.js +9 -1
  138. package/dist/journey-review-api.js.map +1 -1
  139. package/dist/journey-review.d.ts +11 -1
  140. package/dist/journey-review.js +77 -5
  141. package/dist/journey-review.js.map +1 -1
  142. package/dist/journey-stale-api.d.ts +40 -0
  143. package/dist/journey-stale-api.js +57 -0
  144. package/dist/journey-stale-api.js.map +1 -0
  145. package/dist/load-api.js +4 -2
  146. package/dist/load-api.js.map +1 -1
  147. package/dist/locator-health-api.d.ts +101 -0
  148. package/dist/locator-health-api.js +203 -0
  149. package/dist/locator-health-api.js.map +1 -0
  150. package/dist/locator-health.d.ts +195 -0
  151. package/dist/locator-health.js +419 -0
  152. package/dist/locator-health.js.map +1 -0
  153. package/dist/mcp-api.d.ts +3 -1
  154. package/dist/mcp-api.js +21 -2
  155. package/dist/mcp-api.js.map +1 -1
  156. package/dist/mcp-cli-tools.d.ts +4 -1
  157. package/dist/mcp-cli-tools.js +82 -10
  158. package/dist/mcp-cli-tools.js.map +1 -1
  159. package/dist/openrouter-call.d.ts +23 -10
  160. package/dist/openrouter-call.js +14 -11
  161. package/dist/openrouter-call.js.map +1 -1
  162. package/dist/png-pixels.d.ts +12 -0
  163. package/dist/png-pixels.js +96 -0
  164. package/dist/png-pixels.js.map +1 -0
  165. package/dist/project-config.d.ts +29 -0
  166. package/dist/project-config.js +76 -0
  167. package/dist/project-config.js.map +1 -0
  168. package/dist/regression-api.js +2 -1
  169. package/dist/regression-api.js.map +1 -1
  170. package/dist/server-log-verify.js +2 -1
  171. package/dist/server-log-verify.js.map +1 -1
  172. package/dist/signal-triage.js +7 -3
  173. package/dist/signal-triage.js.map +1 -1
  174. package/dist/source-run-api.js +2 -2
  175. package/dist/source-run-api.js.map +1 -1
  176. package/dist/verify-fix-api.d.ts +29 -0
  177. package/dist/verify-fix-api.js +43 -2
  178. package/dist/verify-fix-api.js.map +1 -1
  179. package/dist/verify-fix-cli.js +4 -0
  180. package/dist/verify-fix-cli.js.map +1 -1
  181. package/dist/zip-store.d.ts +15 -0
  182. package/dist/zip-store.js +93 -0
  183. package/dist/zip-store.js.map +1 -0
  184. package/package.json +12 -10
  185. package/skills/jevitate-ci-check/SKILL.md +16 -0
  186. package/skills/jevitate-demo/SKILL.md +4 -1
  187. package/skills/jevitate-getting-started/SKILL.md +7 -2
  188. package/skills/jevitate-run-journey/SKILL.md +15 -0
  189. package/skills/jevitate-test-campaign/SKILL.md +26 -0
  190. package/skills/jevitate-verify-fix/SKILL.md +5 -0
@@ -0,0 +1,357 @@
1
+ import { execFile } from "node:child_process";
2
+ import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { dirname, resolve as resolvePath } from "node:path";
4
+ import { redactText } from "@jevitate/ai-core";
5
+ import { clock } from "@jevitate/domain";
6
+ import { inMcpInvocation } from "./approval-provenance.js";
7
+ import { resolveDataDir } from "./data-dir.js";
8
+ import { readMaskedLine } from "./masked-input.js";
9
+ import { secretCommandRunner } from "./secret-command.js";
10
+ /**
11
+ * #464 — the Journeeze connection: which hosts an upload key may go to, how the key is found, and
12
+ * what `jevitate connect journeeze` saves (journeeze-saas `docs/contract/catalog-bundle-upload-v1.md`).
13
+ *
14
+ * The key itself is NEVER saved by jevitate. `connect` saves a REFERENCE to where the person keeps it
15
+ * (an environment variable, a command that prints it, or a password-manager entry), bound to the
16
+ * Journeeze origin it was verified against, in `~/.jevitate/journeeze.json` (outside the repo, 0600).
17
+ * The reference is shaped like `@jevitate/secrets`' `SecretRef` (`{manager, key, origin, field}`):
18
+ * jevitate delegates to the external source on every use and never implements a vault.
19
+ */
20
+ /** The upload key's format (contract §3): `jzu_` + 40 lowercase base32 characters. */
21
+ export const JOURNEEZE_KEY_RE = /^jzu_[a-z2-7]{40}$/;
22
+ /** Anything that looks like (part of) a key — refused wherever only a reference belongs. */
23
+ const KEY_LIKE_RE = /jzu_[a-z2-7]{8,}/i;
24
+ /** The CI env var (contract §9); read through ai-core's `CredentialStore` (never its plaintext file). */
25
+ export const JOURNEEZE_KEY_ENV = "JOURNEEZE_UPLOAD_KEY";
26
+ /** Optional base URL for CI (`publish` only); pinned like `--url`. */
27
+ export const JOURNEEZE_URL_ENV = "JOURNEEZE_URL";
28
+ export const JOURNEEZE_API_PREFIX = "/api/upload/v1";
29
+ /** Contract §2: the only hosts an upload key is ever sent to (plus loopback, only with `JEVITATE_JOURNEEZE_DEV=1`). */
30
+ export const JOURNEEZE_PINNED_ORIGINS = ["https://app.journeeze.dev", "https://app.staging.journeeze.dev"];
31
+ /** `JEVITATE_JOURNEEZE_DEV=1` allows a loopback Journeeze (local development of Journeeze itself). */
32
+ export const JOURNEEZE_DEV_ENV = "JEVITATE_JOURNEEZE_DEV";
33
+ const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
34
+ /** A Journeeze-specific refusal. `code` is stable; the message never carries a key. */
35
+ export class JourneezeError extends Error {
36
+ code;
37
+ constructor(code, message) {
38
+ super(message);
39
+ this.code = code;
40
+ this.name = "JourneezeError";
41
+ }
42
+ }
43
+ /**
44
+ * The Journeeze origin for a base URL, or a `JourneezeError`: HTTPS on a pinned host, or — only when
45
+ * `JEVITATE_JOURNEEZE_DEV=1` is set — http(s) on a loopback host (local development, contract §2;
46
+ * otherwise an upload key would go over plain HTTP to whatever listens on that port). No path,
47
+ * credentials, query or fragment.
48
+ */
49
+ export function pinnedJourneezeOrigin(baseUrl, env = process.env) {
50
+ let u;
51
+ try {
52
+ u = new URL(baseUrl);
53
+ }
54
+ catch {
55
+ throw new JourneezeError("E_JOURNEEZE_URL", `not a URL: ${JSON.stringify(baseUrl)}`);
56
+ }
57
+ const bare = u.username === "" && u.password === "" && u.search === "" && u.hash === "" && (u.pathname === "/" || u.pathname === "");
58
+ if (!bare)
59
+ throw new JourneezeError("E_JOURNEEZE_URL", `the Journeeze URL must be a bare origin (got ${JSON.stringify(baseUrl)})`);
60
+ if (LOOPBACK_HOSTS.has(u.hostname) && (u.protocol === "http:" || u.protocol === "https:")) {
61
+ if (env[JOURNEEZE_DEV_ENV]?.trim() === "1")
62
+ return u.origin;
63
+ throw new JourneezeError("E_JOURNEEZE_URL", `refusing ${u.origin}: a loopback Journeeze is for local development only — set ${JOURNEEZE_DEV_ENV}=1 to send an upload key there`);
64
+ }
65
+ if (u.protocol === "https:" && JOURNEEZE_PINNED_ORIGINS.includes(u.origin))
66
+ return u.origin;
67
+ throw new JourneezeError("E_JOURNEEZE_URL", `an upload key is only ever sent to ${JOURNEEZE_PINNED_ORIGINS.join(" or ")} (or a loopback host with ${JOURNEEZE_DEV_ENV}=1) — refusing ${u.origin}`);
68
+ }
69
+ export const fetchJourneezeHttp = async (req) => {
70
+ const headers = { ...req.headers };
71
+ // undici sets Content-Length itself for a byte body (a forbidden request header to set by hand).
72
+ delete headers["Content-Length"];
73
+ return fetch(req.url, {
74
+ method: req.method,
75
+ headers,
76
+ redirect: "manual",
77
+ ...(req.body === undefined ? {} : { body: Buffer.from(req.body) }),
78
+ });
79
+ };
80
+ /** Sends `req`, refusing any response that is a redirect (the key never follows one to another origin). */
81
+ export async function sendPinned(http, origin, req) {
82
+ if (new URL(req.url).origin !== origin) {
83
+ throw new JourneezeError("E_JOURNEEZE_ORIGIN", `refusing to send the upload key to ${new URL(req.url).origin}: it is bound to ${origin}`);
84
+ }
85
+ const res = await http(req);
86
+ if (res.status === 0 || (res.status >= 300 && res.status < 400)) {
87
+ const to = res.headers.get("location");
88
+ throw new JourneezeError("E_JOURNEEZE_REDIRECT", `Journeeze answered ${req.method} ${new URL(req.url).pathname} with a redirect${to ? ` to ${safeLocation(to, req.url)}` : ""} — redirects are never followed with an upload key`);
89
+ }
90
+ return res;
91
+ }
92
+ function safeLocation(location, base) {
93
+ try {
94
+ return new URL(location, base).origin;
95
+ }
96
+ catch {
97
+ return "an invalid location";
98
+ }
99
+ }
100
+ /** The JSON body of a response, or `{}` when it is not JSON (unknown fields are ignored, contract §8). */
101
+ export async function jsonBody(res) {
102
+ try {
103
+ const parsed = JSON.parse(await res.text());
104
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
105
+ }
106
+ catch {
107
+ return {};
108
+ }
109
+ }
110
+ /** `code (requestId)` of an error response (contract §5), for messages. Never echoes the request. */
111
+ export function describeError(res, body) {
112
+ const code = typeof body.code === "string" ? body.code : "unknown";
113
+ const message = typeof body.message === "string" ? `: ${body.message.slice(0, 300)}` : "";
114
+ const requestId = res.headers.get("journeeze-request-id") ?? (typeof body.requestId === "string" ? body.requestId : undefined);
115
+ return `${res.status} ${code}${message}${requestId ? ` (request ${requestId})` : ""}`;
116
+ }
117
+ /**
118
+ * Parses what the person typed at the reference prompt: `env:<VAR>`, `cmd:<command>`, `op://…`,
119
+ * `bw:<item>` or `pass:<path>`. Anything that looks like a key itself is refused (without echoing it).
120
+ */
121
+ export function parseKeyRef(text, origin) {
122
+ const t = text.trim();
123
+ if (KEY_LIKE_RE.test(t)) {
124
+ throw new JourneezeError("E_JOURNEEZE_REF", "that looks like the upload key itself — jevitate saves only WHERE the key is kept, never the key");
125
+ }
126
+ const ref = (manager, key) => {
127
+ if (key.trim() === "")
128
+ throw new JourneezeError("E_JOURNEEZE_REF", `the ${manager} reference is empty`);
129
+ return { manager, key: key.trim(), origin, field: "journeeze-upload-key" };
130
+ };
131
+ if (t.startsWith("env:")) {
132
+ const name = t.slice(4).trim();
133
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
134
+ throw new JourneezeError("E_JOURNEEZE_REF", `not an environment variable name: ${JSON.stringify(name)}`);
135
+ return ref("env", name);
136
+ }
137
+ if (t.startsWith("cmd:"))
138
+ return ref("cmd", t.slice(4));
139
+ if (t.startsWith("op://"))
140
+ return ref("op", t);
141
+ if (t.startsWith("bw:"))
142
+ return ref("bw", t.slice(3));
143
+ if (t.startsWith("pass:"))
144
+ return ref("pass", t.slice(5));
145
+ throw new JourneezeError("E_JOURNEEZE_REF", "expected env:<VAR>, cmd:<command>, op://…, bw:<item> or pass:<path>");
146
+ }
147
+ export function defaultKeySources(env = process.env) {
148
+ return {
149
+ env,
150
+ runCommand: secretCommandRunner(),
151
+ exec: (cmd, args) => new Promise((resolveOut, reject) => {
152
+ execFile(cmd, [...args], { timeout: 60_000, maxBuffer: 64 * 1024 }, (err, stdout) => (err ? reject(err) : resolveOut(String(stdout))));
153
+ }),
154
+ };
155
+ }
156
+ const MANAGER_COMMANDS = {
157
+ op: (key) => ["op", ["read", key]],
158
+ bw: (key) => ["bw", ["get", "password", key]],
159
+ pass: (key) => ["pass", ["show", key]],
160
+ };
161
+ /**
162
+ * The key a reference points to, or a `JourneezeError` that names the reference — never the source's
163
+ * output (a misconfigured source may print the secret on failure). The value is checked for the key
164
+ * format; the caller uses it only for the `Authorization` header.
165
+ */
166
+ export async function resolveKeyRef(ref, sources) {
167
+ let raw;
168
+ try {
169
+ if (ref.manager === "env")
170
+ raw = sources.env[ref.key];
171
+ else if (ref.manager === "cmd")
172
+ raw = await sources.runCommand(ref.key);
173
+ else {
174
+ const [cmd, args] = MANAGER_COMMANDS[ref.manager](ref.key);
175
+ raw = await sources.exec(cmd, args);
176
+ }
177
+ }
178
+ catch (err) {
179
+ const code = err?.code;
180
+ throw new JourneezeError("E_JOURNEEZE_KEY_UNRESOLVABLE", `could not read the upload key from ${describeRef(ref)}${typeof code === "number" || typeof code === "string" ? ` (exit code ${String(code)})` : ""}`);
181
+ }
182
+ // `pass show` prints the secret on its first line; the others print it alone.
183
+ const value = (raw ?? "").split(/\r?\n/).map((l) => l.trim()).find((l) => l !== "") ?? "";
184
+ if (value === "")
185
+ throw new JourneezeError("E_JOURNEEZE_KEY_UNRESOLVABLE", `${describeRef(ref)} is empty or unset`);
186
+ if (!JOURNEEZE_KEY_RE.test(value)) {
187
+ throw new JourneezeError("E_JOURNEEZE_KEY_FORMAT", `the value from ${describeRef(ref)} is not a Journeeze upload key (jzu_ followed by 40 characters)`);
188
+ }
189
+ return value;
190
+ }
191
+ /** A display form of a reference (never a value). */
192
+ export function describeRef(ref) {
193
+ switch (ref.manager) {
194
+ case "env":
195
+ return `environment variable ${ref.key}`;
196
+ case "cmd":
197
+ return "the saved key command";
198
+ default:
199
+ return `${ref.manager} entry ${ref.key}`;
200
+ }
201
+ }
202
+ export function connectionsPath(deps = {}) {
203
+ return resolveDataDir(["journeeze.json"], deps.homedir ? { homedir: deps.homedir } : {});
204
+ }
205
+ /** The connection's lookup key: the project data dir (absolute), or `*` outside a project. */
206
+ export function projectKey(projectDir) {
207
+ return projectDir === null ? "*" : resolvePath(projectDir);
208
+ }
209
+ async function readConnections(deps) {
210
+ const path = connectionsPath(deps);
211
+ let raw;
212
+ try {
213
+ raw = await readFile(path, "utf8");
214
+ }
215
+ catch (err) {
216
+ if (err?.code === "ENOENT")
217
+ return { version: 1, connections: {} };
218
+ throw new JourneezeError("E_JOURNEEZE_CONFIG", `cannot read ${path}`);
219
+ }
220
+ try {
221
+ const parsed = JSON.parse(raw);
222
+ if (parsed.version !== 1 || typeof parsed.connections !== "object" || parsed.connections === null)
223
+ throw new Error("shape");
224
+ return parsed;
225
+ }
226
+ catch {
227
+ throw new JourneezeError("E_JOURNEEZE_CONFIG", `${path} is not a jevitate Journeeze connections file — fix or delete it, then run \`jevitate connect journeeze\``);
228
+ }
229
+ }
230
+ /**
231
+ * The saved connection for a project: ONLY that project's own (a connection made in one project, or
232
+ * outside any, never uploads another project's catalog). The `*` connection (made outside any
233
+ * project) applies only outside a project, and never inside an MCP call. A project without its own
234
+ * connection is not connected: a person runs `jevitate connect journeeze` in that project.
235
+ */
236
+ export async function loadConnection(projectDir, deps = {}) {
237
+ if (projectDir === null && inMcpInvocation())
238
+ return undefined;
239
+ const file = await readConnections(deps);
240
+ const key = projectKey(projectDir);
241
+ return Object.prototype.hasOwnProperty.call(file.connections, key) ? file.connections[key] : undefined;
242
+ }
243
+ /** Saves a connection (a reference, never a key) — refuses anything that looks like a key. */
244
+ export async function saveConnection(projectDir, conn, deps = {}) {
245
+ const file = await readConnections(deps);
246
+ const next = { version: 1, connections: { ...file.connections, [projectKey(projectDir)]: conn } };
247
+ const text = `${JSON.stringify(next, null, 2)}\n`;
248
+ if (KEY_LIKE_RE.test(text.replace(/"keyPrefix": "jzu_[a-z2-7]{4}"/g, ""))) {
249
+ throw new JourneezeError("E_JOURNEEZE_CONFIG", "refusing to save: the connection would hold an upload key in plain text");
250
+ }
251
+ const path = connectionsPath(deps);
252
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
253
+ const tmp = `${path}.${process.pid}.tmp`;
254
+ await writeFile(tmp, text, { mode: 0o600 });
255
+ await chmod(tmp, 0o600);
256
+ await rename(tmp, path);
257
+ return path;
258
+ }
259
+ /** Verifies a key with `GET /whoami` — the only call `connect` makes with it. */
260
+ export async function whoami(http, origin, key) {
261
+ const res = await sendPinned(http, origin, {
262
+ method: "GET",
263
+ url: `${origin}${JOURNEEZE_API_PREFIX}/whoami`,
264
+ headers: { Authorization: `Bearer ${key}`, Accept: "application/json" },
265
+ });
266
+ const body = await jsonBody(res);
267
+ if (res.status !== 200) {
268
+ const reconnect = res.status === 401 ? " — check the key (create a new one on the Journeeze board if it was revoked or expired)" : "";
269
+ throw new JourneezeError(res.status === 401 ? "E_JOURNEEZE_KEY_REFUSED" : "E_JOURNEEZE_HTTP", `Journeeze refused the key check: ${describeError(res, body)}${reconnect}`);
270
+ }
271
+ const product = body.product;
272
+ if (typeof product?.id !== "string" || typeof product.name !== "string") {
273
+ throw new JourneezeError("E_JOURNEEZE_HTTP", "Journeeze's key check returned no product");
274
+ }
275
+ const tenant = body.tenant?.name;
276
+ const prefix = typeof body.keyPrefix === "string" && /^jzu_[a-z2-7]{4}$/.test(body.keyPrefix) ? body.keyPrefix : key.slice(0, 8);
277
+ return { product: { id: product.id, name: product.name }, tenant: typeof tenant === "string" ? tenant : "", keyPrefix: prefix };
278
+ }
279
+ export function processTerminal() {
280
+ return {
281
+ isTTY: process.stdin.isTTY === true && process.stderr.isTTY === true,
282
+ async ask(question) {
283
+ const { createInterface } = await import("node:readline/promises");
284
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
285
+ try {
286
+ return await rl.question(`${question} `);
287
+ }
288
+ finally {
289
+ rl.close();
290
+ }
291
+ },
292
+ askHidden: (question) => readMaskedLine(process.stdin, process.stderr, question),
293
+ say: (line) => void process.stderr.write(`${line}\n`),
294
+ };
295
+ }
296
+ const REF_PROMPT = [
297
+ "Where do you keep this product's Journeeze upload key? jevitate saves only this reference, never the key:",
298
+ " env:<VAR> an environment variable",
299
+ " cmd:<command> a command that prints it",
300
+ " op://… | bw:<item> | pass:<path> a password manager entry",
301
+ ].join("\n");
302
+ /**
303
+ * `jevitate connect journeeze`: the person names where the key is kept (or types the key, hidden, to
304
+ * check it first — then names where it is kept, which must hold the same key); the key is verified
305
+ * with `whoami`; the person confirms the product; only the reference is saved.
306
+ */
307
+ export async function runConnect(baseUrl, projectDir, deps = {}) {
308
+ const origin = pinnedJourneezeOrigin(baseUrl);
309
+ const term = deps.terminal ?? processTerminal();
310
+ if (!term.isTTY) {
311
+ throw new JourneezeError("E_CONNECT_NEEDS_TTY", "connect journeeze needs a person at a terminal (it asks for the upload key); in CI set JOURNEEZE_UPLOAD_KEY instead");
312
+ }
313
+ const http = deps.http ?? fetchJourneezeHttp;
314
+ const sources = deps.sources ?? defaultKeySources();
315
+ let typed;
316
+ const seen = [];
317
+ try {
318
+ term.say(REF_PROMPT);
319
+ let answer = (await term.ask("Reference (leave empty to type the key itself and check it first):")).trim();
320
+ if (answer === "") {
321
+ typed = (await term.askHidden("Journeeze upload key (jzu_…; hidden):")).trim();
322
+ seen.push(typed);
323
+ if (!JOURNEEZE_KEY_RE.test(typed))
324
+ throw new JourneezeError("E_JOURNEEZE_KEY_FORMAT", "that is not a Journeeze upload key (jzu_ followed by 40 characters)");
325
+ const who = await whoami(http, origin, typed);
326
+ term.say(`The key is valid for '${who.product.name}'${who.tenant ? ` (${who.tenant})` : ""}. Now say where it is kept.`);
327
+ answer = (await term.ask("Reference:")).trim();
328
+ }
329
+ const ref = parseKeyRef(answer, origin);
330
+ const key = await resolveKeyRef(ref, sources);
331
+ seen.push(key);
332
+ if (typed !== undefined && key !== typed) {
333
+ throw new JourneezeError("E_JOURNEEZE_REF", `${describeRef(ref)} holds a different key than the one you typed — nothing was saved`);
334
+ }
335
+ const who = await whoami(http, origin, key);
336
+ const yes = (await term.ask(`Connect this project to Journeeze product '${who.product.name}'${who.tenant ? ` (${who.tenant})` : ""} at ${origin}? [y/N]`)).trim().toLowerCase();
337
+ if (yes !== "y" && yes !== "yes") {
338
+ throw new JourneezeError("E_CONNECT_DECLINED", "not connected — nothing was saved");
339
+ }
340
+ await saveConnection(projectDir, { baseUrl: origin, product: who.product, keyPrefix: who.keyPrefix, keyRef: ref, connectedAt: new Date(clock.now()).toISOString() }, deps);
341
+ return { baseUrl: origin, product: who.product, keyPrefix: who.keyPrefix, stored: true };
342
+ }
343
+ catch (err) {
344
+ throw scrubError(err, seen, "E_CONNECT");
345
+ }
346
+ }
347
+ /** Re-throws `err` as a `JourneezeError` whose message has every given secret removed. */
348
+ export function scrubError(err, secrets, fallbackCode) {
349
+ const code = err instanceof JourneezeError ? err.code : fallbackCode;
350
+ const message = err instanceof Error ? err.message : String(err);
351
+ return new JourneezeError(code, redactText(message, secrets));
352
+ }
353
+ /** Sleeps on jevitate's clock (tests inject their own). */
354
+ export function clockSleep(ms) {
355
+ return new Promise((r) => clock.setTimeout(r, ms));
356
+ }
357
+ //# sourceMappingURL=journeeze-connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journeeze-connect.js","sourceRoot":"","sources":["../src/journeeze-connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { AnchorEditError, type AddAnchorInput, type AnchorLintIssue, type AnchorSuggestion, type StampedAnchor } from "@jevitate/journey";
2
+ /**
3
+ * #487: edit a draft Journey's anchors (`journey anchor add|remove`, `journey lint --fix`) — the API
4
+ * both the CLI and MCP call. Each edit writes the Journey (step ids minted and anchors stamped the
5
+ * way `promote` does) and returns the anchor lint issues that remain.
6
+ */
7
+ /** A PROMOTED Journey's anchors changed: its approval binds to the content hash, so the edit would silently void it. Exit 64. */
8
+ export declare class PromotedJourneyEditError extends Error {
9
+ readonly code = "E_JOURNEY_ANCHOR_ARGS";
10
+ constructor(id: string);
11
+ }
12
+ export interface AnchorEditResult {
13
+ readonly journeyId: string;
14
+ /** The Journey's anchor-rule problems after the edit (warnings; empty when it complies). */
15
+ readonly issues: readonly AnchorLintIssue[];
16
+ }
17
+ export interface AnchorFixResult extends AnchorEditResult {
18
+ /** The anchors given a step id (empty: nothing needed it, nothing was written). */
19
+ readonly stamped: readonly StampedAnchor[];
20
+ }
21
+ /** Adds an anchor (`stepId` written at once). Throws `UnknownJourneyError`, `PromotedJourneyEditError`, `AnchorEditError`. */
22
+ export declare function addJourneyAnchor(dir: string, id: string, input: AddAnchorInput): Promise<AnchorEditResult>;
23
+ /** Removes the named anchor. Throws as `addJourneyAnchor`. */
24
+ export declare function removeJourneyAnchor(dir: string, id: string, name: string): Promise<AnchorEditResult>;
25
+ /** `journey lint --fix`: stamps each number-only anchor with its step's id and writes the Journey. Nothing to stamp: nothing written, even when promoted. */
26
+ export declare function fixJourneyAnchors(dir: string, id: string): Promise<AnchorFixResult>;
27
+ export interface AnchorSuggestResult {
28
+ readonly journeyId: string;
29
+ readonly steps: number;
30
+ /** Advisory only: nothing was written. Each carries the `journey anchor add` command that accepts it. */
31
+ readonly suggestions: readonly (AnchorSuggestion & {
32
+ readonly command: string;
33
+ })[];
34
+ }
35
+ /** `journey anchors --suggest`: anchors worth adding where the recorded flow changes state. Writes nothing. */
36
+ export declare function suggestJourneyAnchors(dir: string, id: string, readRequests?: readonly string[]): Promise<AnchorSuggestResult>;
37
+ export { AnchorEditError };
@@ -0,0 +1,58 @@
1
+ import { AnchorEditError, FsJourneyStore, JourneyRegistry, addAnchor, anchorAddCommand, anchorLintIssues, removeAnchor, suggestAnchors, stampUnstampedAnchors } from "@jevitate/journey";
2
+ import { UnknownJourneyError } from "./journey-api.js";
3
+ /**
4
+ * #487: edit a draft Journey's anchors (`journey anchor add|remove`, `journey lint --fix`) — the API
5
+ * both the CLI and MCP call. Each edit writes the Journey (step ids minted and anchors stamped the
6
+ * way `promote` does) and returns the anchor lint issues that remain.
7
+ */
8
+ /** A PROMOTED Journey's anchors changed: its approval binds to the content hash, so the edit would silently void it. Exit 64. */
9
+ export class PromotedJourneyEditError extends Error {
10
+ code = "E_JOURNEY_ANCHOR_ARGS";
11
+ constructor(id) {
12
+ super(`journey '${id}' is promoted: changing its anchors changes the content hash its approval is bound to (stamping a step id included, since the hash covers it). ` +
13
+ `Edit it as a draft and re-promote — jevitate journey review ${id}, then jevitate journey promote ${id} — a person re-approves the new content`);
14
+ }
15
+ }
16
+ async function load(dir, id) {
17
+ const store = new FsJourneyStore(dir);
18
+ const journey = await new JourneyRegistry(store).get(id);
19
+ if (journey === null)
20
+ throw new UnknownJourneyError(`unknown journey '${id}'`);
21
+ return { store, journey };
22
+ }
23
+ async function edit(dir, id, change) {
24
+ const { store, journey } = await load(dir, id);
25
+ if (journey.metadata.promoted)
26
+ throw new PromotedJourneyEditError(id);
27
+ const next = change(journey);
28
+ await store.put(next);
29
+ const written = (await store.get(id)) ?? next;
30
+ return { journeyId: id, issues: anchorLintIssues(written) };
31
+ }
32
+ /** Adds an anchor (`stepId` written at once). Throws `UnknownJourneyError`, `PromotedJourneyEditError`, `AnchorEditError`. */
33
+ export function addJourneyAnchor(dir, id, input) {
34
+ return edit(dir, id, (j) => addAnchor(j, input));
35
+ }
36
+ /** Removes the named anchor. Throws as `addJourneyAnchor`. */
37
+ export function removeJourneyAnchor(dir, id, name) {
38
+ return edit(dir, id, (j) => removeAnchor(j, name));
39
+ }
40
+ /** `journey lint --fix`: stamps each number-only anchor with its step's id and writes the Journey. Nothing to stamp: nothing written, even when promoted. */
41
+ export async function fixJourneyAnchors(dir, id) {
42
+ const { store, journey } = await load(dir, id);
43
+ const { journey: next, stamped } = stampUnstampedAnchors(journey);
44
+ if (stamped.length === 0)
45
+ return { journeyId: id, issues: anchorLintIssues(journey), stamped };
46
+ if (journey.metadata.promoted)
47
+ throw new PromotedJourneyEditError(id);
48
+ await store.put(next);
49
+ return { journeyId: id, issues: anchorLintIssues((await store.get(id)) ?? next), stamped };
50
+ }
51
+ /** `journey anchors --suggest`: anchors worth adding where the recorded flow changes state. Writes nothing. */
52
+ export async function suggestJourneyAnchors(dir, id, readRequests) {
53
+ const { journey } = await load(dir, id);
54
+ const suggestions = suggestAnchors(journey, { readRequests }).map((s) => ({ ...s, command: anchorAddCommand(id, s) }));
55
+ return { journeyId: id, steps: journey.recording.pages.reduce((n, p) => n + p.steps.length, 0), suggestions };
56
+ }
57
+ export { AnchorEditError };
58
+ //# sourceMappingURL=journey-anchor-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-anchor-api.js","sourceRoot":"","sources":["../src/journey-anchor-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Command } from "commander";
2
+ import { type CliDeps } from "./cli-shared.js";
3
+ /** Maps an edit failure to its envelope: unknown id and refused edits are usage errors (64). */
4
+ export declare function anchorEditFailure(program: Command, err: unknown): void;
5
+ /**
6
+ * #487 — `jevitate journey anchor add|remove`: edit a draft Journey's anchors without hand-editing
7
+ * `metadata.anchors`. The step id is written at once; the anchor lint issues that remain are printed.
8
+ */
9
+ export declare function registerJourneyAnchorCommands(journey: Command, program: Command, deps: CliDeps): void;
10
+ /**
11
+ * #487 — `journey anchors <id> --suggest`: advisory anchor proposals (a new page, a form that became
12
+ * valid, a completed write), each with the `journey anchor add` command that accepts it. Writes nothing.
13
+ */
14
+ export declare function runAnchorSuggest(program: Command, deps: CliDeps, id: string, opts: {
15
+ dir?: string;
16
+ json?: boolean;
17
+ }): Promise<void>;
@@ -0,0 +1,104 @@
1
+ import { ok, fail } from "./envelope.js";
2
+ import { UnknownJourneyError } from "./journey-api.js";
3
+ import { AnchorEditError, PromotedJourneyEditError, addJourneyAnchor, removeJourneyAnchor, suggestJourneyAnchors } from "./journey-anchor-api.js";
4
+ import { emitJson, resolveJourneysDir } from "./cli-shared.js";
5
+ function collect(value, prev) {
6
+ return [...prev, value];
7
+ }
8
+ /** One human line per anchor-rule problem left after an edit. */
9
+ function renderIssues(result, out) {
10
+ for (const i of result.issues)
11
+ out?.(`${i.severity}\t${i.code}\t${i.message}\n\t fix: ${i.fix}\n`);
12
+ out?.(`${result.issues.length} anchor issue(s) — journey '${result.journeyId}'\n`);
13
+ }
14
+ /** Maps an edit failure to its envelope: unknown id and refused edits are usage errors (64). */
15
+ export function anchorEditFailure(program, err) {
16
+ if (err instanceof UnknownJourneyError)
17
+ emitJson(program, fail("E_UNKNOWN_JOURNEY", err.message));
18
+ else if (err instanceof AnchorEditError || err instanceof PromotedJourneyEditError)
19
+ emitJson(program, fail("E_JOURNEY_ANCHOR_ARGS", err.message));
20
+ else
21
+ emitJson(program, fail("E_JOURNEY_ANCHOR_ARGS", String(err instanceof Error ? err.message : err)));
22
+ }
23
+ /**
24
+ * #487 — `jevitate journey anchor add|remove`: edit a draft Journey's anchors without hand-editing
25
+ * `metadata.anchors`. The step id is written at once; the anchor lint issues that remain are printed.
26
+ */
27
+ export function registerJourneyAnchorCommands(journey, program, deps) {
28
+ const anchor = journey.command("anchor").description("add or remove a draft Journey's anchors (#487): named steps to branch missions off, written with the step's stable id");
29
+ anchor
30
+ .command("add <journeyId>")
31
+ .description("add an anchor to a draft Journey: --step takes a 1-based step number or a step id and writes the step id at once; re-lints and prints the anchor issues")
32
+ .requiredOption("--name <name>", "the anchor's name: 1-64 of lowercase a-z, 0-9, . _ - starting with a letter or digit")
33
+ .requiredOption("--step <n|stepId>", "the step the anchor follows: a 1-based step number or a step id")
34
+ .option("--description <text>", "what this state is, in words")
35
+ .option("--probe <text>", "a suggested adversarial probe at this state (repeatable)", collect, [])
36
+ .option("--dir <path>", "journeys directory (default: the repo's .jevitate/journeys; outside a repo ~/.jevitate/journeys)")
37
+ .option("--json", "emit a JSON envelope")
38
+ .action(async function (journeyId) {
39
+ const { name, step, description, probe, dir, json } = this.opts();
40
+ try {
41
+ const result = await addJourneyAnchor(resolveJourneysDir(deps, dir), journeyId, { name, step, ...(description === undefined ? {} : { description }), probes: probe });
42
+ if (json)
43
+ emitJson(program, ok(result));
44
+ else {
45
+ const out = program.configureOutput().writeOut;
46
+ out?.(`added anchor '${name}' to journey '${journeyId}' (step ${step})\n`);
47
+ renderIssues(result, out);
48
+ process.exitCode = 0;
49
+ }
50
+ }
51
+ catch (err) {
52
+ anchorEditFailure(program, err);
53
+ }
54
+ });
55
+ anchor
56
+ .command("remove <journeyId>")
57
+ .description("remove an anchor from a draft Journey; re-lints and prints the anchor issues")
58
+ .requiredOption("--name <name>", "the anchor to remove")
59
+ .option("--dir <path>", "journeys directory (default: the repo's .jevitate/journeys; outside a repo ~/.jevitate/journeys)")
60
+ .option("--json", "emit a JSON envelope")
61
+ .action(async function (journeyId) {
62
+ const { name, dir, json } = this.opts();
63
+ try {
64
+ const result = await removeJourneyAnchor(resolveJourneysDir(deps, dir), journeyId, name);
65
+ if (json)
66
+ emitJson(program, ok(result));
67
+ else {
68
+ const out = program.configureOutput().writeOut;
69
+ out?.(`removed anchor '${name}' from journey '${journeyId}'\n`);
70
+ renderIssues(result, out);
71
+ process.exitCode = 0;
72
+ }
73
+ }
74
+ catch (err) {
75
+ anchorEditFailure(program, err);
76
+ }
77
+ });
78
+ }
79
+ /**
80
+ * #487 — `journey anchors <id> --suggest`: advisory anchor proposals (a new page, a form that became
81
+ * valid, a completed write), each with the `journey anchor add` command that accepts it. Writes nothing.
82
+ */
83
+ export async function runAnchorSuggest(program, deps, id, opts) {
84
+ try {
85
+ const result = await suggestJourneyAnchors(resolveJourneysDir(deps, opts.dir), id);
86
+ if (opts.json) {
87
+ emitJson(program, ok(result));
88
+ return;
89
+ }
90
+ const out = program.configureOutput().writeOut;
91
+ for (const s of result.suggestions) {
92
+ out?.(`${s.name}\tstep ${s.step}${s.stepId === undefined ? "" : ` (${s.stepId})`}\t${s.kind}\t${s.reason}\n\tprobes: ${s.probes.join("; ")}\n\t${s.command}\n`);
93
+ }
94
+ out?.(`${result.suggestions.length} suggestion(s) for journey '${id}' — advisory, nothing was written\n`);
95
+ process.exitCode = 0;
96
+ }
97
+ catch (err) {
98
+ if (err instanceof UnknownJourneyError)
99
+ emitJson(program, fail("E_UNKNOWN_JOURNEY", err.message));
100
+ else
101
+ emitJson(program, fail("E_JOURNEY_ANCHORS_ARGS", String(err instanceof Error ? err.message : err)));
102
+ }
103
+ }
104
+ //# sourceMappingURL=journey-anchor-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-anchor-cli.js","sourceRoot":"","sources":["../src/journey-anchor-cli.ts"],"names":[],"mappings":"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"}