@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.
- package/README.md +134 -0
- package/assets/commands/local-down.md +19 -0
- package/assets/commands/local-up.md +27 -0
- package/assets/skills/auth-setup/SKILL.md +279 -0
- package/assets/skills/local-dev/SKILL.md +206 -0
- package/assets/skills/login-flow/SKILL.md +383 -0
- package/assets/skills/social-login/SKILL.md +312 -0
- package/dist/agent-auth.d.ts +204 -0
- package/dist/agent-auth.js +553 -0
- package/dist/auth-gate.d.ts +71 -0
- package/dist/auth-gate.js +308 -0
- package/dist/auth-store.d.ts +75 -0
- package/dist/auth-store.js +261 -0
- package/dist/auth.d.ts +93 -0
- package/dist/auth.js +323 -0
- package/dist/cli.d.ts +73 -0
- package/dist/cli.js +484 -0
- package/dist/client.d.ts +158 -0
- package/dist/client.js +679 -0
- package/dist/config.d.ts +135 -0
- package/dist/config.js +344 -0
- package/dist/denial.d.ts +79 -0
- package/dist/denial.js +103 -0
- package/dist/dev.d.ts +95 -0
- package/dist/dev.js +514 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +137 -0
- package/dist/local/cli.d.ts +12 -0
- package/dist/local/cli.js +95 -0
- package/dist/local/configs.d.ts +89 -0
- package/dist/local/configs.js +634 -0
- package/dist/local/health.d.ts +32 -0
- package/dist/local/health.js +65 -0
- package/dist/local/index.d.ts +6 -0
- package/dist/local/index.js +38 -0
- package/dist/local/jaeger-main.d.ts +13 -0
- package/dist/local/jaeger-main.js +85 -0
- package/dist/local/jaeger.d.ts +50 -0
- package/dist/local/jaeger.js +162 -0
- package/dist/local/main.d.ts +7 -0
- package/dist/local/main.js +14 -0
- package/dist/local/manager.d.ts +45 -0
- package/dist/local/manager.js +676 -0
- package/dist/local/seed.d.ts +71 -0
- package/dist/local/seed.js +237 -0
- package/dist/logger.d.ts +29 -0
- package/dist/logger.js +139 -0
- package/dist/mcp.d.ts +76 -0
- package/dist/mcp.js +122 -0
- package/dist/otel/exporter.d.ts +17 -0
- package/dist/otel/exporter.js +12 -0
- package/dist/otel/index.d.ts +2 -0
- package/dist/otel/index.js +8 -0
- package/dist/otel/otlp-http.d.ts +116 -0
- package/dist/otel/otlp-http.js +322 -0
- package/dist/registry/cli.d.ts +12 -0
- package/dist/registry/cli.js +76 -0
- package/dist/registry/config.d.ts +23 -0
- package/dist/registry/config.js +80 -0
- package/dist/registry/index.d.ts +3 -0
- package/dist/registry/index.js +21 -0
- package/dist/registry/main.d.ts +7 -0
- package/dist/registry/main.js +14 -0
- package/dist/registry/manager.d.ts +38 -0
- package/dist/registry/manager.js +674 -0
- package/dist/setup.d.ts +118 -0
- package/dist/setup.js +398 -0
- package/dist/skills.d.ts +78 -0
- package/dist/skills.js +264 -0
- package/dist/subject.d.ts +43 -0
- package/dist/subject.js +55 -0
- package/dist/tool-metadata.d.ts +41 -0
- package/dist/tool-metadata.js +127 -0
- package/dist/tracer.d.ts +172 -0
- package/dist/tracer.js +452 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.js +3 -0
- package/dist/watch-sandbox.d.ts +9 -0
- package/dist/watch-sandbox.js +81 -0
- 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
|
+
}
|