@ory/argus 0.1.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 (80) hide show
  1. package/README.md +134 -0
  2. package/assets/commands/local-down.md +19 -0
  3. package/assets/commands/local-up.md +27 -0
  4. package/assets/skills/auth-setup/SKILL.md +279 -0
  5. package/assets/skills/local-dev/SKILL.md +206 -0
  6. package/assets/skills/login-flow/SKILL.md +383 -0
  7. package/assets/skills/social-login/SKILL.md +312 -0
  8. package/dist/agent-auth.d.ts +204 -0
  9. package/dist/agent-auth.js +553 -0
  10. package/dist/auth-gate.d.ts +71 -0
  11. package/dist/auth-gate.js +308 -0
  12. package/dist/auth-store.d.ts +75 -0
  13. package/dist/auth-store.js +261 -0
  14. package/dist/auth.d.ts +93 -0
  15. package/dist/auth.js +323 -0
  16. package/dist/cli.d.ts +73 -0
  17. package/dist/cli.js +484 -0
  18. package/dist/client.d.ts +158 -0
  19. package/dist/client.js +679 -0
  20. package/dist/config.d.ts +135 -0
  21. package/dist/config.js +344 -0
  22. package/dist/denial.d.ts +79 -0
  23. package/dist/denial.js +103 -0
  24. package/dist/dev.d.ts +95 -0
  25. package/dist/dev.js +514 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.js +137 -0
  28. package/dist/local/cli.d.ts +12 -0
  29. package/dist/local/cli.js +95 -0
  30. package/dist/local/configs.d.ts +89 -0
  31. package/dist/local/configs.js +634 -0
  32. package/dist/local/health.d.ts +32 -0
  33. package/dist/local/health.js +65 -0
  34. package/dist/local/index.d.ts +6 -0
  35. package/dist/local/index.js +38 -0
  36. package/dist/local/jaeger-main.d.ts +13 -0
  37. package/dist/local/jaeger-main.js +85 -0
  38. package/dist/local/jaeger.d.ts +50 -0
  39. package/dist/local/jaeger.js +162 -0
  40. package/dist/local/main.d.ts +7 -0
  41. package/dist/local/main.js +14 -0
  42. package/dist/local/manager.d.ts +45 -0
  43. package/dist/local/manager.js +676 -0
  44. package/dist/local/seed.d.ts +71 -0
  45. package/dist/local/seed.js +237 -0
  46. package/dist/logger.d.ts +29 -0
  47. package/dist/logger.js +139 -0
  48. package/dist/mcp.d.ts +76 -0
  49. package/dist/mcp.js +122 -0
  50. package/dist/otel/exporter.d.ts +17 -0
  51. package/dist/otel/exporter.js +12 -0
  52. package/dist/otel/index.d.ts +2 -0
  53. package/dist/otel/index.js +8 -0
  54. package/dist/otel/otlp-http.d.ts +116 -0
  55. package/dist/otel/otlp-http.js +322 -0
  56. package/dist/registry/cli.d.ts +12 -0
  57. package/dist/registry/cli.js +76 -0
  58. package/dist/registry/config.d.ts +23 -0
  59. package/dist/registry/config.js +80 -0
  60. package/dist/registry/index.d.ts +3 -0
  61. package/dist/registry/index.js +21 -0
  62. package/dist/registry/main.d.ts +7 -0
  63. package/dist/registry/main.js +14 -0
  64. package/dist/registry/manager.d.ts +38 -0
  65. package/dist/registry/manager.js +674 -0
  66. package/dist/setup.d.ts +118 -0
  67. package/dist/setup.js +398 -0
  68. package/dist/skills.d.ts +78 -0
  69. package/dist/skills.js +264 -0
  70. package/dist/subject.d.ts +43 -0
  71. package/dist/subject.js +55 -0
  72. package/dist/tool-metadata.d.ts +41 -0
  73. package/dist/tool-metadata.js +127 -0
  74. package/dist/tracer.d.ts +172 -0
  75. package/dist/tracer.js +452 -0
  76. package/dist/types.d.ts +57 -0
  77. package/dist/types.js +3 -0
  78. package/dist/watch-sandbox.d.ts +9 -0
  79. package/dist/watch-sandbox.js +81 -0
  80. package/package.json +79 -0
@@ -0,0 +1,674 @@
1
+ "use strict";
2
+ /**
3
+ * Local npm registry manager.
4
+ *
5
+ * Manages a Verdaccio Docker container for publishing and installing
6
+ * @ory packages locally. Completely ephemeral — the
7
+ * container is started with `--rm` and all state lives under
8
+ * `.ory-dev/registry/` on the host, bind-mounted into the container.
9
+ *
10
+ * Mirrors the lifecycle model used by the standalone Jaeger container
11
+ * (see `local/jaeger.ts`): single `docker run -d --rm --name <name>`,
12
+ * idempotent start, removal via `docker rm -f`.
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.getLocalRegistryNpmCacheDir = getLocalRegistryNpmCacheDir;
49
+ exports.ensureRegistryRunning = ensureRegistryRunning;
50
+ exports.publishHarnessToLocal = publishHarnessToLocal;
51
+ exports.registryStart = registryStart;
52
+ exports.registryStop = registryStop;
53
+ exports.registryStatus = registryStatus;
54
+ exports.registryPublish = registryPublish;
55
+ exports.registryClean = registryClean;
56
+ exports.registryNpmrc = registryNpmrc;
57
+ const fs = __importStar(require("node:fs"));
58
+ const path = __importStar(require("node:path"));
59
+ const node_child_process_1 = require("node:child_process");
60
+ const config_js_1 = require("./config.js");
61
+ function findProjectRoot(startDir) {
62
+ let dir = startDir;
63
+ while (dir !== path.dirname(dir)) {
64
+ if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml")) ||
65
+ fs.existsSync(path.join(dir, ".git"))) {
66
+ return dir;
67
+ }
68
+ dir = path.dirname(dir);
69
+ }
70
+ return startDir;
71
+ }
72
+ function getRegistryDir() {
73
+ const root = findProjectRoot(process.cwd());
74
+ return path.join(root, config_js_1.REGISTRY_DIR_NAME);
75
+ }
76
+ /**
77
+ * Per-registry npm cache directory.
78
+ *
79
+ * Isolating npm's pacote cache here is the only way to keep republishing
80
+ * the same version reliably — npm caches manifests by URL, so the user's
81
+ * `~/.npm` would otherwise serve stale "this version exists" answers to
82
+ * pnpm publish/npm install even after we drop the version from Verdaccio.
83
+ */
84
+ function getLocalRegistryNpmCacheDir() {
85
+ return path.join(getRegistryDir(), "npm-cache");
86
+ }
87
+ function ensureDir(dir) {
88
+ if (!fs.existsSync(dir)) {
89
+ fs.mkdirSync(dir, { recursive: true });
90
+ }
91
+ }
92
+ function listWorkspaceScopedPackages(workspaceRoot) {
93
+ return listWorkspaceScopedPackageJsons(workspaceRoot)
94
+ .map((file) => readPackageName(file))
95
+ .filter((n) => n !== null);
96
+ }
97
+ function listWorkspaceScopedPackageJsons(workspaceRoot) {
98
+ const packagesDir = path.join(workspaceRoot, "packages");
99
+ if (!fs.existsSync(packagesDir))
100
+ return [];
101
+ const files = [];
102
+ for (const entry of fs.readdirSync(packagesDir)) {
103
+ const pkgJson = path.join(packagesDir, entry, "package.json");
104
+ if (!fs.existsSync(pkgJson))
105
+ continue;
106
+ if (readPackageName(pkgJson)?.startsWith("@ory/")) {
107
+ files.push(pkgJson);
108
+ }
109
+ }
110
+ return files;
111
+ }
112
+ function readPackageName(pkgJsonPath) {
113
+ try {
114
+ const data = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
115
+ return data.name ?? null;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ /**
122
+ * Strip `publishConfig.registry` and `publishConfig.provenance` from every
123
+ * `@ory/*` package.json for the duration of `fn`, then restore.
124
+ *
125
+ * The OIDC trusted-publishing release workflow pins each package's
126
+ * `publishConfig.registry` to `https://registry.npmjs.org/` with
127
+ * `provenance: true`. `publishConfig` overrides both the `--registry` CLI
128
+ * flag and the `npm_config_registry` env var on `pnpm publish`, so without
129
+ * this transform `pnpm publish --registry http://localhost:4873` still
130
+ * targets real npmjs.org and fails with `E404 undefined` (no auth) /
131
+ * provenance errors. We unset the two fields only on disk for the publish
132
+ * call, then put the files back exactly as they were so the CI release
133
+ * workflow keeps working.
134
+ */
135
+ async function withLocalPublishConfig(workspaceRoot, fn) {
136
+ const files = listWorkspaceScopedPackageJsons(workspaceRoot);
137
+ const backups = new Map();
138
+ for (const file of files) {
139
+ const original = fs.readFileSync(file, "utf-8");
140
+ const pkg = JSON.parse(original);
141
+ if (!pkg.publishConfig)
142
+ continue;
143
+ const stripped = { ...pkg.publishConfig };
144
+ delete stripped.registry;
145
+ delete stripped.provenance;
146
+ backups.set(file, original);
147
+ const next = { ...pkg, publishConfig: stripped };
148
+ fs.writeFileSync(file, JSON.stringify(next, null, 2) + "\n");
149
+ }
150
+ const restore = () => {
151
+ for (const [file, original] of backups) {
152
+ try {
153
+ fs.writeFileSync(file, original);
154
+ }
155
+ catch {
156
+ // Best-effort on shutdown paths.
157
+ }
158
+ }
159
+ backups.clear();
160
+ };
161
+ const sigHandler = () => {
162
+ restore();
163
+ process.exit(130);
164
+ };
165
+ process.once("SIGINT", sigHandler);
166
+ process.once("SIGTERM", sigHandler);
167
+ try {
168
+ return await fn();
169
+ }
170
+ finally {
171
+ process.off("SIGINT", sigHandler);
172
+ process.off("SIGTERM", sigHandler);
173
+ restore();
174
+ }
175
+ }
176
+ async function cleanScopeStorage() {
177
+ // Verdaccio caches package metadata in memory; deleting on-disk storage
178
+ // alone leaves stale manifests served and causes publish to report
179
+ // "already exists". Issue an HTTP DELETE for every known scoped package
180
+ // first (Verdaccio drops both memory + disk state), then remove any
181
+ // residual scope dir for completeness.
182
+ const root = findProjectRoot(process.cwd());
183
+ const names = listWorkspaceScopedPackages(root);
184
+ await Promise.all(names.map(async (name) => {
185
+ const encoded = name.replace("/", "%2f");
186
+ try {
187
+ await fetch(`${config_js_1.REGISTRY_URL}/${encoded}/-rev/0`, { method: "DELETE" });
188
+ }
189
+ catch {
190
+ // Best-effort.
191
+ }
192
+ }));
193
+ const scopeDir = path.join(getRegistryDir(), "storage", "@ory");
194
+ if (fs.existsSync(scopeDir)) {
195
+ fs.rmSync(scopeDir, { recursive: true, force: true });
196
+ }
197
+ }
198
+ // ─── Docker plumbing ───────────────────────────────────────────────
199
+ function dockerAvailable() {
200
+ const result = (0, node_child_process_1.spawnSync)("docker", ["info"], { stdio: "ignore" });
201
+ return result.status === 0;
202
+ }
203
+ function inspectContainer(name) {
204
+ const result = (0, node_child_process_1.spawnSync)("docker", ["inspect", "--format", "{{.State.Running}}", name], { stdio: ["ignore", "pipe", "ignore"] });
205
+ if (result.status !== 0) {
206
+ return { exists: false, running: false };
207
+ }
208
+ const out = (result.stdout?.toString() ?? "").trim();
209
+ return { exists: true, running: out === "true" };
210
+ }
211
+ /**
212
+ * Build the `--user UID:GID` args matching the host user, so bind-mounted
213
+ * files written by Verdaccio are owned by the host user (otherwise
214
+ * `cleanScopeStorage`'s `fs.rmSync` would fail on Linux). No-op on
215
+ * Windows where the POSIX uid/gid accessors return undefined.
216
+ */
217
+ function hostUserArgs() {
218
+ const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
219
+ const gid = typeof process.getgid === "function" ? process.getgid() : undefined;
220
+ if (uid === undefined || gid === undefined)
221
+ return [];
222
+ return ["--user", `${uid}:${gid}`];
223
+ }
224
+ function isRegistryRunning() {
225
+ return inspectContainer(config_js_1.REGISTRY_CONTAINER).running;
226
+ }
227
+ // ─── Token storage ─────────────────────────────────────────────────
228
+ function getTokenFile(registryDir) {
229
+ return path.join(registryDir, "auth-token");
230
+ }
231
+ function readToken(registryDir) {
232
+ const tokenFile = getTokenFile(registryDir);
233
+ if (!fs.existsSync(tokenFile))
234
+ return null;
235
+ return fs.readFileSync(tokenFile, "utf-8").trim() || null;
236
+ }
237
+ async function waitForRegistry(timeoutMs = 30_000) {
238
+ const start = Date.now();
239
+ while (Date.now() - start < timeoutMs) {
240
+ try {
241
+ const res = await fetch(`${config_js_1.REGISTRY_URL}/-/ping`);
242
+ if (res.ok)
243
+ return true;
244
+ }
245
+ catch {
246
+ // Not ready yet
247
+ }
248
+ await new Promise((r) => setTimeout(r, 250));
249
+ }
250
+ return false;
251
+ }
252
+ /**
253
+ * Register a dev user with Verdaccio and return the auth token.
254
+ *
255
+ * Verdaccio 6 requires the full CouchDB-style user document — a minimal
256
+ * `{name, password}` body short-circuits with 409 before the auth plugin
257
+ * can create the user. If the user already exists (rare, only when
258
+ * htpasswd state leaked from a prior run), retry as a login.
259
+ */
260
+ async function registerDevUser(registryDir) {
261
+ const headers = { "Content-Type": "application/json" };
262
+ const userPath = "/-/user/org.couchdb.user:dev";
263
+ const createPayload = JSON.stringify({
264
+ _id: "org.couchdb.user:dev",
265
+ name: "dev",
266
+ password: "dev",
267
+ type: "user",
268
+ roles: [],
269
+ date: new Date().toISOString(),
270
+ });
271
+ let res = await fetch(`${config_js_1.REGISTRY_URL}${userPath}`, {
272
+ method: "PUT",
273
+ headers,
274
+ body: createPayload,
275
+ });
276
+ if (res.status === 409) {
277
+ res = await fetch(`${config_js_1.REGISTRY_URL}${userPath}`, {
278
+ method: "PUT",
279
+ headers,
280
+ body: JSON.stringify({ name: "dev", password: "dev" }),
281
+ });
282
+ }
283
+ // 201 = created, 200 = logged in
284
+ if (!res.ok) {
285
+ throw new Error(`Failed to register dev user: HTTP ${res.status}`);
286
+ }
287
+ const body = (await res.json());
288
+ const token = body.token;
289
+ if (!token) {
290
+ throw new Error("Verdaccio did not return an auth token.");
291
+ }
292
+ fs.writeFileSync(getTokenFile(registryDir), token);
293
+ return token;
294
+ }
295
+ // ─── Public lifecycle ──────────────────────────────────────────────
296
+ /**
297
+ * Start the local registry if it isn't already running.
298
+ */
299
+ async function ensureRegistryRunning() {
300
+ if (isRegistryRunning())
301
+ return;
302
+ await registryStart();
303
+ }
304
+ /**
305
+ * Env vars stripped before invoking the per-package publish, mirroring the
306
+ * defensive `env -u` list in the root `publish-packages` script that the
307
+ * release workflow runs. Keeping the two lists identical means a local
308
+ * publish sees the same minimal npm config the CI publish does, instead of
309
+ * silently inheriting whatever the developer has set globally.
310
+ */
311
+ const STRIPPED_NPM_CONFIG_ENV = [
312
+ "npm_config_globalconfig",
313
+ "npm_config_npm_globalconfig",
314
+ "npm_config_dir",
315
+ "npm_config_workspace_root",
316
+ "npm_config_verify_deps_before_run",
317
+ "npm_config_minimum_release_age",
318
+ "npm_config__jsr_registry",
319
+ ];
320
+ function publishEnv(extra) {
321
+ const env = { ...process.env };
322
+ for (const key of STRIPPED_NPM_CONFIG_ENV) {
323
+ delete env[key];
324
+ }
325
+ return { ...env, ...extra };
326
+ }
327
+ /**
328
+ * Publish all `@ory/*` workspace packages to the local registry.
329
+ *
330
+ * Intent: mirror the production release path (`pnpm publish-packages` →
331
+ * `changeset publish`) as closely as a local Verdaccio target allows. The
332
+ * release workflow runs `pnpm build` first and strips a fixed set of npm
333
+ * config env vars; we do the same here. The remaining divergences are
334
+ * forced by the local target:
335
+ *
336
+ * - `publishConfig.registry`/`provenance` is stripped from each
337
+ * package.json for the duration of the call so writes land on
338
+ * localhost instead of npmjs.org. See `withLocalPublishConfig`.
339
+ * - Auth is a Verdaccio `_authToken` rather than OIDC trusted
340
+ * publishing.
341
+ * - The publish primitive is a two-step `pnpm pack` + `npm publish
342
+ * <tarball>`, rather than the single `pnpm publish` prod uses. Neither
343
+ * tool alone covers both required behaviors against Verdaccio:
344
+ * * `pnpm publish` rewrites `workspace:*` dep specifiers to a
345
+ * concrete version when packing (npm publish does not — it would
346
+ * upload `workspace:*` literally and the sandbox install would
347
+ * then fail with `Unsupported URL Type "workspace:"`).
348
+ * * `npm publish` populates the `readme` field in the manifest
349
+ * payload (pnpm publish does not — npmjs.org extracts README
350
+ * server-side from the tarball, but Verdaccio doesn't, so its UI
351
+ * shows "No README data found!").
352
+ * `pnpm pack` does the workspace rewrite while producing a tarball;
353
+ * `npm publish` of that tarball populates the readme field.
354
+ * - We force a republish at the same version (Verdaccio scope is wiped
355
+ * first); changeset's "skip if already published" check is bypassed.
356
+ *
357
+ * @param opts.quiet Suppress per-package output and only surface the
358
+ * captured streams on failure. Used by the dev
359
+ * launcher to keep its console clean.
360
+ */
361
+ async function publishAllOryToLocal(opts = {}) {
362
+ if (!isRegistryRunning()) {
363
+ throw new Error("Local registry is not running. Call ensureRegistryRunning() first.");
364
+ }
365
+ const registryDir = getRegistryDir();
366
+ const token = readToken(registryDir);
367
+ if (!token) {
368
+ throw new Error("No registry auth token found. Restart the registry: pnpm registry:clean && pnpm registry:start");
369
+ }
370
+ await cleanScopeStorage();
371
+ const cacheDir = getLocalRegistryNpmCacheDir();
372
+ if (fs.existsSync(cacheDir)) {
373
+ fs.rmSync(cacheDir, { recursive: true, force: true });
374
+ }
375
+ const packDir = path.join(registryDir, "pack");
376
+ if (fs.existsSync(packDir)) {
377
+ fs.rmSync(packDir, { recursive: true, force: true });
378
+ }
379
+ fs.mkdirSync(packDir, { recursive: true });
380
+ const root = findProjectRoot(process.cwd());
381
+ const pkgJsons = listWorkspaceScopedPackageJsons(root);
382
+ await withLocalPublishConfig(root, () => {
383
+ for (const pkgJsonPath of pkgJsons) {
384
+ const pkgDir = path.dirname(pkgJsonPath);
385
+ const { name, version } = readPackageNameVersion(pkgJsonPath);
386
+ const pkgName = name ?? path.basename(pkgDir);
387
+ if (!opts.quiet) {
388
+ console.log(`Publishing ${pkgName}...`);
389
+ }
390
+ const packResult = (0, node_child_process_1.spawnSync)("pnpm", ["pack", "--pack-destination", packDir], {
391
+ cwd: pkgDir,
392
+ stdio: opts.quiet ? "pipe" : "inherit",
393
+ encoding: "utf-8",
394
+ env: publishEnv({}),
395
+ });
396
+ if (packResult.status !== 0) {
397
+ emitCapturedOutput(opts.quiet, "pnpm pack", packResult);
398
+ throw new Error(`Failed to pack ${pkgName} for local registry (exit ${packResult.status}).`);
399
+ }
400
+ const tarball = path.join(packDir, tarballFilename(pkgName, version));
401
+ if (!fs.existsSync(tarball)) {
402
+ throw new Error(`Packed ${pkgName} but expected tarball not found at ${tarball}.`);
403
+ }
404
+ const publishResult = (0, node_child_process_1.spawnSync)("npm", ["publish", tarball, "--registry", config_js_1.REGISTRY_URL, "--access=public"], {
405
+ cwd: pkgDir,
406
+ stdio: opts.quiet ? "pipe" : "inherit",
407
+ encoding: "utf-8",
408
+ env: publishEnv({
409
+ npm_config_registry: config_js_1.REGISTRY_URL,
410
+ npm_config_cache: cacheDir,
411
+ npm_config_loglevel: "error",
412
+ // Per-registry auth token. Verdaccio's `publish: $all` allows
413
+ // anonymous writes, but npm itself refuses to publish without
414
+ // local credentials.
415
+ [`npm_config_//${stripScheme(config_js_1.REGISTRY_URL)}/:_authToken`]: token,
416
+ }),
417
+ });
418
+ if (publishResult.status !== 0) {
419
+ emitCapturedOutput(opts.quiet, "npm publish", publishResult);
420
+ throw new Error(`Failed to publish ${pkgName} to local registry (exit ${publishResult.status}).`);
421
+ }
422
+ }
423
+ });
424
+ }
425
+ function readPackageNameVersion(pkgJsonPath) {
426
+ try {
427
+ const data = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
428
+ return { name: data.name ?? null, version: data.version ?? "0.0.0" };
429
+ }
430
+ catch {
431
+ return { name: null, version: "0.0.0" };
432
+ }
433
+ }
434
+ function tarballFilename(pkgName, version) {
435
+ return `${pkgName.replace(/^@/, "").replace("/", "-")}-${version}.tgz`;
436
+ }
437
+ function emitCapturedOutput(quiet, label, result) {
438
+ if (!quiet)
439
+ return;
440
+ const stdout = (result.stdout ?? "").toString().trim();
441
+ const stderr = (result.stderr ?? "").toString().trim();
442
+ if (!stdout && !stderr)
443
+ return;
444
+ process.stderr.write(`\n── ${label} output ──\n`);
445
+ if (stdout)
446
+ process.stderr.write(stdout + "\n");
447
+ if (stderr)
448
+ process.stderr.write(stderr + "\n");
449
+ process.stderr.write(`── end ${label} output ──\n\n`);
450
+ }
451
+ function stripScheme(url) {
452
+ return url.replace(/^https?:\/\//, "");
453
+ }
454
+ /**
455
+ * Publish all `@ory/*` packages to the local registry — entrypoint used by
456
+ * the dev launcher. The `harnessName` arg is retained for call-site
457
+ * compatibility; we publish the whole scope every time so any harness gets
458
+ * a coherent registry state without needing to compute its dep closure.
459
+ */
460
+ async function publishHarnessToLocal(_harnessName) {
461
+ await publishAllOryToLocal({ quiet: true });
462
+ }
463
+ // ─── Commands ──────────────────────────────────────────────────────
464
+ async function registryStart() {
465
+ const registryDir = getRegistryDir();
466
+ const storageDir = path.join(registryDir, "storage");
467
+ if (isRegistryRunning()) {
468
+ console.log(`Local registry is already running (container ${config_js_1.REGISTRY_CONTAINER}).`);
469
+ console.log(` URL: ${config_js_1.REGISTRY_URL}`);
470
+ return;
471
+ }
472
+ if (!dockerAvailable()) {
473
+ console.error("Error: Docker is not running. Start Docker Desktop (or the Docker daemon) and try again.");
474
+ process.exit(1);
475
+ }
476
+ console.log("Starting local npm registry (Verdaccio in Docker)...");
477
+ console.log(` Image: ${config_js_1.REGISTRY_IMAGE}`);
478
+ console.log(` Container: ${config_js_1.REGISTRY_CONTAINER}`);
479
+ console.log(` Directory: ${registryDir}`);
480
+ console.log(` URL: ${config_js_1.REGISTRY_URL}`);
481
+ console.log("");
482
+ // Write config files on the host. Both are bind-mounted into the container.
483
+ ensureDir(registryDir);
484
+ ensureDir(storageDir);
485
+ // Wipe the persisted htpasswd + Verdaccio metadata DB on every fresh start.
486
+ // Without this, a stale `dev` entry from a previous run causes the new
487
+ // container's user registration to short-circuit with 409 ("user already
488
+ // exists") before reaching the htpasswd auth plugin, which then never
489
+ // hands back a usable token. The registry is ephemeral by design (clean
490
+ // wipes everything anyway), so dropping the htpasswd here is safe.
491
+ for (const stale of ["htpasswd", ".verdaccio-db.json"]) {
492
+ const p = path.join(storageDir, stale);
493
+ if (fs.existsSync(p))
494
+ fs.rmSync(p, { force: true });
495
+ }
496
+ const configPath = path.join(registryDir, "config.yaml");
497
+ fs.writeFileSync(configPath, (0, config_js_1.verdaccioConfigYaml)());
498
+ // .npmrc is consumed by host-side npm/pnpm tooling, not by the container.
499
+ fs.writeFileSync(path.join(registryDir, ".npmrc"), (0, config_js_1.npmrcContent)());
500
+ // Stale --rm container could only exist if Docker died mid-run; clean it up
501
+ // so the new `docker run --rm` doesn't collide on the container name.
502
+ const state = inspectContainer(config_js_1.REGISTRY_CONTAINER);
503
+ if (state.exists && !state.running) {
504
+ (0, node_child_process_1.spawnSync)("docker", ["rm", "-f", config_js_1.REGISTRY_CONTAINER], { stdio: "ignore" });
505
+ }
506
+ const runArgs = [
507
+ "run",
508
+ "-d",
509
+ "--rm",
510
+ "--name",
511
+ config_js_1.REGISTRY_CONTAINER,
512
+ "-p",
513
+ `${config_js_1.REGISTRY_PORT}:${config_js_1.REGISTRY_PORT}`,
514
+ ...hostUserArgs(),
515
+ "-v",
516
+ `${storageDir}:${config_js_1.REGISTRY_CONTAINER_STORAGE}`,
517
+ "-v",
518
+ `${configPath}:${config_js_1.REGISTRY_CONTAINER_CONFIG}:ro`,
519
+ config_js_1.REGISTRY_IMAGE,
520
+ ];
521
+ const run = (0, node_child_process_1.spawnSync)("docker", runArgs, {
522
+ stdio: ["ignore", "ignore", "pipe"],
523
+ encoding: "utf-8",
524
+ });
525
+ if (run.status !== 0) {
526
+ const stderr = (run.stderr ?? "").toString().trim();
527
+ console.error(`Error: 'docker run ${config_js_1.REGISTRY_IMAGE}' failed (exit ${run.status}).`);
528
+ if (stderr)
529
+ console.error(stderr);
530
+ process.exit(1);
531
+ }
532
+ // Wait for it to become ready (Verdaccio startup + image pull on first run
533
+ // can take longer than a native process — bump the default timeout).
534
+ console.log(" Waiting for registry to become ready...");
535
+ const ready = await waitForRegistry();
536
+ if (!ready) {
537
+ console.error("Error: Registry did not start within 30 seconds.");
538
+ console.error(` Inspect container logs: docker logs ${config_js_1.REGISTRY_CONTAINER}`);
539
+ process.exit(1);
540
+ }
541
+ // Register a dev user for publish auth
542
+ console.log(" Registering dev user for publish access...");
543
+ try {
544
+ await registerDevUser(registryDir);
545
+ }
546
+ catch (err) {
547
+ const msg = err instanceof Error ? err.message : String(err);
548
+ console.error(` Warning: Could not register dev user: ${msg}`);
549
+ console.error(" Publishing may fail. You can retry with 'registry:start'.");
550
+ }
551
+ console.log(` Registry is running (container ${config_js_1.REGISTRY_CONTAINER}).`);
552
+ console.log("");
553
+ console.log("To publish all packages:");
554
+ console.log(" pnpm registry:publish");
555
+ console.log("");
556
+ console.log("To install from this registry:");
557
+ console.log(` npm install --registry ${config_js_1.REGISTRY_URL} @ory/<package>`);
558
+ }
559
+ async function registryStop() {
560
+ if (!dockerAvailable()) {
561
+ console.log("Local registry is not running (Docker is unavailable, so no container could be active).");
562
+ return;
563
+ }
564
+ const state = inspectContainer(config_js_1.REGISTRY_CONTAINER);
565
+ if (!state.exists) {
566
+ console.log("Local registry is not running.");
567
+ return;
568
+ }
569
+ console.log(`Stopping local registry (container ${config_js_1.REGISTRY_CONTAINER})...`);
570
+ const rm = (0, node_child_process_1.spawnSync)("docker", ["rm", "-f", config_js_1.REGISTRY_CONTAINER], {
571
+ stdio: ["ignore", "ignore", "pipe"],
572
+ encoding: "utf-8",
573
+ });
574
+ if (rm.status !== 0) {
575
+ const stderr = (rm.stderr ?? "").toString().trim();
576
+ console.error(`Warning: 'docker rm -f' exited ${rm.status}.`);
577
+ if (stderr)
578
+ console.error(stderr);
579
+ return;
580
+ }
581
+ console.log("Registry stopped.");
582
+ }
583
+ async function registryStatus() {
584
+ const registryDir = getRegistryDir();
585
+ console.log("Local npm Registry Status");
586
+ console.log("=========================");
587
+ console.log("");
588
+ console.log(` Directory: ${registryDir}`);
589
+ console.log(` URL: ${config_js_1.REGISTRY_URL}`);
590
+ console.log(` Container: ${config_js_1.REGISTRY_CONTAINER} (${config_js_1.REGISTRY_IMAGE})`);
591
+ console.log("");
592
+ if (!dockerAvailable()) {
593
+ console.log(" Status: UNKNOWN (Docker is not running)");
594
+ console.log(" Start Docker Desktop and re-run 'registry:status'.");
595
+ return;
596
+ }
597
+ if (!fs.existsSync(registryDir)) {
598
+ console.log(" Status: NOT INITIALIZED");
599
+ console.log(" Run 'registry:start' to start the local registry.");
600
+ return;
601
+ }
602
+ const state = inspectContainer(config_js_1.REGISTRY_CONTAINER);
603
+ if (!state.exists || !state.running) {
604
+ console.log(" Status: STOPPED");
605
+ console.log(" Run 'registry:start' to start the local registry.");
606
+ return;
607
+ }
608
+ console.log(` Status: RUNNING`);
609
+ // Check if it responds to pings
610
+ try {
611
+ const res = await fetch(`${config_js_1.REGISTRY_URL}/-/ping`);
612
+ if (res.ok) {
613
+ console.log(" Health: OK");
614
+ }
615
+ else {
616
+ console.log(` Health: UNHEALTHY (HTTP ${res.status})`);
617
+ }
618
+ }
619
+ catch {
620
+ console.log(" Health: UNREACHABLE");
621
+ }
622
+ // List published packages
623
+ const storageDir = path.join(registryDir, "storage");
624
+ if (fs.existsSync(storageDir)) {
625
+ const scopeDir = path.join(storageDir, "@ory");
626
+ if (fs.existsSync(scopeDir)) {
627
+ const packages = fs.readdirSync(scopeDir).filter((f) => {
628
+ return fs.statSync(path.join(scopeDir, f)).isDirectory();
629
+ });
630
+ if (packages.length > 0) {
631
+ console.log("");
632
+ console.log(" Published packages:");
633
+ for (const pkg of packages) {
634
+ console.log(` @ory/${pkg}`);
635
+ }
636
+ }
637
+ }
638
+ }
639
+ }
640
+ async function registryPublish() {
641
+ console.log("");
642
+ console.log("Publishing all @ory/* packages to local registry...");
643
+ console.log("");
644
+ try {
645
+ await publishAllOryToLocal();
646
+ }
647
+ catch (err) {
648
+ const msg = err instanceof Error ? err.message : String(err);
649
+ console.error(`Error: ${msg}`);
650
+ process.exit(1);
651
+ }
652
+ console.log("");
653
+ console.log("Published.");
654
+ }
655
+ async function registryClean() {
656
+ // Stop first if running
657
+ await registryStop();
658
+ const registryDir = getRegistryDir();
659
+ if (!fs.existsSync(registryDir)) {
660
+ console.log("No local registry data found. Nothing to clean.");
661
+ return;
662
+ }
663
+ console.log(`Removing ${registryDir}...`);
664
+ fs.rmSync(registryDir, { recursive: true, force: true });
665
+ console.log("Local registry cleaned. All published packages removed.");
666
+ }
667
+ async function registryNpmrc() {
668
+ console.log("# Add this to your .npmrc or pass --registry to npm/pnpm:");
669
+ console.log(`registry=${config_js_1.REGISTRY_URL}/`);
670
+ console.log("");
671
+ console.log("# Or use the --registry flag:");
672
+ console.log(`# npm install --registry ${config_js_1.REGISTRY_URL} @ory/<package>`);
673
+ console.log(`# pnpm add --registry ${config_js_1.REGISTRY_URL} @ory/<package>`);
674
+ }