@jevitate/cli 0.9.0 → 0.10.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.
- package/dist/anchor-baselines.d.ts +26 -0
- package/dist/anchor-baselines.js +25 -0
- package/dist/anchor-baselines.js.map +1 -0
- package/dist/approval-provenance.d.ts +32 -2
- package/dist/approval-provenance.js +92 -5
- package/dist/approval-provenance.js.map +1 -1
- package/dist/bin.js +10194 -6042
- package/dist/build-info.generated.js +2 -2
- package/dist/catalog-analysis.js +14 -0
- package/dist/catalog-analysis.js.map +1 -1
- package/dist/catalog-api.js +12 -4
- package/dist/catalog-api.js.map +1 -1
- package/dist/catalog-bundle-api.d.ts +73 -0
- package/dist/catalog-bundle-api.js +206 -0
- package/dist/catalog-bundle-api.js.map +1 -0
- package/dist/catalog-bundle-findings.d.ts +54 -0
- package/dist/catalog-bundle-findings.js +252 -0
- package/dist/catalog-bundle-findings.js.map +1 -0
- package/dist/catalog-bundle.d.ts +192 -0
- package/dist/catalog-bundle.js +578 -0
- package/dist/catalog-bundle.js.map +1 -0
- package/dist/catalog-cli.d.ts +6 -0
- package/dist/catalog-cli.js +180 -5
- package/dist/catalog-cli.js.map +1 -1
- package/dist/catalog-refs.d.ts +42 -0
- package/dist/catalog-refs.js +188 -0
- package/dist/catalog-refs.js.map +1 -0
- package/dist/catalog-review.d.ts +3 -1
- package/dist/catalog-review.js +61 -0
- package/dist/catalog-review.js.map +1 -1
- package/dist/catalog.d.ts +27 -3
- package/dist/catalog.js +46 -2
- package/dist/catalog.js.map +1 -1
- package/dist/check-api.js +162 -9
- package/dist/check-api.js.map +1 -1
- package/dist/check-cli.js +71 -2
- package/dist/check-cli.js.map +1 -1
- package/dist/check-execute.d.ts +12 -0
- package/dist/check-execute.js +55 -3
- package/dist/check-execute.js.map +1 -1
- package/dist/check-types.d.ts +98 -0
- package/dist/check-types.js.map +1 -1
- package/dist/cli-output.d.ts +6 -0
- package/dist/cli-output.js +23 -0
- package/dist/cli-output.js.map +1 -1
- package/dist/cli-shared.d.ts +10 -0
- package/dist/cli-shared.js +0 -4
- package/dist/cli-shared.js.map +1 -1
- package/dist/exit-codes.js +15 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/forge-verify.d.ts +224 -0
- package/dist/forge-verify.js +649 -0
- package/dist/forge-verify.js.map +1 -0
- package/dist/init-codeowners.d.ts +19 -0
- package/dist/init-codeowners.js +66 -0
- package/dist/init-codeowners.js.map +1 -1
- package/dist/job-draft-api.d.ts +52 -0
- package/dist/job-draft-api.js +110 -0
- package/dist/job-draft-api.js.map +1 -0
- package/dist/journeeze-api.d.ts +71 -0
- package/dist/journeeze-api.js +285 -0
- package/dist/journeeze-api.js.map +1 -0
- package/dist/journeeze-connect.d.ts +156 -0
- package/dist/journeeze-connect.js +357 -0
- package/dist/journeeze-connect.js.map +1 -0
- package/dist/journey-anchor-gate.d.ts +52 -0
- package/dist/journey-anchor-gate.js +101 -0
- package/dist/journey-anchor-gate.js.map +1 -0
- package/dist/journey-api.d.ts +1 -0
- package/dist/journey-api.js +37 -10
- package/dist/journey-api.js.map +1 -1
- package/dist/journey-cli.js +57 -5
- package/dist/journey-cli.js.map +1 -1
- package/dist/journey-heal.js +1 -1
- package/dist/journey-heal.js.map +1 -1
- package/dist/journey-lint-sarif.d.ts +3 -2
- package/dist/journey-lint-sarif.js +9 -2
- package/dist/journey-lint-sarif.js.map +1 -1
- package/dist/journey-migrate-api.d.ts +54 -0
- package/dist/journey-migrate-api.js +64 -0
- package/dist/journey-migrate-api.js.map +1 -0
- package/dist/journey-proposal-store.d.ts +9 -0
- package/dist/journey-proposal-store.js +23 -0
- package/dist/journey-proposal-store.js.map +1 -1
- package/dist/journey-result-record.d.ts +2 -0
- package/dist/journey-result-record.js +5 -0
- package/dist/journey-result-record.js.map +1 -1
- package/dist/journey-review-api.js +9 -1
- package/dist/journey-review-api.js.map +1 -1
- package/dist/journey-review.d.ts +11 -1
- package/dist/journey-review.js +77 -5
- package/dist/journey-review.js.map +1 -1
- package/dist/journey-stale-api.d.ts +40 -0
- package/dist/journey-stale-api.js +57 -0
- package/dist/journey-stale-api.js.map +1 -0
- package/dist/locator-health-api.d.ts +101 -0
- package/dist/locator-health-api.js +203 -0
- package/dist/locator-health-api.js.map +1 -0
- package/dist/locator-health.d.ts +195 -0
- package/dist/locator-health.js +419 -0
- package/dist/locator-health.js.map +1 -0
- package/dist/mcp-cli-tools.d.ts +3 -1
- package/dist/mcp-cli-tools.js +55 -5
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/project-config.d.ts +29 -0
- package/dist/project-config.js +76 -0
- package/dist/project-config.js.map +1 -0
- package/package.json +18 -16
- package/skills/jevitate-ci-check/SKILL.md +16 -0
- package/skills/jevitate-getting-started/SKILL.md +7 -2
- package/skills/jevitate-run-journey/SKILL.md +11 -0
- package/skills/jevitate-test-campaign/SKILL.md +13 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join, relative, isAbsolute } from "node:path";
|
|
5
|
+
import { envCredentialStore, redactText } from "@jevitate/ai-core";
|
|
6
|
+
import { exportCatalogBundle } from "./catalog-bundle-api.js";
|
|
7
|
+
import { loadLocalCredentials } from "./credentials-file.js";
|
|
8
|
+
import { readCliVersion } from "./version.js";
|
|
9
|
+
import { JOURNEEZE_API_PREFIX, JOURNEEZE_KEY_ENV, JOURNEEZE_KEY_RE, JOURNEEZE_URL_ENV, JourneezeError, clockSleep, defaultKeySources, describeError, describeRef, fetchJourneezeHttp, jsonBody, loadConnection, pinnedJourneezeOrigin, resolveKeyRef, runConnect, scrubError, sendPinned, } from "./journeeze-connect.js";
|
|
10
|
+
/**
|
|
11
|
+
* #464 — the Journeeze upload (journeeze-saas `docs/contract/catalog-bundle-upload-v1.md` §9).
|
|
12
|
+
*
|
|
13
|
+
* - `jevitate connect journeeze [--url <base>]` — CLI ONLY, never an MCP tool (journeeze-connect.ts):
|
|
14
|
+
* the person names where the per-product upload key (`jzu_…`) is kept — an env var, a command, or a
|
|
15
|
+
* password-manager entry — or types it hidden to check it first; the key is verified with
|
|
16
|
+
* `GET /api/upload/v1/whoami`, the person confirms the product, and ONLY the reference is saved
|
|
17
|
+
* (`~/.jevitate/journeeze.json`, bound to the Journeeze origin). The key is never saved by jevitate.
|
|
18
|
+
* - `jevitate publish journeeze [--dry-run]` / MCP `publish_to_journeeze`: exports the bundle
|
|
19
|
+
* (catalog-bundle-api.ts), `POST /api/upload/v1/bundles` (bundle.json as `application/json`: 0.10
|
|
20
|
+
* exports no media) with `Idempotency-Key = sha256-<hex of bundle.json>`, polls `statusUrl` to
|
|
21
|
+
* `imported`/`refused`, and reports summary, warnings and errors. The key is resolved by jevitate
|
|
22
|
+
* itself: `JOURNEEZE_UPLOAD_KEY` (CI; never the plaintext credentials file), else the saved
|
|
23
|
+
* reference. It is never an argument, never in a result, never in an error — the model never sees
|
|
24
|
+
* it. Hosts are pinned (contract §2) and redirects are never followed. Publishing never approves.
|
|
25
|
+
*/
|
|
26
|
+
export const JOURNEEZE_DEFAULT_URL = "https://app.journeeze.dev";
|
|
27
|
+
/** Interactive: needs a person at a terminal (refuses without a TTY). The key is read here, never passed in. */
|
|
28
|
+
export async function connectJourneeze(req, deps = {}) {
|
|
29
|
+
return runConnect(req.baseUrl, req.projectDir, deps);
|
|
30
|
+
}
|
|
31
|
+
/** Contract §4.2/§6: the body limit, and bundle.json's own (the JSON body IS bundle.json). */
|
|
32
|
+
const MAX_BUNDLE_JSON_BYTES = 16 * 1024 * 1024;
|
|
33
|
+
const POLL_START_MS = 1_000;
|
|
34
|
+
const POLL_CAP_MS = 15_000;
|
|
35
|
+
const POLL_BUDGET_MS = 10 * 60_000;
|
|
36
|
+
/** Attempts for a retryable failure (429/503/network) — always with the same Idempotency-Key. */
|
|
37
|
+
const MAX_ATTEMPTS = 3;
|
|
38
|
+
const MAX_RETRY_AFTER_MS = 60_000;
|
|
39
|
+
/**
|
|
40
|
+
* The key and origin to publish with: `JOURNEEZE_UPLOAD_KEY` (through ai-core's CredentialStore,
|
|
41
|
+
* whose plaintext-file fallback is disabled for it) at `JOURNEEZE_URL` / the saved origin / the
|
|
42
|
+
* default; else the saved reference, at the origin it is bound to.
|
|
43
|
+
*/
|
|
44
|
+
async function resolvePublishKey(req, deps) {
|
|
45
|
+
const env = deps.env ?? process.env;
|
|
46
|
+
let local = {};
|
|
47
|
+
try {
|
|
48
|
+
local = loadLocalCredentials(deps.homedir ? { homedir: deps.homedir } : {});
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
local = {}; // an unreadable credentials file never supplies this key anyway
|
|
52
|
+
}
|
|
53
|
+
const fromEnv = envCredentialStore(env, local).read(JOURNEEZE_KEY_ENV);
|
|
54
|
+
const saved = await loadConnection(req.catalogDir, deps);
|
|
55
|
+
const envUrl = env[JOURNEEZE_URL_ENV]?.trim();
|
|
56
|
+
if (fromEnv !== undefined) {
|
|
57
|
+
if (!JOURNEEZE_KEY_RE.test(fromEnv)) {
|
|
58
|
+
throw new JourneezeError("E_JOURNEEZE_KEY_FORMAT", `${JOURNEEZE_KEY_ENV} is not a Journeeze upload key (jzu_ followed by 40 characters)`);
|
|
59
|
+
}
|
|
60
|
+
return { key: fromEnv, origin: pinnedJourneezeOrigin(envUrl || saved?.baseUrl || JOURNEEZE_DEFAULT_URL) };
|
|
61
|
+
}
|
|
62
|
+
if (saved === undefined) {
|
|
63
|
+
throw new JourneezeError("E_JOURNEEZE_NOT_CONNECTED", `this project is not connected to Journeeze: a person runs \`jevitate connect journeeze\` at their terminal (or CI sets ${JOURNEEZE_KEY_ENV})`);
|
|
64
|
+
}
|
|
65
|
+
const origin = pinnedJourneezeOrigin(saved.baseUrl);
|
|
66
|
+
if (pinnedJourneezeOrigin(saved.keyRef.origin) !== origin || (envUrl && pinnedJourneezeOrigin(envUrl) !== origin)) {
|
|
67
|
+
throw new JourneezeError("E_JOURNEEZE_ORIGIN", `the saved key reference is bound to ${saved.keyRef.origin} — refusing to use it for another origin; run \`jevitate connect journeeze\` again`);
|
|
68
|
+
}
|
|
69
|
+
const sources = deps.sources ?? defaultKeySources(env);
|
|
70
|
+
try {
|
|
71
|
+
return { key: await resolveKeyRef(saved.keyRef, sources), origin };
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err instanceof JourneezeError) {
|
|
75
|
+
throw new JourneezeError(err.code, `${err.message} — fix ${describeRef(saved.keyRef)} or run \`jevitate connect journeeze\` again`);
|
|
76
|
+
}
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Exports the bundle into a temp dir and checks what the contract checks before `202` (§4.2). */
|
|
81
|
+
async function buildBundle(req, deps, key) {
|
|
82
|
+
const outDir = await mkdtemp(join(tmpdir(), "jev-journeeze-"));
|
|
83
|
+
try {
|
|
84
|
+
const exported = await (deps.exportBundle ?? exportCatalogBundle)({ format: "journeeze-bundle", catalogDir: req.catalogDir, journeysDir: req.journeysDir, outDir });
|
|
85
|
+
const rel = relative(outDir, exported.bundlePath);
|
|
86
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel))
|
|
87
|
+
throw new JourneezeError("E_JOURNEEZE_BUNDLE", "the exported bundle is not where it was asked to be written");
|
|
88
|
+
const body = new Uint8Array(await readFile(exported.bundlePath));
|
|
89
|
+
if (body.byteLength > MAX_BUNDLE_JSON_BYTES) {
|
|
90
|
+
throw new JourneezeError("E_JOURNEEZE_BUNDLE", `bundle.json is ${body.byteLength} bytes — over Journeeze's ${MAX_BUNDLE_JSON_BYTES}-byte limit`);
|
|
91
|
+
}
|
|
92
|
+
const text = Buffer.from(body).toString("utf8");
|
|
93
|
+
if (text.includes(key))
|
|
94
|
+
throw new JourneezeError("E_JOURNEEZE_BUNDLE", "the bundle contains the upload key — refusing to send it");
|
|
95
|
+
let bundle;
|
|
96
|
+
try {
|
|
97
|
+
bundle = JSON.parse(text);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw new JourneezeError("E_JOURNEEZE_BUNDLE", "the exported bundle.json is not JSON");
|
|
101
|
+
}
|
|
102
|
+
if (bundle.kind !== "journeeze.catalog-bundle" || bundle.version !== 1) {
|
|
103
|
+
throw new JourneezeError("E_JOURNEEZE_BUNDLE", 'the exported bundle is not a v1 catalog bundle (kind "journeeze.catalog-bundle", version 1)');
|
|
104
|
+
}
|
|
105
|
+
if (Array.isArray(bundle.files) && bundle.files.length > 0) {
|
|
106
|
+
throw new JourneezeError("E_JOURNEEZE_BUNDLE", "the bundle lists media files; this version uploads only a media-free bundle (application/json)");
|
|
107
|
+
}
|
|
108
|
+
const hex = createHash("sha256").update(body).digest("hex");
|
|
109
|
+
return { body, idempotencyKey: `sha256-${hex}` };
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
await rm(outDir, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const RETRYABLE = new Set([429, 503]);
|
|
116
|
+
function retryAfterMs(res, attempt) {
|
|
117
|
+
const header = res.headers.get("retry-after");
|
|
118
|
+
const seconds = header === null ? NaN : Number(header);
|
|
119
|
+
const ms = Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : POLL_START_MS * 2 ** attempt;
|
|
120
|
+
return Math.min(ms, MAX_RETRY_AFTER_MS);
|
|
121
|
+
}
|
|
122
|
+
/** Contract §5: what the person does about a non-2xx, by code. */
|
|
123
|
+
function errorFor(res, body) {
|
|
124
|
+
const what = describeError(res, body);
|
|
125
|
+
const code = typeof body.code === "string" ? body.code : "";
|
|
126
|
+
if (res.status === 401)
|
|
127
|
+
return new JourneezeError("E_JOURNEEZE_KEY_REFUSED", `Journeeze refused the upload key (${what}) — a person runs \`jevitate connect journeeze\` with a valid key`);
|
|
128
|
+
if (code === "key_in_url")
|
|
129
|
+
return new JourneezeError("E_JOURNEEZE_KEY_REVOKED", `Journeeze revoked the key because it appeared in a URL (${what}) — create a new key and reconnect`);
|
|
130
|
+
if (res.status === 403)
|
|
131
|
+
return new JourneezeError("E_JOURNEEZE_FORBIDDEN", `Journeeze refused the upload (${what}) — the key lacks the upload scope or the product is suspended`);
|
|
132
|
+
if (res.status === 409)
|
|
133
|
+
return new JourneezeError("E_JOURNEEZE_CONFLICT", `Journeeze refused the upload (${what})`);
|
|
134
|
+
if (RETRYABLE.has(res.status))
|
|
135
|
+
return new JourneezeError("E_JOURNEEZE_UNAVAILABLE", `Journeeze is busy or unavailable (${what}) — retry later; the same bundle reuses its Idempotency-Key`);
|
|
136
|
+
return new JourneezeError("E_JOURNEEZE_HTTP", `Journeeze refused the upload (${what})`);
|
|
137
|
+
}
|
|
138
|
+
const TERMINAL = new Set(["imported", "refused"]);
|
|
139
|
+
/**
|
|
140
|
+
* Builds the result from a status body (contract §4.3). The nested summary is flattened to
|
|
141
|
+
* `section.counter` numbers; findings become warnings; errors keep their code and path.
|
|
142
|
+
*/
|
|
143
|
+
function fromStatus(base, s, statusUrl) {
|
|
144
|
+
const summary = {};
|
|
145
|
+
const raw = s.body.summary;
|
|
146
|
+
if (typeof raw === "object" && raw !== null) {
|
|
147
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
148
|
+
if (typeof v === "number")
|
|
149
|
+
summary[k] = v;
|
|
150
|
+
else if (typeof v === "object" && v !== null)
|
|
151
|
+
for (const [k2, n] of Object.entries(v))
|
|
152
|
+
if (typeof n === "number")
|
|
153
|
+
summary[`${k}.${k2}`] = n;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const list = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === "object" && x !== null) : []);
|
|
157
|
+
const warnings = list(s.body.findings).map((f) => `${String(f.severity ?? "warning")} ${String(f.kind ?? "finding")}${Array.isArray(f.ids) ? `: ${f.ids.join(", ")}` : ""}`);
|
|
158
|
+
const errors = list(s.body.errors).map((e) => `${String(e.code ?? "error")}: ${String(e.message ?? "")}${typeof e.path === "string" ? ` (at ${e.path})` : ""}`);
|
|
159
|
+
const status = s.status === "imported" || s.status === "refused" ? s.status : "processing";
|
|
160
|
+
return {
|
|
161
|
+
dryRun: false,
|
|
162
|
+
...base,
|
|
163
|
+
...(s.uploadId === undefined ? {} : { uploadId: s.uploadId }),
|
|
164
|
+
statusUrl,
|
|
165
|
+
status,
|
|
166
|
+
...(Object.keys(summary).length > 0 ? { summary } : {}),
|
|
167
|
+
warnings: status === "processing" ? [...warnings, `Journeeze is still processing the upload — check ${statusUrl} later`] : warnings,
|
|
168
|
+
errors,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function parseStatus(body) {
|
|
172
|
+
return {
|
|
173
|
+
...(typeof body.uploadId === "string" ? { uploadId: body.uploadId } : {}),
|
|
174
|
+
...(typeof body.status === "string" ? { status: body.status } : {}),
|
|
175
|
+
...(typeof body.statusUrl === "string" ? { statusUrl: body.statusUrl } : {}),
|
|
176
|
+
body,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async function upload(origin, key, bundle, deps) {
|
|
180
|
+
const http = deps.http ?? fetchJourneezeHttp;
|
|
181
|
+
const sleep = deps.sleep ?? clockSleep;
|
|
182
|
+
const auth = `Bearer ${key}`;
|
|
183
|
+
const base = { baseUrl: origin, idempotencyKey: bundle.idempotencyKey };
|
|
184
|
+
const digest = `sha-256=:${createHash("sha256").update(bundle.body).digest("base64")}:`;
|
|
185
|
+
let accepted;
|
|
186
|
+
let digestRetried = false;
|
|
187
|
+
for (let attempt = 0; accepted === undefined; attempt++) {
|
|
188
|
+
let res;
|
|
189
|
+
try {
|
|
190
|
+
res = await sendPinned(http, origin, {
|
|
191
|
+
method: "POST",
|
|
192
|
+
url: `${origin}${JOURNEEZE_API_PREFIX}/bundles`,
|
|
193
|
+
headers: {
|
|
194
|
+
Authorization: auth,
|
|
195
|
+
"Content-Type": "application/json",
|
|
196
|
+
"Content-Length": String(bundle.body.byteLength),
|
|
197
|
+
"Content-Digest": digest,
|
|
198
|
+
"Idempotency-Key": bundle.idempotencyKey,
|
|
199
|
+
"Journeeze-Producer": `jevitate/${readCliVersion()}`,
|
|
200
|
+
Accept: "application/json",
|
|
201
|
+
},
|
|
202
|
+
body: bundle.body,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
if (err instanceof JourneezeError || attempt + 1 >= MAX_ATTEMPTS)
|
|
207
|
+
throw err;
|
|
208
|
+
await sleep(POLL_START_MS * 2 ** attempt); // a network failure: retry, same Idempotency-Key
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const body = await jsonBody(res);
|
|
212
|
+
if (res.status === 202 || res.status === 200) {
|
|
213
|
+
accepted = parseStatus(body);
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
if (RETRYABLE.has(res.status) && attempt + 1 < MAX_ATTEMPTS) {
|
|
217
|
+
await sleep(retryAfterMs(res, attempt));
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (res.status === 400 && body.code === "digest_mismatch" && !digestRetried) {
|
|
221
|
+
digestRetried = true; // contract §5: retry once, then stop
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (res.status === 413 || res.status === 422) {
|
|
225
|
+
// The bundle itself was refused before processing: a refused upload, not a transport failure.
|
|
226
|
+
return { dryRun: false, ...base, status: "refused", warnings: [], errors: [describeError(res, body)] };
|
|
227
|
+
}
|
|
228
|
+
throw errorFor(res, body);
|
|
229
|
+
}
|
|
230
|
+
const statusPath = accepted.statusUrl ?? (accepted.uploadId ? `${JOURNEEZE_API_PREFIX}/bundles/${encodeURIComponent(accepted.uploadId)}` : undefined);
|
|
231
|
+
if (statusPath === undefined)
|
|
232
|
+
throw new JourneezeError("E_JOURNEEZE_HTTP", "Journeeze accepted the upload but returned no upload id");
|
|
233
|
+
const statusUrl = new URL(statusPath, origin);
|
|
234
|
+
if (statusUrl.origin !== origin)
|
|
235
|
+
throw new JourneezeError("E_JOURNEEZE_ORIGIN", `Journeeze's statusUrl points to ${statusUrl.origin} — the key is never sent to another origin`);
|
|
236
|
+
let current = accepted;
|
|
237
|
+
let waited = 0;
|
|
238
|
+
let delay = POLL_START_MS;
|
|
239
|
+
const budget = deps.pollBudgetMs ?? POLL_BUDGET_MS;
|
|
240
|
+
while (!TERMINAL.has(current.status ?? "") && waited < budget) {
|
|
241
|
+
await sleep(delay);
|
|
242
|
+
waited += delay;
|
|
243
|
+
delay = Math.min(delay * 2, POLL_CAP_MS);
|
|
244
|
+
let res;
|
|
245
|
+
try {
|
|
246
|
+
res = await sendPinned(http, origin, { method: "GET", url: statusUrl.href, headers: { Authorization: auth, Accept: "application/json" } });
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
if (err instanceof JourneezeError)
|
|
250
|
+
throw err;
|
|
251
|
+
continue; // a network blip while polling: keep polling within the window
|
|
252
|
+
}
|
|
253
|
+
const body = await jsonBody(res);
|
|
254
|
+
if (res.status === 200) {
|
|
255
|
+
const next = parseStatus(body);
|
|
256
|
+
current = next.uploadId === undefined && current.uploadId !== undefined ? { ...next, uploadId: current.uploadId } : next;
|
|
257
|
+
}
|
|
258
|
+
else if (!RETRYABLE.has(res.status))
|
|
259
|
+
throw errorFor(res, body);
|
|
260
|
+
}
|
|
261
|
+
return fromStatus(base, current, statusUrl.href);
|
|
262
|
+
}
|
|
263
|
+
export async function publishToJourneeze(req, deps = {}) {
|
|
264
|
+
let key;
|
|
265
|
+
try {
|
|
266
|
+
const resolved = await resolvePublishKey(req, deps);
|
|
267
|
+
key = resolved.key;
|
|
268
|
+
const bundle = await buildBundle(req, deps, key);
|
|
269
|
+
const result = req.dryRun
|
|
270
|
+
? { dryRun: true, baseUrl: resolved.origin, idempotencyKey: bundle.idempotencyKey, status: "dry-run", warnings: [], errors: [] }
|
|
271
|
+
: await upload(resolved.origin, key, bundle, deps);
|
|
272
|
+
// Belt and braces: whatever Journeeze echoed, the key never leaves in a result.
|
|
273
|
+
const text = JSON.stringify(result);
|
|
274
|
+
return text.includes(key) ? JSON.parse(redactText(text, [key])) : result;
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
throw scrubError(err, key === undefined ? [] : [key], "E_PUBLISH_JOURNEEZE");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/** The human rendering (no `--json`). */
|
|
281
|
+
export function renderPublishJourneeze(r) {
|
|
282
|
+
const summary = r.summary === undefined ? "" : `${Object.entries(r.summary).map(([k, v]) => ` ${k}: ${v}`).join("\n")}\n`;
|
|
283
|
+
return `${r.dryRun ? "dry run: nothing sent" : `upload ${r.uploadId ?? "?"}: ${r.status}`} (${r.idempotencyKey}) → ${r.baseUrl}\n${summary}${r.warnings.map((w) => `warning: ${w}\n`).join("")}${r.errors.map((e) => `error: ${e}\n`).join("")}`;
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=journeeze-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journeeze-api.js","sourceRoot":"","sources":["../src/journeeze-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #464 — the Journeeze connection: which hosts an upload key may go to, how the key is found, and
|
|
3
|
+
* what `jevitate connect journeeze` saves (journeeze-saas `docs/contract/catalog-bundle-upload-v1.md`).
|
|
4
|
+
*
|
|
5
|
+
* The key itself is NEVER saved by jevitate. `connect` saves a REFERENCE to where the person keeps it
|
|
6
|
+
* (an environment variable, a command that prints it, or a password-manager entry), bound to the
|
|
7
|
+
* Journeeze origin it was verified against, in `~/.jevitate/journeeze.json` (outside the repo, 0600).
|
|
8
|
+
* The reference is shaped like `@jevitate/secrets`' `SecretRef` (`{manager, key, origin, field}`):
|
|
9
|
+
* jevitate delegates to the external source on every use and never implements a vault.
|
|
10
|
+
*/
|
|
11
|
+
/** The upload key's format (contract §3): `jzu_` + 40 lowercase base32 characters. */
|
|
12
|
+
export declare const JOURNEEZE_KEY_RE: RegExp;
|
|
13
|
+
/** The CI env var (contract §9); read through ai-core's `CredentialStore` (never its plaintext file). */
|
|
14
|
+
export declare const JOURNEEZE_KEY_ENV = "JOURNEEZE_UPLOAD_KEY";
|
|
15
|
+
/** Optional base URL for CI (`publish` only); pinned like `--url`. */
|
|
16
|
+
export declare const JOURNEEZE_URL_ENV = "JOURNEEZE_URL";
|
|
17
|
+
export declare const JOURNEEZE_API_PREFIX = "/api/upload/v1";
|
|
18
|
+
/** Contract §2: the only hosts an upload key is ever sent to (plus loopback, only with `JEVITATE_JOURNEEZE_DEV=1`). */
|
|
19
|
+
export declare const JOURNEEZE_PINNED_ORIGINS: readonly string[];
|
|
20
|
+
/** `JEVITATE_JOURNEEZE_DEV=1` allows a loopback Journeeze (local development of Journeeze itself). */
|
|
21
|
+
export declare const JOURNEEZE_DEV_ENV = "JEVITATE_JOURNEEZE_DEV";
|
|
22
|
+
/** A Journeeze-specific refusal. `code` is stable; the message never carries a key. */
|
|
23
|
+
export declare class JourneezeError extends Error {
|
|
24
|
+
readonly code: string;
|
|
25
|
+
constructor(code: string, message: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The Journeeze origin for a base URL, or a `JourneezeError`: HTTPS on a pinned host, or — only when
|
|
29
|
+
* `JEVITATE_JOURNEEZE_DEV=1` is set — http(s) on a loopback host (local development, contract §2;
|
|
30
|
+
* otherwise an upload key would go over plain HTTP to whatever listens on that port). No path,
|
|
31
|
+
* credentials, query or fragment.
|
|
32
|
+
*/
|
|
33
|
+
export declare function pinnedJourneezeOrigin(baseUrl: string, env?: Readonly<Record<string, string | undefined>>): string;
|
|
34
|
+
export interface JourneezeHttpRequest {
|
|
35
|
+
readonly method: "GET" | "POST";
|
|
36
|
+
readonly url: string;
|
|
37
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
38
|
+
readonly body?: Uint8Array;
|
|
39
|
+
}
|
|
40
|
+
export interface JourneezeHttpResponse {
|
|
41
|
+
readonly status: number;
|
|
42
|
+
readonly headers: {
|
|
43
|
+
get(name: string): string | null;
|
|
44
|
+
};
|
|
45
|
+
text(): Promise<string>;
|
|
46
|
+
}
|
|
47
|
+
/** The one way jevitate talks to Journeeze; tests inject a fake. Redirects are never followed. */
|
|
48
|
+
export type JourneezeHttp = (req: JourneezeHttpRequest) => Promise<JourneezeHttpResponse>;
|
|
49
|
+
export declare const fetchJourneezeHttp: JourneezeHttp;
|
|
50
|
+
/** Sends `req`, refusing any response that is a redirect (the key never follows one to another origin). */
|
|
51
|
+
export declare function sendPinned(http: JourneezeHttp, origin: string, req: JourneezeHttpRequest): Promise<JourneezeHttpResponse>;
|
|
52
|
+
/** The JSON body of a response, or `{}` when it is not JSON (unknown fields are ignored, contract §8). */
|
|
53
|
+
export declare function jsonBody(res: JourneezeHttpResponse): Promise<Record<string, unknown>>;
|
|
54
|
+
/** `code (requestId)` of an error response (contract §5), for messages. Never echoes the request. */
|
|
55
|
+
export declare function describeError(res: JourneezeHttpResponse, body: Record<string, unknown>): string;
|
|
56
|
+
/** The managers a reference can name: `env` (a variable), `cmd` (a shell command), or a password manager CLI. */
|
|
57
|
+
export type JourneezeKeyManager = "env" | "cmd" | "op" | "bw" | "pass";
|
|
58
|
+
/** What `connect` saves: where the key lives (never the key), bound to the origin it was verified on. */
|
|
59
|
+
export interface JourneezeKeyRef {
|
|
60
|
+
readonly manager: JourneezeKeyManager;
|
|
61
|
+
/** The variable name, the command, or the manager's entry (`op://…`, a `bw` item, a `pass` path). */
|
|
62
|
+
readonly key: string;
|
|
63
|
+
readonly origin: string;
|
|
64
|
+
readonly field: "journeeze-upload-key";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parses what the person typed at the reference prompt: `env:<VAR>`, `cmd:<command>`, `op://…`,
|
|
68
|
+
* `bw:<item>` or `pass:<path>`. Anything that looks like a key itself is refused (without echoing it).
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseKeyRef(text: string, origin: string): JourneezeKeyRef;
|
|
71
|
+
/** How a reference is resolved; tests inject fakes. Nothing resolved is cached or persisted. */
|
|
72
|
+
export interface KeySources {
|
|
73
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
74
|
+
/** Runs a shell command (a `cmd:` reference); resolves its stdout. */
|
|
75
|
+
readonly runCommand: (command: string) => Promise<string>;
|
|
76
|
+
/** Runs a password manager's CLI without a shell; resolves its stdout. */
|
|
77
|
+
readonly exec: (cmd: string, args: readonly string[]) => Promise<string>;
|
|
78
|
+
}
|
|
79
|
+
export declare function defaultKeySources(env?: Readonly<Record<string, string | undefined>>): KeySources;
|
|
80
|
+
/**
|
|
81
|
+
* The key a reference points to, or a `JourneezeError` that names the reference — never the source's
|
|
82
|
+
* output (a misconfigured source may print the secret on failure). The value is checked for the key
|
|
83
|
+
* format; the caller uses it only for the `Authorization` header.
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveKeyRef(ref: JourneezeKeyRef, sources: KeySources): Promise<string>;
|
|
86
|
+
/** A display form of a reference (never a value). */
|
|
87
|
+
export declare function describeRef(ref: JourneezeKeyRef): string;
|
|
88
|
+
export interface JourneezeConnection {
|
|
89
|
+
readonly baseUrl: string;
|
|
90
|
+
readonly product: {
|
|
91
|
+
readonly id: string;
|
|
92
|
+
readonly name: string;
|
|
93
|
+
};
|
|
94
|
+
readonly keyPrefix: string;
|
|
95
|
+
readonly keyRef: JourneezeKeyRef;
|
|
96
|
+
readonly connectedAt: string;
|
|
97
|
+
}
|
|
98
|
+
export interface ConnectionStoreDeps {
|
|
99
|
+
readonly homedir?: () => string;
|
|
100
|
+
}
|
|
101
|
+
export declare function connectionsPath(deps?: ConnectionStoreDeps): string;
|
|
102
|
+
/** The connection's lookup key: the project data dir (absolute), or `*` outside a project. */
|
|
103
|
+
export declare function projectKey(projectDir: string | null): string;
|
|
104
|
+
/**
|
|
105
|
+
* The saved connection for a project: ONLY that project's own (a connection made in one project, or
|
|
106
|
+
* outside any, never uploads another project's catalog). The `*` connection (made outside any
|
|
107
|
+
* project) applies only outside a project, and never inside an MCP call. A project without its own
|
|
108
|
+
* connection is not connected: a person runs `jevitate connect journeeze` in that project.
|
|
109
|
+
*/
|
|
110
|
+
export declare function loadConnection(projectDir: string | null, deps?: ConnectionStoreDeps): Promise<JourneezeConnection | undefined>;
|
|
111
|
+
/** Saves a connection (a reference, never a key) — refuses anything that looks like a key. */
|
|
112
|
+
export declare function saveConnection(projectDir: string | null, conn: JourneezeConnection, deps?: ConnectionStoreDeps): Promise<string>;
|
|
113
|
+
export interface Whoami {
|
|
114
|
+
readonly product: {
|
|
115
|
+
readonly id: string;
|
|
116
|
+
readonly name: string;
|
|
117
|
+
};
|
|
118
|
+
readonly tenant: string;
|
|
119
|
+
readonly keyPrefix: string;
|
|
120
|
+
}
|
|
121
|
+
/** Verifies a key with `GET /whoami` — the only call `connect` makes with it. */
|
|
122
|
+
export declare function whoami(http: JourneezeHttp, origin: string, key: string): Promise<Whoami>;
|
|
123
|
+
/** The terminal `connect` talks to; tests inject a scripted one. Prompts go to stderr (stdout is the result). */
|
|
124
|
+
export interface ConnectTerminal {
|
|
125
|
+
readonly isTTY: boolean;
|
|
126
|
+
/** A visible answer (the reference, the confirmation). */
|
|
127
|
+
ask(question: string): Promise<string>;
|
|
128
|
+
/** A hidden answer (the key): never echoed. */
|
|
129
|
+
askHidden(question: string): Promise<string>;
|
|
130
|
+
say(line: string): void;
|
|
131
|
+
}
|
|
132
|
+
export declare function processTerminal(): ConnectTerminal;
|
|
133
|
+
export interface ConnectDeps extends ConnectionStoreDeps {
|
|
134
|
+
readonly http?: JourneezeHttp;
|
|
135
|
+
readonly terminal?: ConnectTerminal;
|
|
136
|
+
readonly sources?: KeySources;
|
|
137
|
+
}
|
|
138
|
+
export interface ConnectOutcome {
|
|
139
|
+
readonly baseUrl: string;
|
|
140
|
+
readonly product: {
|
|
141
|
+
readonly id: string;
|
|
142
|
+
readonly name: string;
|
|
143
|
+
};
|
|
144
|
+
readonly keyPrefix: string;
|
|
145
|
+
readonly stored: boolean;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `jevitate connect journeeze`: the person names where the key is kept (or types the key, hidden, to
|
|
149
|
+
* check it first — then names where it is kept, which must hold the same key); the key is verified
|
|
150
|
+
* with `whoami`; the person confirms the product; only the reference is saved.
|
|
151
|
+
*/
|
|
152
|
+
export declare function runConnect(baseUrl: string, projectDir: string | null, deps?: ConnectDeps): Promise<ConnectOutcome>;
|
|
153
|
+
/** Re-throws `err` as a `JourneezeError` whose message has every given secret removed. */
|
|
154
|
+
export declare function scrubError(err: unknown, secrets: readonly string[], fallbackCode: string): JourneezeError;
|
|
155
|
+
/** Sleeps on jevitate's clock (tests inject their own). */
|
|
156
|
+
export declare function clockSleep(ms: number): Promise<void>;
|