@walkeros/cli 4.9.0 → 4.10.0-next-1794522704623

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/index.js CHANGED
@@ -241,6 +241,52 @@ async function resolveLocalPackage(packageName, localPath, configDir, logger) {
241
241
  `Local package path not found: ${localPath} (resolved to ${absolutePath})`
242
242
  );
243
243
  }
244
+ var ROOT_COPY_EXCLUDED = [".turbo", ".git"];
245
+ function excludedFromCopy(root, file) {
246
+ const relative2 = path4.relative(root, file);
247
+ if (relative2 === "") return false;
248
+ const parts = relative2.split(path4.sep);
249
+ if (ROOT_COPY_EXCLUDED.includes(parts[0])) return true;
250
+ if (parts.includes("node_modules")) return true;
251
+ const name = parts[parts.length - 1];
252
+ return name === ".env" || name.startsWith(".env.");
253
+ }
254
+ async function copyPackageRoot(source, target) {
255
+ await fs3.copy(source, target, {
256
+ dereference: true,
257
+ filter: (file) => !excludedFromCopy(source, file)
258
+ });
259
+ }
260
+ function stringLeaves(value, into = []) {
261
+ if (typeof value === "string") into.push(value);
262
+ else if (value && typeof value === "object")
263
+ for (const inner of Object.values(value)) stringLeaves(inner, into);
264
+ return into;
265
+ }
266
+ var TYPE_CONDITIONS = ["types", "typings"];
267
+ function codeExports(value, into = []) {
268
+ if (typeof value === "string") {
269
+ if (!/\.d\.[cm]?ts$/.test(value) && !value.endsWith(".json"))
270
+ into.push(value);
271
+ } else if (value && typeof value === "object") {
272
+ for (const [key, inner] of Object.entries(value))
273
+ if (!TYPE_CONDITIONS.includes(key)) codeExports(inner, into);
274
+ }
275
+ return into;
276
+ }
277
+ function entriesOutsideDist(manifest) {
278
+ if (!manifest || typeof manifest !== "object") return false;
279
+ const fields = [
280
+ ...["main", "module", "bin"].flatMap(
281
+ (field) => stringLeaves(Reflect.get(manifest, field))
282
+ ),
283
+ ...codeExports(Reflect.get(manifest, "exports"))
284
+ ];
285
+ return fields.some((entry) => {
286
+ const relative2 = path4.posix.normalize(entry.replace(/^\.\//, ""));
287
+ return relative2 !== "package.json" && relative2 !== "dist" && !relative2.startsWith("dist/");
288
+ });
289
+ }
244
290
  async function copyLocalPackage(localPkg, targetDir, logger) {
245
291
  const packageDir = path4.join(targetDir, "node_modules", localPkg.name);
246
292
  if (localPkg.type === "file") {
@@ -258,15 +304,7 @@ async function copyLocalPackage(localPkg, targetDir, logger) {
258
304
  }
259
305
  if (localPkg.type === "directory") {
260
306
  await fs3.ensureDir(path4.dirname(packageDir));
261
- const entries = await fs3.readdir(localPkg.absolutePath);
262
- for (const entry of entries) {
263
- if (!["node_modules", ".turbo", ".git"].includes(entry)) {
264
- await fs3.copy(
265
- path4.join(localPkg.absolutePath, entry),
266
- path4.join(packageDir, entry)
267
- );
268
- }
269
- }
307
+ await copyPackageRoot(localPkg.absolutePath, packageDir);
270
308
  await fs3.writeJson(path4.join(packageDir, "package.json"), {
271
309
  name: localPkg.name,
272
310
  main: "./index.ts"
@@ -281,18 +319,11 @@ async function copyLocalPackage(localPkg, targetDir, logger) {
281
319
  path4.join(localPkg.absolutePath, "package.json"),
282
320
  path4.join(packageDir, "package.json")
283
321
  );
284
- if (localPkg.hasDistFolder) {
322
+ const manifest = await fs3.readJson(path4.join(localPkg.absolutePath, "package.json")).catch(() => void 0);
323
+ if (localPkg.hasDistFolder && !entriesOutsideDist(manifest)) {
285
324
  await fs3.copy(localPkg.distPath, path4.join(packageDir, "dist"));
286
325
  } else {
287
- const entries = await fs3.readdir(localPkg.absolutePath);
288
- for (const entry of entries) {
289
- if (!["node_modules", ".turbo", ".git"].includes(entry)) {
290
- await fs3.copy(
291
- path4.join(localPkg.absolutePath, entry),
292
- path4.join(packageDir, entry)
293
- );
294
- }
295
- }
326
+ await copyPackageRoot(localPkg.absolutePath, packageDir);
296
327
  }
297
328
  logger.info(`\u{1F4E6} Using local: ${localPkg.name} from ${localPkg.absolutePath}`);
298
329
  return packageDir;
@@ -350,11 +381,523 @@ async function readStdinToTempFile(label) {
350
381
  return tmpPath;
351
382
  }
352
383
 
384
+ // src/lib/credentials.ts
385
+ import {
386
+ closeSync as closeSync2,
387
+ openSync as openSync2,
388
+ readFileSync as readFileSync2,
389
+ readdirSync,
390
+ statSync as statSync2,
391
+ unlinkSync as unlinkSync2,
392
+ writeSync as writeSync2
393
+ } from "fs";
394
+ import { randomBytes as randomBytes2 } from "crypto";
395
+ import { join as join2 } from "path";
396
+ import { z } from "zod";
397
+
398
+ // src/lib/config-file.ts
399
+ import {
400
+ readFileSync,
401
+ mkdirSync,
402
+ unlinkSync,
403
+ renameSync,
404
+ openSync,
405
+ writeSync,
406
+ fsyncSync,
407
+ closeSync,
408
+ statSync
409
+ } from "fs";
410
+ import { randomBytes } from "crypto";
411
+ import { join } from "path";
412
+ import { homedir } from "os";
413
+ function getConfigDir() {
414
+ const xdgConfig = process.env.XDG_CONFIG_HOME;
415
+ const base = xdgConfig || join(homedir(), ".config");
416
+ return join(base, "walkeros");
417
+ }
418
+ function getConfigPath() {
419
+ return join(getConfigDir(), "config.json");
420
+ }
421
+ function readConfig() {
422
+ const configPath = getConfigPath();
423
+ try {
424
+ const content = readFileSync(configPath, "utf-8");
425
+ return JSON.parse(content);
426
+ } catch {
427
+ return null;
428
+ }
429
+ }
430
+ function ensureConfigDir() {
431
+ const dir = getConfigDir();
432
+ mkdirSync(dir, { recursive: true, mode: 448 });
433
+ return dir;
434
+ }
435
+ function writePrivateFile(path23, content) {
436
+ const tempPath = `${path23}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
437
+ try {
438
+ const fd = openSync(tempPath, "wx", 384);
439
+ try {
440
+ writeSync(fd, content);
441
+ fsyncSync(fd);
442
+ } finally {
443
+ closeSync(fd);
444
+ }
445
+ renameSync(tempPath, path23);
446
+ } catch (error) {
447
+ try {
448
+ unlinkSync(tempPath);
449
+ } catch {
450
+ }
451
+ throw error;
452
+ }
453
+ }
454
+ function replaceConfigFile(config) {
455
+ ensureConfigDir();
456
+ writePrivateFile(getConfigPath(), JSON.stringify(config, null, 2));
457
+ }
458
+ function writeConfig(config) {
459
+ replaceConfigFile({ ...readConfig() ?? {}, ...config });
460
+ }
461
+ function writeTelemetryOnlyConfig(partial) {
462
+ writeConfig(partial);
463
+ }
464
+ function resolveAppUrl() {
465
+ const envUrl = process.env.WALKEROS_APP_URL;
466
+ if (envUrl) return envUrl;
467
+ const config = readConfig();
468
+ if (config?.appUrl) return config.appUrl;
469
+ return "https://app.walkeros.io";
470
+ }
471
+
472
+ // src/lib/credentials.ts
473
+ var LoginSchema = z.object({
474
+ issuer: z.string(),
475
+ client_id: z.string(),
476
+ token_endpoint: z.string(),
477
+ revocation_endpoint: z.string(),
478
+ access_token: z.string(),
479
+ /** Epoch milliseconds. */
480
+ expires_at: z.number(),
481
+ refresh_token: z.string().optional(),
482
+ scope: z.string().optional()
483
+ });
484
+ var CredentialsSchema = z.record(z.string(), z.unknown());
485
+ function getCredentialsPath() {
486
+ return join2(getConfigDir(), "credentials.json");
487
+ }
488
+ function getLockPath() {
489
+ return join2(getConfigDir(), "credentials.lock");
490
+ }
491
+ function readCredentials() {
492
+ let raw;
493
+ try {
494
+ raw = JSON.parse(readFileSync2(getCredentialsPath(), "utf-8"));
495
+ } catch {
496
+ return {};
497
+ }
498
+ const parsed = CredentialsSchema.safeParse(raw);
499
+ if (!parsed.success) return {};
500
+ const logins = {};
501
+ for (const [issuer, value] of Object.entries(parsed.data)) {
502
+ const login = LoginSchema.safeParse(value);
503
+ if (login.success && login.data.issuer === issuer)
504
+ logins[issuer] = login.data;
505
+ }
506
+ return logins;
507
+ }
508
+ function writeCredentials(logins) {
509
+ ensureConfigDir();
510
+ writePrivateFile(getCredentialsPath(), JSON.stringify(logins, null, 2));
511
+ }
512
+ function originOf(url) {
513
+ try {
514
+ return new URL(url).origin;
515
+ } catch {
516
+ return null;
517
+ }
518
+ }
519
+ function loginForAppUrl(logins, appUrl) {
520
+ const origin = originOf(appUrl);
521
+ if (origin === null) return void 0;
522
+ const exact = logins[appUrl.replace(/\/+$/, "")];
523
+ if (exact) return exact;
524
+ return Object.values(logins).find(
525
+ (login) => originOf(login.issuer) === origin
526
+ );
527
+ }
528
+ var CredentialsBusyError = class extends Error {
529
+ };
530
+ var LOCK_TIMING = {
531
+ waitMs: 15e3,
532
+ staleMs: 3e4,
533
+ pollMs: 100
534
+ };
535
+ var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
536
+ function isErrnoCode(error, code) {
537
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
538
+ }
539
+ function tryCreateLock(path23, mark) {
540
+ let fd;
541
+ try {
542
+ fd = openSync2(path23, "wx", 384);
543
+ } catch (error) {
544
+ if (isErrnoCode(error, "EEXIST")) return false;
545
+ throw error;
546
+ }
547
+ try {
548
+ writeSync2(fd, mark);
549
+ } finally {
550
+ closeSync2(fd);
551
+ }
552
+ return true;
553
+ }
554
+ function readLock(path23) {
555
+ try {
556
+ const ageMs = Date.now() - statSync2(path23).mtimeMs;
557
+ return { mark: readFileSync2(path23, "utf-8"), ageMs };
558
+ } catch {
559
+ return null;
560
+ }
561
+ }
562
+ async function withCredentialsLock(fn, timing = LOCK_TIMING) {
563
+ const dir = ensureConfigDir();
564
+ const path23 = getLockPath();
565
+ const mark = `${process.pid} ${(/* @__PURE__ */ new Date()).toISOString()} ${randomBytes2(8).toString("hex")}`;
566
+ const deadline = Date.now() + timing.waitMs;
567
+ for (; ; ) {
568
+ if (tryCreateLock(path23, mark)) break;
569
+ const held = readLock(path23);
570
+ if (held && held.ageMs > timing.staleMs) {
571
+ const again = readLock(path23);
572
+ if (again && again.mark === held.mark) {
573
+ try {
574
+ unlinkSync2(path23);
575
+ } catch {
576
+ }
577
+ }
578
+ continue;
579
+ }
580
+ if (Date.now() >= deadline)
581
+ throw new CredentialsBusyError(
582
+ `another walkeros command is updating the sign-in in ${dir}: try again`
583
+ );
584
+ await sleep(timing.pollMs);
585
+ }
586
+ try {
587
+ return await fn();
588
+ } finally {
589
+ const held = readLock(path23);
590
+ if (held && held.mark === mark) {
591
+ try {
592
+ unlinkSync2(path23);
593
+ } catch {
594
+ }
595
+ }
596
+ }
597
+ }
598
+ var PENDING_TTL_MS = 15 * 60 * 1e3;
599
+ var STATE_SHAPE = /^[A-Za-z0-9_-]{1,128}$/;
600
+ var PendingSchema = z.object({
601
+ code_verifier: z.string(),
602
+ state: z.string().regex(STATE_SHAPE),
603
+ redirect_uri: z.string(),
604
+ issuer: z.string(),
605
+ client_id: z.string(),
606
+ token_endpoint: z.string(),
607
+ /** Epoch milliseconds. */
608
+ created_at: z.number()
609
+ });
610
+
611
+ // src/core/oauth-login.ts
612
+ import { z as z3 } from "zod";
613
+
614
+ // src/core/app-fetch.ts
615
+ import { z as z2 } from "zod";
616
+
617
+ // src/lib/secure-url.ts
618
+ function isLoopback(hostname) {
619
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
620
+ }
621
+ function requireSecureUrl(url) {
622
+ let parsed;
623
+ try {
624
+ parsed = new URL(url);
625
+ } catch {
626
+ return url;
627
+ }
628
+ if (parsed.protocol !== "http:" || isLoopback(parsed.hostname)) return url;
629
+ throw new Error(
630
+ `Refusing to send walkerOS credentials over plain http to ${url}. Use https, or localhost / 127.0.0.1 / [::1] for local development.`
631
+ );
632
+ }
633
+ function isSecureSignInUrl(url) {
634
+ let parsed;
635
+ try {
636
+ parsed = new URL(url);
637
+ } catch {
638
+ return false;
639
+ }
640
+ if (parsed.protocol === "https:") return true;
641
+ return parsed.protocol === "http:" && isLoopback(parsed.hostname);
642
+ }
643
+
644
+ // src/core/client-context.ts
645
+ var context;
646
+ function setClientContext(input) {
647
+ const envType = process.env.WALKEROS_CLIENT_TYPE;
648
+ const type = envType ?? input.type ?? "cli";
649
+ context = { type, version: input.version };
650
+ }
651
+ function getClientContext() {
652
+ return context;
653
+ }
654
+ function resetClientContext() {
655
+ context = void 0;
656
+ }
657
+ function clientContextHeaders() {
658
+ if (!context) return {};
659
+ return {
660
+ "User-Agent": `walkeros-${context.type}/${context.version}`,
661
+ "X-WalkerOS-Client": context.type,
662
+ "X-WalkerOS-Client-Version": context.version
663
+ };
664
+ }
665
+
666
+ // src/core/app-fetch.ts
667
+ var AppErrorSchema = z2.object({
668
+ error: z2.object({ message: z2.string().min(1) })
669
+ });
670
+ function appErrorMessage(body) {
671
+ const parsed = AppErrorSchema.safeParse(body);
672
+ return parsed.success ? parsed.data.error.message : void 0;
673
+ }
674
+ async function appFetch(path23, init = {}) {
675
+ const url = `${(init.baseUrl ?? resolveAppUrl()).replace(/\/+$/, "")}${path23}`;
676
+ const headers = new Headers(clientContextHeaders());
677
+ for (const [name, value] of Object.entries(init.headers ?? {}))
678
+ headers.set(name, value);
679
+ if (init.token && !headers.has("Authorization"))
680
+ headers.set("Authorization", `Bearer ${init.token}`);
681
+ if (headers.has("Authorization")) requireSecureUrl(url);
682
+ const request = { headers };
683
+ if (init.method && init.method !== "GET") request.method = init.method;
684
+ if (init.body !== void 0) request.body = init.body;
685
+ if (init.signal) request.signal = init.signal;
686
+ if (init.redirect) request.redirect = init.redirect;
687
+ return (init.fetch ?? fetch)(url, request);
688
+ }
689
+
690
+ // src/core/app-paths.ts
691
+ var APP_PATHS = {
692
+ whoami: "/api/auth/whoami",
693
+ health: "/api/health",
694
+ telemetry: "/api/telemetry",
695
+ openapi: "/api/openapi.json",
696
+ authorizationServer: "/.well-known/oauth-authorization-server",
697
+ cliClient: "/oauth/walkeros-cli"
698
+ };
699
+
700
+ // src/core/oauth-login.ts
701
+ var REQUEST_TIMEOUT_MS = 1e4;
702
+ var FRESH_FOR_MS = 6e4;
703
+ var LoginError = class extends Error {
704
+ };
705
+ var MetadataSchema = z3.object({
706
+ issuer: z3.string(),
707
+ authorization_endpoint: z3.string(),
708
+ token_endpoint: z3.string(),
709
+ revocation_endpoint: z3.string(),
710
+ code_challenge_methods_supported: z3.array(z3.string()).optional()
711
+ });
712
+ var ClientDocumentSchema = z3.object({
713
+ client_id: z3.string(),
714
+ redirect_uris: z3.array(z3.string())
715
+ });
716
+ var TokenSetSchema = z3.object({
717
+ access_token: z3.string().min(1),
718
+ token_type: z3.string().refine((type) => type.toLowerCase() === "bearer"),
719
+ expires_in: z3.number(),
720
+ refresh_token: z3.string().min(1).optional(),
721
+ scope: z3.string().optional()
722
+ });
723
+ var OAuthErrorSchema = z3.object({ error: z3.string() });
724
+ async function postForm(url, form, expectTokens) {
725
+ if (!isSecureSignInUrl(url))
726
+ throw new LoginError(
727
+ `refusing to send a walkerOS sign-in to ${url}: it is neither https nor http on this machine: run walkeros login`
728
+ );
729
+ let response;
730
+ try {
731
+ response = await fetch(url, {
732
+ method: "POST",
733
+ headers: {
734
+ ...clientContextHeaders(),
735
+ "Content-Type": "application/x-www-form-urlencoded",
736
+ Accept: "application/json"
737
+ },
738
+ body: new URLSearchParams(form).toString(),
739
+ redirect: "error",
740
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
741
+ });
742
+ } catch (error2) {
743
+ return {
744
+ kind: "unreachable",
745
+ reason: fetchFailureReason(error2, REQUEST_TIMEOUT_MS)
746
+ };
747
+ }
748
+ let body;
749
+ try {
750
+ body = await response.json();
751
+ } catch {
752
+ body = void 0;
753
+ }
754
+ if (response.ok) {
755
+ if (!expectTokens) return { kind: "ok" };
756
+ const tokens = TokenSetSchema.safeParse(body);
757
+ return tokens.success ? { kind: "ok", tokens: tokens.data } : { kind: "failed", status: response.status };
758
+ }
759
+ const error = OAuthErrorSchema.safeParse(body);
760
+ return error.success ? { kind: "refused", status: response.status, error: error.data.error } : { kind: "failed", status: response.status };
761
+ }
762
+ function answerText(answer) {
763
+ switch (answer.kind) {
764
+ case "ok":
765
+ return "HTTP 200";
766
+ case "refused":
767
+ return `HTTP ${answer.status} ${answer.error}`;
768
+ case "failed":
769
+ return `HTTP ${answer.status}`;
770
+ case "unreachable":
771
+ return answer.reason;
772
+ }
773
+ }
774
+ function isFresh(login, now = Date.now()) {
775
+ return login.expires_at - now > FRESH_FOR_MS;
776
+ }
777
+ function loginOf(endpoints, tokens, now = Date.now()) {
778
+ return {
779
+ issuer: endpoints.issuer,
780
+ client_id: endpoints.clientId,
781
+ token_endpoint: endpoints.tokenEndpoint,
782
+ revocation_endpoint: endpoints.revocationEndpoint,
783
+ access_token: tokens.access_token,
784
+ expires_at: now + tokens.expires_in * 1e3,
785
+ ...tokens.refresh_token === void 0 ? {} : { refresh_token: tokens.refresh_token },
786
+ ...tokens.scope === void 0 ? {} : { scope: tokens.scope }
787
+ };
788
+ }
789
+ var SignInError = class extends Error {
790
+ constructor(message, reason) {
791
+ super(message);
792
+ this.reason = reason;
793
+ }
794
+ reason;
795
+ };
796
+ function without(logins, issuer) {
797
+ return Object.fromEntries(
798
+ Object.entries(logins).filter(([key]) => key !== issuer)
799
+ );
800
+ }
801
+ function endedError(issuer) {
802
+ return new SignInError(
803
+ `your sign-in to ${issuer} ended: run walkeros login`,
804
+ "ended"
805
+ );
806
+ }
807
+ async function refreshStoredLogin(issuer, options = {}) {
808
+ try {
809
+ return await withCredentialsLock(async () => {
810
+ const logins = readCredentials();
811
+ const login = logins[issuer];
812
+ if (login === void 0) throw endedError(issuer);
813
+ if (login.access_token !== options.rejected && isFresh(login))
814
+ return login;
815
+ const remove = () => writeCredentials(without(logins, issuer));
816
+ if (login.refresh_token === void 0) {
817
+ remove();
818
+ throw endedError(issuer);
819
+ }
820
+ const answer = await postForm(
821
+ login.token_endpoint,
822
+ {
823
+ grant_type: "refresh_token",
824
+ client_id: login.client_id,
825
+ refresh_token: login.refresh_token
826
+ },
827
+ true
828
+ );
829
+ if (answer.kind === "ok" && answer.tokens) {
830
+ const tokens = answer.tokens;
831
+ const refreshed = loginOf(
832
+ {
833
+ issuer: login.issuer,
834
+ clientId: login.client_id,
835
+ tokenEndpoint: login.token_endpoint,
836
+ revocationEndpoint: login.revocation_endpoint
837
+ },
838
+ {
839
+ ...tokens,
840
+ refresh_token: tokens.refresh_token ?? login.refresh_token,
841
+ scope: tokens.scope ?? login.scope
842
+ }
843
+ );
844
+ writeCredentials({ ...logins, [issuer]: refreshed });
845
+ return refreshed;
846
+ }
847
+ if (answer.kind === "refused" && (answer.error === "invalid_grant" || answer.error === "invalid_client")) {
848
+ remove();
849
+ throw endedError(issuer);
850
+ }
851
+ if (answer.kind === "unreachable")
852
+ throw new SignInError(
853
+ `could not reach ${issuer} to renew the sign-in: ${answer.reason}: try again`,
854
+ "unreachable"
855
+ );
856
+ throw new SignInError(
857
+ `${issuer} did not renew the sign-in (${answerText(answer)}): try again, or run walkeros login`,
858
+ "unreachable"
859
+ );
860
+ }, options.timing);
861
+ } catch (error) {
862
+ if (error instanceof SignInError) throw error;
863
+ if (error instanceof LoginError)
864
+ throw new SignInError(error.message, "ended");
865
+ if (error instanceof CredentialsBusyError)
866
+ throw new SignInError(error.message, "busy");
867
+ throw error;
868
+ }
869
+ }
870
+
353
871
  // src/core/auth.ts
354
872
  function resolveAccessToken() {
355
873
  const token = process.env.WALKEROS_TOKEN;
356
874
  return token ? token : null;
357
875
  }
876
+ async function resolveCredential(appUrl = resolveAppUrl()) {
877
+ const env = resolveAccessToken();
878
+ if (env !== null) return { source: "env", token: env };
879
+ const logins = readCredentials();
880
+ const login = loginForAppUrl(logins, appUrl);
881
+ if (login === void 0) {
882
+ const issuers = Object.keys(logins);
883
+ if (issuers.length === 0) return null;
884
+ throw new SignInError(
885
+ `signed in to ${issuers.join(", ")}, not ${appUrl.replace(/\/+$/, "")}: run walkeros login`,
886
+ "elsewhere"
887
+ );
888
+ }
889
+ const usable = isFresh(login) ? login : await refreshStoredLogin(login.issuer);
890
+ return { source: "login", token: usable.access_token, issuer: usable.issuer };
891
+ }
892
+ async function sendWithCredential(credential, send) {
893
+ const response = await send(credential.token);
894
+ if (response.status !== 401 || credential.source !== "login") return response;
895
+ await response.body?.cancel();
896
+ const renewed = await refreshStoredLogin(credential.issuer, {
897
+ rejected: credential.token
898
+ });
899
+ return send(renewed.access_token);
900
+ }
358
901
 
359
902
  // src/core/event-validation.ts
360
903
  import { schemas } from "@walkeros/core/dev";
@@ -1001,12 +1544,13 @@ function resolveStepPackage(flow, kind, id, logger) {
1001
1544
  }
1002
1545
 
1003
1546
  // src/core/package-manager.ts
1547
+ import { createHash as createHash3 } from "crypto";
1004
1548
  import pacote from "pacote";
1005
1549
  import path10 from "path";
1006
1550
  import fs10 from "fs-extra";
1007
1551
  import { readFile } from "fs/promises";
1008
1552
  import os from "os";
1009
- import semver2 from "semver";
1553
+ import semver3 from "semver";
1010
1554
 
1011
1555
  // src/core/cache-utils.ts
1012
1556
  import { getHashServer } from "@walkeros/server-core";
@@ -1202,6 +1746,246 @@ function assertTransitiveRegistrySpec(check) {
1202
1746
  );
1203
1747
  }
1204
1748
 
1749
+ // src/core/package-tree.ts
1750
+ import { createHash as createHash2 } from "crypto";
1751
+ import npa2 from "npm-package-arg";
1752
+ import semver2 from "semver";
1753
+ import { z as z4 } from "@walkeros/core/dev";
1754
+ var PACKAGE_TREE_VERSION = 1;
1755
+ var PACKAGE_TREE_MAX_ENTRIES = 5e3;
1756
+ var SHA512_INTEGRITY = /^sha512-[A-Za-z0-9+/]{86}==$/;
1757
+ var PackageTreeEntrySchema = z4.object({
1758
+ name: z4.string().min(1).describe("Registry name of the package"),
1759
+ version: z4.string().min(1).describe("Exact version"),
1760
+ resolved: z4.string().min(1).describe("Tarball URL on the registry"),
1761
+ integrity: z4.string().min(1).describe("sha512 of the tarball bytes"),
1762
+ dependencies: z4.record(z4.string().min(1), z4.string()).optional().describe("Edges followed: dependencies and required peers")
1763
+ }).strict();
1764
+ var PackageTreeSchema = z4.object({
1765
+ version: z4.literal(PACKAGE_TREE_VERSION),
1766
+ registry: z4.string().min(1).describe("Registry every entry comes from"),
1767
+ before: z4.string().nullable().describe("Release-age cutoff the tree was resolved under"),
1768
+ packages: z4.record(z4.string().min(1), PackageTreeEntrySchema).describe("Entries by install path")
1769
+ }).strict().meta({ id: "PackageTree", title: "Recorded package tree" });
1770
+ var PackageTreeError = class extends Error {
1771
+ constructor(problems) {
1772
+ super(`Package tree refused: ${problems.join("; ")}`);
1773
+ this.problems = problems;
1774
+ this.name = "PackageTreeError";
1775
+ }
1776
+ problems;
1777
+ code = "PACKAGE_TREE_INVALID";
1778
+ };
1779
+ function canonicalPackageTree(tree) {
1780
+ return JSON.stringify(sortKeys(tree));
1781
+ }
1782
+ function sortKeys(value) {
1783
+ if (Array.isArray(value)) return value.map(sortKeys);
1784
+ if (typeof value === "object" && value !== null) {
1785
+ const sorted = {};
1786
+ for (const key of Object.keys(value).sort()) {
1787
+ sorted[key] = sortKeys(Reflect.get(value, key));
1788
+ }
1789
+ return sorted;
1790
+ }
1791
+ return value;
1792
+ }
1793
+ function packageTreeDigest(tree) {
1794
+ return createHash2("sha256").update(canonicalPackageTree(tree)).digest("hex");
1795
+ }
1796
+ function normalizeRegistry(registry) {
1797
+ try {
1798
+ const url = new URL(registry);
1799
+ if (!url.pathname.endsWith("/")) url.pathname = `${url.pathname}/`;
1800
+ return url;
1801
+ } catch {
1802
+ return null;
1803
+ }
1804
+ }
1805
+ function isPackageName(name) {
1806
+ try {
1807
+ const parsed = npa2(`${name}@0.0.0`);
1808
+ return parsed.name === name && parsed.type === "version";
1809
+ } catch {
1810
+ return false;
1811
+ }
1812
+ }
1813
+ function scopeOf(name) {
1814
+ return name.startsWith("@") ? name.slice(0, name.indexOf("/")) : "";
1815
+ }
1816
+ function isExemptName(name, exempt) {
1817
+ return exempt.some((prefix) => name.startsWith(prefix));
1818
+ }
1819
+ function aliasCrossesExemption(installName, realName, exempt) {
1820
+ return isExemptName(installName, exempt) !== isExemptName(realName, exempt);
1821
+ }
1822
+ function edgeTargetName(depName, spec) {
1823
+ try {
1824
+ const parsed = npa2.resolve(depName, spec);
1825
+ if (parsed.type === "alias") return parsed.subSpec.name ?? null;
1826
+ return parsed.name ?? null;
1827
+ } catch {
1828
+ return null;
1829
+ }
1830
+ }
1831
+ function isExactSpec(depName, spec) {
1832
+ try {
1833
+ const parsed = npa2.resolve(depName, spec);
1834
+ if (parsed.type === "alias") return parsed.subSpec.type === "version";
1835
+ return parsed.type === "version";
1836
+ } catch {
1837
+ return false;
1838
+ }
1839
+ }
1840
+ function edgeIsExempt(edge, exempt) {
1841
+ const target = edgeTargetName(edge.name, edge.spec);
1842
+ if (target === null) return false;
1843
+ if (isExemptName(target, exempt)) return true;
1844
+ return edge.parentName !== void 0 && isExemptName(edge.parentName, exempt) && isExactSpec(edge.name, edge.spec);
1845
+ }
1846
+ function tarballPath(registryPath, name, version) {
1847
+ const base = name.startsWith("@") ? name.slice(name.indexOf("/") + 1) : name;
1848
+ return `${registryPath}${name}/-/${base}-${version}.tgz`;
1849
+ }
1850
+ var LAYOUT = /^node_modules\/((?:@[^/]+\/)?[^/@][^/]*)(?:\/node_modules\/((?:@[^/]+\/)?[^/@][^/]*))?$/;
1851
+ function parseInstallPath(path23) {
1852
+ const match = LAYOUT.exec(path23);
1853
+ if (!match) return null;
1854
+ const [, first, second] = match;
1855
+ if (!isPackageName(first)) return null;
1856
+ if (second === void 0) return { name: first };
1857
+ if (!isPackageName(second)) return null;
1858
+ return { name: second, consumer: first };
1859
+ }
1860
+ function lookupPaths(path23, dep) {
1861
+ const at = parseInstallPath(path23);
1862
+ if (!at) return [];
1863
+ return at.consumer === void 0 ? [`node_modules/${at.name}/node_modules/${dep}`, `node_modules/${dep}`] : [
1864
+ `node_modules/${at.consumer}/node_modules/${dep}`,
1865
+ `node_modules/${dep}`
1866
+ ];
1867
+ }
1868
+ function packageTreeProblems(value, options) {
1869
+ const parsed = PackageTreeSchema.safeParse(value);
1870
+ if (!parsed.success) {
1871
+ return parsed.error.issues.map(
1872
+ (issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`
1873
+ );
1874
+ }
1875
+ const tree = parsed.data;
1876
+ const problems = [];
1877
+ const exempt = options.exempt ?? [];
1878
+ const registry = normalizeRegistry(options.registry);
1879
+ const treeRegistry = normalizeRegistry(tree.registry);
1880
+ if (!registry)
1881
+ return [`the build registry is not a URL: ${options.registry}`];
1882
+ if (!treeRegistry || treeRegistry.href !== registry.href) {
1883
+ problems.push(`registry ${tree.registry} is not ${registry.href}`);
1884
+ }
1885
+ const paths = Object.keys(tree.packages);
1886
+ if (paths.length > PACKAGE_TREE_MAX_ENTRIES) {
1887
+ return [
1888
+ `${paths.length} entries, more than ${PACKAGE_TREE_MAX_ENTRIES}`,
1889
+ ...problems
1890
+ ];
1891
+ }
1892
+ for (const [path23, entry] of Object.entries(tree.packages)) {
1893
+ const at = parseInstallPath(path23);
1894
+ if (!at) {
1895
+ problems.push(
1896
+ `${path23}: not node_modules/<name> or node_modules/<name>/node_modules/<name>`
1897
+ );
1898
+ continue;
1899
+ }
1900
+ if (!isPackageName(entry.name)) {
1901
+ problems.push(`${path23}: name ${entry.name} is not a package name`);
1902
+ continue;
1903
+ }
1904
+ if (aliasCrossesExemption(at.name, entry.name, exempt)) {
1905
+ problems.push(
1906
+ `${path23}: an alias of ${entry.name} crosses an exempt prefix`
1907
+ );
1908
+ }
1909
+ if (semver2.valid(entry.version) !== entry.version) {
1910
+ problems.push(`${path23}: version ${entry.version} is not exact`);
1911
+ }
1912
+ if (!SHA512_INTEGRITY.test(entry.integrity)) {
1913
+ problems.push(`${path23}: integrity is not one sha512 value`);
1914
+ }
1915
+ const scoped = options.scopeRegistries?.[scopeOf(entry.name)];
1916
+ const from = scoped ? normalizeRegistry(scoped) : registry;
1917
+ if (!tarballOn(entry.resolved, from, entry.name, entry.version)) {
1918
+ problems.push(
1919
+ `${path23}: resolved ${entry.resolved} is not the ${entry.name}@${entry.version} tarball on ${from?.origin ?? "the registry"}`
1920
+ );
1921
+ }
1922
+ for (const [dep, spec] of Object.entries(entry.dependencies ?? {})) {
1923
+ const target = edgeTargetName(dep, spec);
1924
+ if (target === null) {
1925
+ problems.push(`${path23}: dependency ${dep}@${spec} is no registry spec`);
1926
+ continue;
1927
+ }
1928
+ if (aliasCrossesExemption(dep, target, exempt)) {
1929
+ problems.push(
1930
+ `${path23}: dependency ${dep}@${spec} crosses an exempt prefix`
1931
+ );
1932
+ continue;
1933
+ }
1934
+ const found = lookupPaths(path23, dep).find((p) => tree.packages[p]);
1935
+ if (!found || tree.packages[found].name !== target) {
1936
+ problems.push(
1937
+ `${path23}: dependency ${dep}@${spec} has no ${target} in the tree`
1938
+ );
1939
+ }
1940
+ }
1941
+ }
1942
+ for (const [name, spec] of Object.entries(options.declared ?? {})) {
1943
+ const root = tree.packages[`node_modules/${name}`];
1944
+ if (!root || root.name !== name) {
1945
+ problems.push(`declared ${name}@${spec} is not at node_modules/${name}`);
1946
+ } else if (!versionNamedBy(root.version, spec)) {
1947
+ problems.push(`declared ${name}@${spec} is ${root.version} in the tree`);
1948
+ }
1949
+ }
1950
+ return problems;
1951
+ }
1952
+ function assertPackageTree(value, options) {
1953
+ const problems = packageTreeProblems(value, options);
1954
+ if (problems.length > 0) throw new PackageTreeError(problems);
1955
+ return PackageTreeSchema.parse(value);
1956
+ }
1957
+ function tarballOn(resolved, registry, name, version) {
1958
+ if (!registry) return false;
1959
+ let url;
1960
+ try {
1961
+ url = new URL(resolved);
1962
+ } catch {
1963
+ return false;
1964
+ }
1965
+ return url.origin === registry.origin && url.protocol === registry.protocol && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname === tarballPath(registry.pathname, name, version);
1966
+ }
1967
+ function versionNamedBy(version, spec) {
1968
+ if (semver2.valid(spec) !== null) return semver2.eq(spec, version);
1969
+ if (semver2.validRange(spec) !== null) {
1970
+ return semver2.satisfies(version, spec, { includePrerelease: true });
1971
+ }
1972
+ return true;
1973
+ }
1974
+ function treeVersionLines(tree) {
1975
+ return Object.entries(tree.packages).flatMap(([path23, entry]) => {
1976
+ const at = parseInstallPath(path23);
1977
+ return at && at.consumer === void 0 ? [`${at.name}@${entry.version}`] : [];
1978
+ }).sort((a, b) => a.localeCompare(b));
1979
+ }
1980
+ function followedEdges(manifest) {
1981
+ const edges = { ...manifest.dependencies ?? {} };
1982
+ for (const [name, spec] of Object.entries(manifest.peerDependencies ?? {})) {
1983
+ if (manifest.peerDependenciesMeta?.[name]?.optional === true) continue;
1984
+ if (!(name in edges)) edges[name] = spec;
1985
+ }
1986
+ return edges;
1987
+ }
1988
+
1205
1989
  // src/core/package-manager.ts
1206
1990
  var IGNORE_SCRIPTS = { ignoreScripts: true };
1207
1991
  var PACOTE_OPTS = {
@@ -1210,6 +1994,32 @@ var PACOTE_OPTS = {
1210
1994
  where: void 0,
1211
1995
  ...IGNORE_SCRIPTS
1212
1996
  };
1997
+ var UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
1998
+ "E404",
1999
+ "E410",
2000
+ "ETARGET",
2001
+ "EINTEGRITY"
2002
+ ]);
2003
+ var PackageUnavailableError = class extends Error {
2004
+ constructor(installPath, entry, reason) {
2005
+ super(
2006
+ `Recorded package ${entry.name}@${entry.version} (${installPath}) could not be installed: ${reason}`
2007
+ );
2008
+ this.installPath = installPath;
2009
+ this.name = "PackageUnavailableError";
2010
+ }
2011
+ installPath;
2012
+ code = "PACKAGE_UNAVAILABLE";
2013
+ };
2014
+ function scopeRegistriesOf(npmConfig) {
2015
+ const scoped = {};
2016
+ for (const [key, value] of Object.entries(npmConfig)) {
2017
+ if (key.startsWith("@") && key.endsWith(":registry") && typeof value === "string") {
2018
+ scoped[key.slice(0, -":registry".length)] = value;
2019
+ }
2020
+ }
2021
+ return scoped;
2022
+ }
1213
2023
  async function loadNpmConfigForPacote(projectDir = process.cwd(), homeDir = os.homedir()) {
1214
2024
  const merged = {};
1215
2025
  const sources = [
@@ -1334,6 +2144,13 @@ function extractWithResilience(spec, dest, opts, _logger, retryOptions) {
1334
2144
  retryOptions
1335
2145
  );
1336
2146
  }
2147
+ function tarballWithResilience(spec, opts, retryOptions) {
2148
+ return withPacoteRetry(
2149
+ (signal) => pacote.tarball(spec, withSignal(opts, signal)),
2150
+ `download ${spec}`,
2151
+ retryOptions
2152
+ );
2153
+ }
1337
2154
  function manifestWithResilience(spec, opts, _logger, retryOptions) {
1338
2155
  return withPacoteRetry(
1339
2156
  (signal) => pacote.manifest(spec, withSignal(opts, signal)),
@@ -1353,9 +2170,10 @@ function getNestedPackageDirectory(baseDir, consumerName, nestedPackageName) {
1353
2170
  nestedPackageName
1354
2171
  );
1355
2172
  }
1356
- async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmConfig = PACOTE_OPTS) {
2173
+ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmConfig = PACOTE_OPTS, cooldown) {
1357
2174
  const allSpecs = /* @__PURE__ */ new Map();
1358
2175
  const visited = /* @__PURE__ */ new Set();
2176
+ const exemptEdge = (name, spec, parentName) => cooldown !== void 0 && edgeIsExempt({ name, spec, parentName }, cooldown.exempt);
1359
2177
  const directLocalNames = new Set(
1360
2178
  packages.filter((p) => p.path).map((p) => p.name)
1361
2179
  );
@@ -1366,7 +2184,7 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1366
2184
  );
1367
2185
  }
1368
2186
  }
1369
- const substituteDep = (depName, depSpec, source, from, optional) => {
2187
+ const substituteDep = (depName, depSpec, source, from, optional, parentName) => {
1370
2188
  const overrideSpec = overrides[depName];
1371
2189
  if (overrideSpec) {
1372
2190
  return {
@@ -1374,10 +2192,18 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1374
2192
  spec: overrideSpec,
1375
2193
  source: "override",
1376
2194
  from: `override (was ${depSpec} from ${from})`,
1377
- optional
2195
+ optional,
2196
+ exempt: exemptEdge(depName, overrideSpec)
1378
2197
  };
1379
2198
  }
1380
- return { name: depName, spec: depSpec, source, from, optional };
2199
+ return {
2200
+ name: depName,
2201
+ spec: depSpec,
2202
+ source,
2203
+ from,
2204
+ optional,
2205
+ exempt: exemptEdge(depName, depSpec, parentName)
2206
+ };
1381
2207
  };
1382
2208
  assertRegistrySpecs([
1383
2209
  ...packages.filter((pkg) => !pkg.path).map((pkg) => ({ name: pkg.name, spec: pkg.version, from: "flow.json" })),
@@ -1395,7 +2221,8 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1395
2221
  source: "direct",
1396
2222
  from: "flow.json",
1397
2223
  optional: false,
1398
- localPath: pkg.path
2224
+ localPath: pkg.path,
2225
+ exempt: exemptEdge(pkg.name, pkg.version)
1399
2226
  }));
1400
2227
  while (queue.length > 0) {
1401
2228
  const item = queue.shift();
@@ -1411,6 +2238,7 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1411
2238
  }
1412
2239
  assertTransitiveRegistrySpec(item);
1413
2240
  }
2241
+ if (cooldown && !item.localPath) assertAliasKeepsSide(item, cooldown);
1414
2242
  if (!allSpecs.has(item.name)) allSpecs.set(item.name, []);
1415
2243
  const existing = allSpecs.get(item.name);
1416
2244
  if (!existing.some((s) => s.spec === item.spec && s.from === item.from)) {
@@ -1419,7 +2247,8 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1419
2247
  source: item.source,
1420
2248
  from: item.from,
1421
2249
  optional: item.optional,
1422
- localPath: item.localPath
2250
+ localPath: item.localPath,
2251
+ ...cooldown ? { exempt: item.exempt } : {}
1423
2252
  });
1424
2253
  }
1425
2254
  const visitKey = `${item.name}@${item.spec}`;
@@ -1468,10 +2297,16 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1468
2297
  try {
1469
2298
  manifest = await manifestWithResilience(
1470
2299
  `${item.name}@${item.spec}`,
1471
- npmConfig,
2300
+ cooldownOptions(npmConfig, cooldown, item.exempt),
1472
2301
  logger
1473
2302
  );
2303
+ if (cooldown && !item.exempt) {
2304
+ assertPublishedBefore(manifest, cooldown, item.from);
2305
+ }
1474
2306
  } catch (error) {
2307
+ if (cooldown) {
2308
+ throw cooldownFailure(item.name, item.spec, item.from, cooldown, error);
2309
+ }
1475
2310
  logger.warn(
1476
2311
  `Failed to fetch manifest for ${item.name}@${item.spec} after retries; its dependencies may be unresolved: ${error instanceof Error ? error.message : String(error)}`
1477
2312
  );
@@ -1481,7 +2316,14 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1481
2316
  for (const [depName, depSpec] of Object.entries(deps)) {
1482
2317
  if (typeof depSpec === "string") {
1483
2318
  queue.push(
1484
- substituteDep(depName, depSpec, "dependency", item.name, false)
2319
+ substituteDep(
2320
+ depName,
2321
+ depSpec,
2322
+ "dependency",
2323
+ item.name,
2324
+ false,
2325
+ manifest.name
2326
+ )
1485
2327
  );
1486
2328
  }
1487
2329
  }
@@ -1496,13 +2338,46 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
1496
2338
  depSpec,
1497
2339
  "peerDependency",
1498
2340
  item.name,
1499
- isOptional
2341
+ isOptional,
2342
+ manifest.name
1500
2343
  )
1501
2344
  );
1502
2345
  }
1503
2346
  }
1504
2347
  }
1505
- return allSpecs;
2348
+ return allSpecs;
2349
+ }
2350
+ function cooldownOptions(npmConfig, cooldown, exempt) {
2351
+ return cooldown && !exempt ? { ...npmConfig, before: cooldown.before } : npmConfig;
2352
+ }
2353
+ function assertPublishedBefore(manifest, cooldown, from) {
2354
+ const time = Reflect.get(manifest, "_time");
2355
+ const label = `${manifest.name}@${manifest.version} (from ${from})`;
2356
+ if (typeof time !== "string" || Number.isNaN(Date.parse(time))) {
2357
+ throw new Error(`${label} has no publish time in the registry`);
2358
+ }
2359
+ if (Date.parse(time) > cooldown.before.getTime()) {
2360
+ throw new Error(
2361
+ `${label} was published ${time}, after ${cooldown.before.toISOString()}`
2362
+ );
2363
+ }
2364
+ }
2365
+ function cooldownFailure(name, spec, from, cooldown, error) {
2366
+ if (readErrorCode(error) === "ETARGET") {
2367
+ return new Error(
2368
+ `No version of ${name}@${spec} (from ${from}) was published at or before ${cooldown.before.toISOString()}`
2369
+ );
2370
+ }
2371
+ if (error instanceof Error) return error;
2372
+ return new Error(String(error));
2373
+ }
2374
+ function assertAliasKeepsSide(item, cooldown) {
2375
+ const target = edgeTargetName(item.name, item.spec);
2376
+ if (target !== null && aliasCrossesExemption(item.name, target, cooldown.exempt)) {
2377
+ throw new PackageTreeError([
2378
+ `${item.name}@${item.spec} (from ${item.from}) aliases across an exempt prefix`
2379
+ ]);
2380
+ }
1506
2381
  }
1507
2382
  var SOURCE_PRIORITY = {
1508
2383
  direct: 0,
@@ -1510,21 +2385,26 @@ var SOURCE_PRIORITY = {
1510
2385
  dependency: 2,
1511
2386
  peerDependency: 3
1512
2387
  };
1513
- async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
2388
+ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}, cooldown) {
1514
2389
  const topLevel = /* @__PURE__ */ new Map();
1515
2390
  const nested = [];
1516
2391
  for (const [name, specs] of allSpecs) {
1517
2392
  let nestLoser2 = function(spec, alreadyNested2) {
1518
2393
  if (spec.localPath) return;
1519
2394
  if (alreadyNested2.has(spec.spec)) return;
1520
- if (semver2.valid(spec.spec) === null && semver2.validRange(spec.spec) === null) {
2395
+ if (semver3.valid(spec.spec) === null && semver3.validRange(spec.spec) === null) {
1521
2396
  logger.debug(
1522
2397
  `Skipping non-semver loser spec for ${name}: ${spec.spec} from ${spec.from}`
1523
2398
  );
1524
2399
  return;
1525
2400
  }
1526
2401
  const consumers = activeSpecs.filter((s) => s.spec === spec.spec).map((s) => s.from);
1527
- nested.push({ name, version: spec.spec, consumers });
2402
+ nested.push({
2403
+ name,
2404
+ version: spec.spec,
2405
+ consumers,
2406
+ ...cooldown ? { exempt: specExempt(spec.spec) } : {}
2407
+ });
1528
2408
  alreadyNested2.add(spec.spec);
1529
2409
  };
1530
2410
  var nestLoser = nestLoser2;
@@ -1537,6 +2417,7 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
1537
2417
  });
1538
2418
  continue;
1539
2419
  }
2420
+ const specExempt = (spec) => cooldown !== void 0 && specs.filter((s) => s.spec === spec).every((s) => s.exempt === true);
1540
2421
  const nonPeerSpecs = specs.filter((s) => s.source !== "peerDependency");
1541
2422
  const peerSpecs = specs.filter((s) => s.source === "peerDependency");
1542
2423
  let activeSpecs;
@@ -1554,7 +2435,7 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
1554
2435
  (a, b) => SOURCE_PRIORITY[a.source] - SOURCE_PRIORITY[b.source]
1555
2436
  );
1556
2437
  const directSpecs = activeSpecs.filter((s) => s.source === "direct");
1557
- const directExact = directSpecs.find((s) => semver2.valid(s.spec) !== null);
2438
+ const directExact = directSpecs.find((s) => semver3.valid(s.spec) !== null);
1558
2439
  let chosenVersion;
1559
2440
  const alreadyNested = /* @__PURE__ */ new Set();
1560
2441
  if (directExact) {
@@ -1562,15 +2443,20 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
1562
2443
  } else if (directSpecs.length > 0) {
1563
2444
  chosenVersion = directSpecs[0].spec;
1564
2445
  } else {
1565
- const exactVersions = activeSpecs.filter((s) => semver2.valid(s.spec) !== null).map((s) => s.spec);
2446
+ const exactVersions = activeSpecs.filter((s) => semver3.valid(s.spec) !== null).map((s) => s.spec);
1566
2447
  const uniqueExact = [...new Set(exactVersions)];
1567
2448
  if (uniqueExact.length > 1) {
1568
- const sorted = [...uniqueExact].sort(semver2.rcompare);
2449
+ const sorted = [...uniqueExact].sort(semver3.rcompare);
1569
2450
  chosenVersion = sorted[0];
1570
2451
  for (let i = 1; i < sorted.length; i++) {
1571
2452
  const loserVersion = sorted[i];
1572
2453
  const consumers = activeSpecs.filter((s) => s.spec === loserVersion).map((s) => s.from);
1573
- nested.push({ name, version: loserVersion, consumers });
2454
+ nested.push({
2455
+ name,
2456
+ version: loserVersion,
2457
+ consumers,
2458
+ ...cooldown ? { exempt: specExempt(loserVersion) } : {}
2459
+ });
1574
2460
  alreadyNested.add(loserVersion);
1575
2461
  }
1576
2462
  } else if (uniqueExact.length === 1) {
@@ -1580,17 +2466,25 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
1580
2466
  }
1581
2467
  }
1582
2468
  let concreteVersion;
1583
- if (semver2.valid(chosenVersion)) {
2469
+ if (semver3.valid(chosenVersion)) {
1584
2470
  concreteVersion = chosenVersion;
1585
2471
  } else {
2472
+ const askedBy = specs.filter((s) => s.spec === chosenVersion).map((s) => s.from).join(", ");
1586
2473
  try {
2474
+ const exempt = specExempt(chosenVersion);
1587
2475
  const manifest = await manifestWithResilience(
1588
2476
  `${name}@${chosenVersion}`,
1589
- npmConfig,
2477
+ cooldownOptions(npmConfig, cooldown, exempt),
1590
2478
  logger
1591
2479
  );
2480
+ if (cooldown && !exempt) {
2481
+ assertPublishedBefore(manifest, cooldown, askedBy);
2482
+ }
1592
2483
  concreteVersion = manifest.version;
1593
2484
  } catch (err) {
2485
+ if (cooldown) {
2486
+ throw cooldownFailure(name, chosenVersion, askedBy, cooldown, err);
2487
+ }
1594
2488
  if (process.env.BUNDLER_STRICT_RANGES === "0") {
1595
2489
  logger.warn(
1596
2490
  `Could not resolve ${name}@${chosenVersion} for strict validation: ${err instanceof Error ? err.message : String(err)}. Skipping range validation (BUNDLER_STRICT_RANGES=0).`
@@ -1606,7 +2500,7 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
1606
2500
  for (const spec of specs) {
1607
2501
  if (spec.localPath) continue;
1608
2502
  if (spec.spec === chosenVersion) continue;
1609
- const satisfied = semver2.valid(spec.spec) ? spec.spec === concreteVersion : semver2.validRange(spec.spec) ? semver2.satisfies(concreteVersion, spec.spec, {
2503
+ const satisfied = semver3.valid(spec.spec) ? spec.spec === concreteVersion : semver3.validRange(spec.spec) ? semver3.satisfies(concreteVersion, spec.spec, {
1610
2504
  includePrerelease: true
1611
2505
  }) : true;
1612
2506
  if (satisfied) continue;
@@ -1618,7 +2512,11 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
1618
2512
  nestLoser2(spec, alreadyNested);
1619
2513
  }
1620
2514
  }
1621
- topLevel.set(name, { name, version: chosenVersion });
2515
+ topLevel.set(name, {
2516
+ name,
2517
+ version: chosenVersion,
2518
+ ...cooldown ? { exempt: specExempt(chosenVersion) } : {}
2519
+ });
1622
2520
  }
1623
2521
  const consolidatedMap = /* @__PURE__ */ new Map();
1624
2522
  for (const entry of nested) {
@@ -1628,13 +2526,16 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
1628
2526
  for (const c of entry.consumers) {
1629
2527
  if (!existing.consumers.includes(c)) existing.consumers.push(c);
1630
2528
  }
2529
+ if (existing.exempt !== void 0) {
2530
+ existing.exempt = existing.exempt && entry.exempt === true;
2531
+ }
1631
2532
  } else {
1632
2533
  consolidatedMap.set(key, { ...entry, consumers: [...entry.consumers] });
1633
2534
  }
1634
2535
  }
1635
2536
  return { topLevel, nested: [...consolidatedMap.values()] };
1636
2537
  }
1637
- async function downloadPackagesWithResolution(packages, targetDir, logger, useCache = true, configDir, tmpDir, overrides = {}, npmConfig = PACOTE_OPTS) {
2538
+ async function downloadPackagesWithResolution(packages, targetDir, logger, useCache = true, configDir, tmpDir, overrides = {}, npmConfig = PACOTE_OPTS, cooldown) {
1638
2539
  return downloadPackagesImpl(
1639
2540
  packages,
1640
2541
  targetDir,
@@ -1643,10 +2544,11 @@ async function downloadPackagesWithResolution(packages, targetDir, logger, useCa
1643
2544
  configDir,
1644
2545
  tmpDir,
1645
2546
  overrides,
1646
- npmConfig
2547
+ npmConfig,
2548
+ cooldown
1647
2549
  );
1648
2550
  }
1649
- async function downloadPackagesImpl(packages, targetDir, logger, useCache, configDir, tmpDir, overrides, npmConfig) {
2551
+ async function downloadPackagesImpl(packages, targetDir, logger, useCache, configDir, tmpDir, overrides, npmConfig, cooldown) {
1650
2552
  const packagePaths = /* @__PURE__ */ new Map();
1651
2553
  const userSpecifiedPackages = new Set(packages.map((p) => p.name));
1652
2554
  validateNoDuplicatePackages(packages);
@@ -1656,11 +2558,30 @@ async function downloadPackagesImpl(packages, targetDir, logger, useCache, confi
1656
2558
  logger,
1657
2559
  configDir,
1658
2560
  overrides,
1659
- npmConfig
2561
+ npmConfig,
2562
+ cooldown
2563
+ );
2564
+ const resolution = await resolveVersionConflicts(
2565
+ allSpecs,
2566
+ logger,
2567
+ npmConfig,
2568
+ cooldown
1660
2569
  );
1661
- const resolution = await resolveVersionConflicts(allSpecs, logger, npmConfig);
1662
2570
  const { topLevel, nested } = resolution;
1663
2571
  await fs10.ensureDir(targetDir);
2572
+ if (cooldown) {
2573
+ const tree = await installResolvedTree(
2574
+ packages,
2575
+ resolution,
2576
+ targetDir,
2577
+ logger,
2578
+ { npmConfig, cooldown, tmpDir }
2579
+ );
2580
+ for (const name of topLevel.keys()) {
2581
+ packagePaths.set(name, getPackageDirectory(targetDir, name));
2582
+ }
2583
+ return { packagePaths, resolution, tree };
2584
+ }
1664
2585
  const localPackageMap = /* @__PURE__ */ new Map();
1665
2586
  for (const pkg of packages) {
1666
2587
  if (pkg.path) localPackageMap.set(pkg.name, pkg.path);
@@ -1721,7 +2642,7 @@ async function downloadPackagesImpl(packages, targetDir, logger, useCache, confi
1721
2642
  }
1722
2643
  for (const nestedPkg of nested) {
1723
2644
  let resolvedSpec = `${nestedPkg.name}@${nestedPkg.version}`;
1724
- if (!semver2.valid(nestedPkg.version)) {
2645
+ if (!semver3.valid(nestedPkg.version)) {
1725
2646
  try {
1726
2647
  const manifest = await manifestWithResilience(
1727
2648
  resolvedSpec,
@@ -1760,6 +2681,169 @@ async function downloadPackagesImpl(packages, targetDir, logger, useCache, confi
1760
2681
  }
1761
2682
  return { packagePaths, resolution };
1762
2683
  }
2684
+ function npmCacheDir(tmpDir) {
2685
+ return process.env.NPM_CACHE_DIR || getTmpPath(tmpDir, "cache", "npm");
2686
+ }
2687
+ function sha512Of(integrity) {
2688
+ return integrity?.split(/\s+/).find((value) => value.startsWith("sha512-")) ?? null;
2689
+ }
2690
+ async function readInstalledManifest(dir) {
2691
+ const manifest = await fs10.readJson(
2692
+ path10.join(dir, "package.json")
2693
+ );
2694
+ return manifest;
2695
+ }
2696
+ async function installResolvedTree(packages, resolution, targetDir, logger, context2) {
2697
+ const { npmConfig, cooldown, tmpDir } = context2;
2698
+ const cacheDir = npmCacheDir(tmpDir);
2699
+ const entries = {};
2700
+ const install = async (installName, spec, exempt, installPath) => {
2701
+ let picked;
2702
+ try {
2703
+ picked = await manifestWithResilience(
2704
+ `${installName}@${spec}`,
2705
+ cooldownOptions(npmConfig, cooldown, exempt),
2706
+ logger
2707
+ );
2708
+ if (!exempt) assertPublishedBefore(picked, cooldown, installPath);
2709
+ } catch (error) {
2710
+ throw cooldownFailure(installName, spec, installPath, cooldown, error);
2711
+ }
2712
+ if (aliasCrossesExemption(installName, picked.name, cooldown.exempt)) {
2713
+ throw new PackageTreeError([
2714
+ `${installPath}: ${installName} holds ${picked.name}, across an exempt prefix`
2715
+ ]);
2716
+ }
2717
+ const resolved = picked._resolved;
2718
+ const registryIntegrity = picked._integrity || void 0;
2719
+ const dir = path10.join(targetDir, installPath);
2720
+ await fs10.ensureDir(path10.dirname(dir));
2721
+ const result = await extractWithResilience(
2722
+ resolved,
2723
+ dir,
2724
+ {
2725
+ ...npmConfig,
2726
+ cache: cacheDir,
2727
+ ...registryIntegrity ? { integrity: registryIntegrity } : {}
2728
+ },
2729
+ logger
2730
+ );
2731
+ let integrity = sha512Of(result.integrity) ?? sha512Of(registryIntegrity);
2732
+ if (integrity === null) {
2733
+ const bytes = await tarballWithResilience(resolved, {
2734
+ ...npmConfig,
2735
+ cache: cacheDir,
2736
+ ...registryIntegrity ? { integrity: registryIntegrity } : {}
2737
+ });
2738
+ integrity = `sha512-${createHash3("sha512").update(bytes).digest("base64")}`;
2739
+ }
2740
+ const edges = followedEdges(await readInstalledManifest(dir));
2741
+ entries[installPath] = {
2742
+ name: picked.name,
2743
+ version: picked.version,
2744
+ resolved,
2745
+ integrity,
2746
+ ...Object.keys(edges).length > 0 ? { dependencies: edges } : {}
2747
+ };
2748
+ logger.debug(
2749
+ `Installed ${picked.name}@${picked.version} at ${installPath}`
2750
+ );
2751
+ };
2752
+ for (const [name, pkg] of resolution.topLevel) {
2753
+ if (pkg.localPath) {
2754
+ throw new PackageTreeError([
2755
+ `${name}: a local package cannot be part of a recorded tree`
2756
+ ]);
2757
+ }
2758
+ await install(
2759
+ name,
2760
+ pkg.version,
2761
+ pkg.exempt === true,
2762
+ `node_modules/${name}`
2763
+ );
2764
+ }
2765
+ for (const nestedPkg of resolution.nested) {
2766
+ for (const consumer of nestedPkg.consumers) {
2767
+ await install(
2768
+ nestedPkg.name,
2769
+ nestedPkg.version,
2770
+ nestedPkg.exempt === true,
2771
+ `node_modules/${consumer}/node_modules/${nestedPkg.name}`
2772
+ );
2773
+ }
2774
+ }
2775
+ const tree = {
2776
+ version: PACKAGE_TREE_VERSION,
2777
+ registry: npmConfig.registry,
2778
+ before: cooldown.before.toISOString(),
2779
+ packages: entries
2780
+ };
2781
+ return assertPackageTree(tree, {
2782
+ registry: npmConfig.registry,
2783
+ scopeRegistries: scopeRegistriesOf(npmConfig),
2784
+ declared: Object.fromEntries(
2785
+ packages.map((pkg) => [pkg.name, pkg.version])
2786
+ ),
2787
+ exempt: cooldown.exempt
2788
+ });
2789
+ }
2790
+ async function installPackageTree(tree, targetDir, logger, context2) {
2791
+ const { npmConfig, tmpDir, declared } = context2;
2792
+ const checked = assertPackageTree(tree, {
2793
+ registry: npmConfig.registry,
2794
+ scopeRegistries: scopeRegistriesOf(npmConfig),
2795
+ declared
2796
+ });
2797
+ const cacheDir = npmCacheDir(tmpDir);
2798
+ const ordered = Object.entries(checked.packages).sort(
2799
+ ([a], [b]) => a.split("/").length - b.split("/").length || a.localeCompare(b)
2800
+ );
2801
+ for (const [installPath, entry] of ordered) {
2802
+ const dir = path10.join(targetDir, installPath);
2803
+ await fs10.ensureDir(path10.dirname(dir));
2804
+ try {
2805
+ await extractWithResilience(
2806
+ entry.resolved,
2807
+ dir,
2808
+ { ...npmConfig, cache: cacheDir, integrity: entry.integrity },
2809
+ logger
2810
+ );
2811
+ } catch (error) {
2812
+ const code = readErrorCode(error);
2813
+ if (code === void 0 || !UNAVAILABLE_CODES.has(code)) throw error;
2814
+ const found = Reflect.get(Object(error), "found");
2815
+ const reason = code === "EINTEGRITY" ? `integrity ${entry.integrity} recorded, ${found === void 0 ? "other bytes" : String(found)} received` : "the registry does not serve it";
2816
+ throw new PackageUnavailableError(installPath, entry, reason);
2817
+ }
2818
+ }
2819
+ const problems = [];
2820
+ const packagePaths = /* @__PURE__ */ new Map();
2821
+ for (const [installPath, entry] of ordered) {
2822
+ const dir = path10.join(targetDir, installPath);
2823
+ const installed = await readInstalledManifest(dir);
2824
+ if (installed.name !== entry.name || installed.version !== entry.version) {
2825
+ problems.push(
2826
+ `${installPath}: holds ${installed.name}@${installed.version}, recorded ${entry.name}@${entry.version}`
2827
+ );
2828
+ }
2829
+ for (const [dep, spec] of Object.entries(followedEdges(installed))) {
2830
+ const target = edgeTargetName(dep, spec);
2831
+ const found = lookupPaths(installPath, dep).find(
2832
+ (candidate) => checked.packages[candidate] !== void 0
2833
+ );
2834
+ if (target === null || !found || checked.packages[found].name !== target) {
2835
+ problems.push(
2836
+ `${installPath}: dependency ${dep}@${spec} has no ${target ?? dep} in the tree`
2837
+ );
2838
+ }
2839
+ }
2840
+ const at = parseInstallPath(installPath);
2841
+ if (at && at.consumer === void 0) packagePaths.set(at.name, dir);
2842
+ }
2843
+ if (problems.length > 0) throw new PackageTreeError(problems);
2844
+ logger.debug(`Installed ${ordered.length} packages from the recorded tree`);
2845
+ return packagePaths;
2846
+ }
1763
2847
  async function getCachedPackagePath(pkg, tmpDir) {
1764
2848
  const cacheDir = getTmpPath(tmpDir, "cache", "packages");
1765
2849
  const cacheKey = await getPackageCacheKey(pkg.name, pkg.version);
@@ -1852,6 +2936,27 @@ function assertDepsTraced(opts) {
1852
2936
  (dep) => !opts.importedPackages.has(dep) && !isTraced(dep)
1853
2937
  );
1854
2938
  }
2939
+ async function missingEntry(packageDir) {
2940
+ let manifest;
2941
+ try {
2942
+ manifest = JSON.parse(
2943
+ await fs11.readFile(path11.join(packageDir, "package.json"), "utf8")
2944
+ );
2945
+ } catch {
2946
+ return void 0;
2947
+ }
2948
+ if (!manifest || typeof manifest !== "object") return void 0;
2949
+ const entries = ["main", "module", "exports"].flatMap((field) => stringLeaves(Reflect.get(manifest, field))).filter((entry) => !entry.includes("*") && !entry.endsWith("package.json"));
2950
+ if (entries.length === 0) return void 0;
2951
+ for (const entry of entries) {
2952
+ try {
2953
+ await fs11.access(path11.join(packageDir, entry));
2954
+ return void 0;
2955
+ } catch {
2956
+ }
2957
+ }
2958
+ return entries[0];
2959
+ }
1855
2960
  async function collectImportedPackages(file) {
1856
2961
  let result;
1857
2962
  try {
@@ -1923,7 +3028,7 @@ async function walkFiles(root, dir, visit) {
1923
3028
  // src/commands/bundle/assert-consumer-deps.ts
1924
3029
  import fs12 from "fs-extra";
1925
3030
  import * as path12 from "path";
1926
- import semver3 from "semver";
3031
+ import semver4 from "semver";
1927
3032
  async function assertConsumerDepsSatisfied(root, logger) {
1928
3033
  const consumers = await collectInstalledPackages(
1929
3034
  path12.join(root, "node_modules")
@@ -1944,13 +3049,13 @@ async function assertConsumerDepsSatisfied(root, logger) {
1944
3049
  );
1945
3050
  continue;
1946
3051
  }
1947
- if (semver3.validRange(declaredRange) === null && semver3.valid(declaredRange) === null) {
3052
+ if (semver4.validRange(declaredRange) === null && semver4.valid(declaredRange) === null) {
1948
3053
  logger.debug(
1949
3054
  `assertConsumerDepsSatisfied: skipping non-semver declared range ${depName}@${declaredRange} from ${pkg.name}`
1950
3055
  );
1951
3056
  continue;
1952
3057
  }
1953
- const satisfied = semver3.valid(declaredRange) ? declaredRange === installed.version : semver3.satisfies(installed.version, declaredRange, {
3058
+ const satisfied = semver4.valid(declaredRange) ? declaredRange === installed.version : semver4.satisfies(installed.version, declaredRange, {
1954
3059
  includePrerelease: true
1955
3060
  });
1956
3061
  if (!satisfied) {
@@ -2030,22 +3135,22 @@ function flowNeeds(flow) {
2030
3135
  ...Object.values(flow.destinations ?? {})
2031
3136
  ];
2032
3137
  return {
2033
- observe: flow.config?.observe !== void 0 || flow.collector?.observe !== void 0,
3138
+ observe: false,
2034
3139
  stores: Object.keys(flow.stores ?? {}).length > 0,
2035
3140
  state: steps.some(carriesState)
2036
3141
  };
2037
3142
  }
2038
- function buildFlagDefines(needs) {
3143
+ function buildFlagDefines(needs, observe) {
2039
3144
  if (!needs) {
2040
3145
  return {
2041
- __WALKEROS_OBSERVE__: "true",
3146
+ __WALKEROS_OBSERVE__: String(observe),
2042
3147
  __WALKEROS_STORES__: "true",
2043
3148
  __WALKEROS_STATE__: "true",
2044
3149
  __WALKEROS_VALIDATE__: "true"
2045
3150
  };
2046
3151
  }
2047
3152
  return {
2048
- __WALKEROS_OBSERVE__: String(needs.observe),
3153
+ __WALKEROS_OBSERVE__: String(observe),
2049
3154
  __WALKEROS_STORES__: String(needs.stores),
2050
3155
  __WALKEROS_STATE__: String(needs.state),
2051
3156
  __WALKEROS_VALIDATE__: "false"
@@ -2148,21 +3253,21 @@ async function ensureCodeOnDisk(codeContent, compiledCode, tmpDir, inputs) {
2148
3253
  }
2149
3254
 
2150
3255
  // src/version.ts
2151
- import { readFileSync } from "fs";
3256
+ import { readFileSync as readFileSync3 } from "fs";
2152
3257
  import { fileURLToPath } from "url";
2153
- import { dirname as dirname3, join as join3 } from "path";
3258
+ import { dirname as dirname3, join as join5 } from "path";
2154
3259
  var versionFilename = fileURLToPath(import.meta.url);
2155
3260
  var versionDirname = dirname3(versionFilename);
2156
3261
  function findPackageJson() {
2157
3262
  const paths = [
2158
- join3(versionDirname, "../package.json"),
3263
+ join5(versionDirname, "../package.json"),
2159
3264
  // dist/ or src/
2160
- join3(versionDirname, "../../package.json")
3265
+ join5(versionDirname, "../../package.json")
2161
3266
  // src/core/ (not used, but safe)
2162
3267
  ];
2163
3268
  for (const p of paths) {
2164
3269
  try {
2165
- return readFileSync(p, "utf-8");
3270
+ return readFileSync3(p, "utf-8");
2166
3271
  } catch {
2167
3272
  }
2168
3273
  }
@@ -2268,6 +3373,46 @@ async function resolvedVersionLines(topLevel, packagePaths) {
2268
3373
  }
2269
3374
  return lines;
2270
3375
  }
3376
+ async function installPackages(packagesArray, targetDir, cacheDir, buildOptions, npmConfig, logger) {
3377
+ const install = buildOptions.packageInstall;
3378
+ if (install && "tree" in install) {
3379
+ const packagePaths2 = await installPackageTree(
3380
+ install.tree,
3381
+ targetDir,
3382
+ logger,
3383
+ {
3384
+ npmConfig,
3385
+ tmpDir: cacheDir,
3386
+ declared: Object.fromEntries(
3387
+ packagesArray.map((pkg) => [pkg.name, pkg.version])
3388
+ )
3389
+ }
3390
+ );
3391
+ return { packagePaths: packagePaths2, versionLines: treeVersionLines(install.tree) };
3392
+ }
3393
+ const { packagePaths, resolution, tree } = await downloadPackagesWithResolution(
3394
+ packagesArray,
3395
+ targetDir,
3396
+ logger,
3397
+ // A recorded tree is built from what was extracted, never a cache copy.
3398
+ install ? false : buildOptions.cache,
3399
+ buildOptions.configDir,
3400
+ // For resolving relative local paths
3401
+ cacheDir,
3402
+ buildOptions.overrides,
3403
+ npmConfig,
3404
+ install?.resolve
3405
+ );
3406
+ if (install) {
3407
+ if (!tree) throw new Error("A resolve under a cutoff returned no tree");
3408
+ install.onTree(tree);
3409
+ return { packagePaths, versionLines: treeVersionLines(tree) };
3410
+ }
3411
+ return {
3412
+ packagePaths,
3413
+ versionLines: await resolvedVersionLines(resolution.topLevel, packagePaths)
3414
+ };
3415
+ }
2271
3416
  async function bundleCore(flowSettings, buildOptions, logger, showStats = false, provenance) {
2272
3417
  const bundleStartTime = Date.now();
2273
3418
  const CACHE_DIR = buildOptions.tempDir || getTmpPath();
@@ -2296,16 +3441,13 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false,
2296
3441
  // Pass local path if defined
2297
3442
  })
2298
3443
  );
2299
- const { packagePaths, resolution: resolutionResult } = await downloadPackagesWithResolution(
3444
+ const { packagePaths, versionLines } = await installPackages(
2300
3445
  packagesArray,
2301
3446
  TEMP_DIR,
2302
- logger,
2303
- buildOptions.cache,
2304
- buildOptions.configDir,
2305
- // For resolving relative local paths
2306
3447
  CACHE_DIR,
2307
- buildOptions.overrides,
2308
- npmConfig
3448
+ buildOptions,
3449
+ npmConfig,
3450
+ logger
2309
3451
  );
2310
3452
  const warnedBundleFieldPackages = /* @__PURE__ */ new Set();
2311
3453
  for (const [pkgName, pkgPath] of packagePaths.entries()) {
@@ -2344,10 +3486,7 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false,
2344
3486
  if (process.env.BUNDLER_STRICT_RANGES !== "0") {
2345
3487
  await assertConsumerDepsSatisfied(TEMP_DIR, logger);
2346
3488
  }
2347
- const sortedVersions = await resolvedVersionLines(
2348
- resolutionResult.topLevel,
2349
- packagePaths
2350
- );
3489
+ const sortedVersions = versionLines;
2351
3490
  const versionsHash = await getHashServer3(sortedVersions.join("\n"), 12);
2352
3491
  applyCollectorDefaults(flowSettings);
2353
3492
  if (provenance) {
@@ -2497,8 +3636,7 @@ ${dataDeclaration}${needs}`;
2497
3636
  const stage2Entry = (buildOptions.platform || "node") === "browser" ? generateWebEntry(stage1Path, dataPayload, {
2498
3637
  windowCollector: buildOptions.windowCollector,
2499
3638
  windowElb: buildOptions.windowElb,
2500
- platform: buildOptions.platform,
2501
- observe: readObserveConnect(flowSettings, logger)
3639
+ platform: buildOptions.platform
2502
3640
  }) : generateServerEntry(stage1Path, dataPayload);
2503
3641
  const stage2EntryPath = path14.join(TEMP_DIR, "stage2.mjs");
2504
3642
  await fs14.writeFile(stage2EntryPath, stage2Entry);
@@ -2525,7 +3663,7 @@ ${dataDeclaration}${needs}`;
2525
3663
  stage2Options.define = {
2526
3664
  "process.env.NODE_ENV": '"production"',
2527
3665
  global: "globalThis",
2528
- ...buildFlagDefines(flowNeeds(flowSettings))
3666
+ ...buildFlagDefines(flowNeeds(flowSettings), false)
2529
3667
  };
2530
3668
  stage2Options.target = resolveTarget(buildOptions);
2531
3669
  } else {
@@ -2742,8 +3880,11 @@ async function runNftServerPath(outputPath, flowSettings, buildOptions, tempDir,
2742
3880
  importedPackages: await collectImportedPackages(outputPath)
2743
3881
  });
2744
3882
  for (const name of unused) {
3883
+ const entry = await missingEntry(
3884
+ path14.join(tempDir, "node_modules", name)
3885
+ );
2745
3886
  logger.warn(
2746
- `Package ${name} is declared in bundle.packages but nothing imports it; it is not in the bundle. Remove it from bundle.packages if unused.`
3887
+ entry ? `Package ${name} is declared in bundle.packages, but its installed copy has no ${entry}; it is not in the bundle.` : `Package ${name} is declared in bundle.packages but nothing imports it; it is not in the bundle. Remove it from bundle.packages if unused.`
2747
3888
  );
2748
3889
  }
2749
3890
  }
@@ -3197,66 +4338,12 @@ ${HOST_LOGGER_MERGE}
3197
4338
 
3198
4339
  const result = await startFlow(config);
3199
4340
 
3200
- // Telemetry observer installation: the host (pipeline.ts) builds the
3201
- // observer functions via createTelemetryObserver + createBatchedPoster
3202
- // and forwards them through context. Added to collector.observers so the
3203
- // runtime self-emission loop drives them.
3204
- if (context.observers) {
3205
- for (const observer of context.observers) {
3206
- result.collector.observers.add(observer);
3207
- }
3208
- }
3209
-
3210
- // The host forwards the level supplier beside the observers; the collector
3211
- // capture path reads it to decide destination call capture at trace.
3212
- if (context.observeLevel) result.collector.observeLevel = context.observeLevel;
3213
-
3214
4341
  const httpSource = Object.values(result.collector.sources || {})
3215
4342
  .find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
3216
4343
 
3217
4344
  return { ...result, httpHandler: httpSource ? httpSource.httpHandler : undefined };
3218
4345
  }`;
3219
4346
  }
3220
- function readObserveConnect(flowSettings, logger) {
3221
- const observe = flowSettings.config?.observe;
3222
- if (!observe) return void 0;
3223
- const { url, binding, level, sample } = observe;
3224
- if (url === void 0 && binding === void 0) return void 0;
3225
- const trimmedUrl = typeof url === "string" ? url.trim() : "";
3226
- const trimmedBinding = typeof binding === "string" ? binding.trim() : "";
3227
- if (trimmedUrl !== "" && trimmedBinding !== "") {
3228
- return {
3229
- url: trimmedUrl,
3230
- binding: trimmedBinding,
3231
- // Same JSON-borne prudence as the pair: only known-shaped public
3232
- // values reach the emitted literal.
3233
- ...level === "off" || level === "standard" || level === "trace" ? { level } : {},
3234
- // Sample is a fraction in [0, 1] per the Flow.Observe contract; the
3235
- // range test also drops NaN and the infinities.
3236
- ...typeof sample === "number" && sample >= 0 && sample <= 1 ? { sample } : {}
3237
- };
3238
- }
3239
- const missing = [
3240
- ...trimmedUrl === "" ? ["url"] : [],
3241
- ...trimmedBinding === "" ? ["binding"] : []
3242
- ].join(" and ");
3243
- logger?.warn(
3244
- `config.observe has no effect: missing or empty ${missing}. Observe connect wiring was skipped; set both config.observe.url and config.observe.binding to non-empty public values.`
3245
- );
3246
- return void 0;
3247
- }
3248
- var OBSERVE_SLOT_IMPORT = "import { pageObserveSlot } from '@walkeros/web-core';\n";
3249
- function emitObserveAssignment(observe) {
3250
- const literal = {
3251
- url: observe.url,
3252
- binding: observe.binding,
3253
- ...observe.flowId !== void 0 ? { flowId: observe.flowId } : {},
3254
- ...observe.level !== void 0 ? { level: observe.level } : {},
3255
- ...observe.sample !== void 0 ? { sample: observe.sample } : {}
3256
- };
3257
- return `
3258
- config.observe = { ...${JSON.stringify(literal)}, slot: pageObserveSlot() };`;
3259
- }
3260
4347
  var MOIN_IMPORT = "import { moin } from '@walkeros/web-core';\n";
3261
4348
  function emitMoinCall(target) {
3262
4349
  if (!target)
@@ -3305,16 +4392,14 @@ function generateWebEntry(stage1Path, dataPayload, options = {}) {
3305
4392
  }
3306
4393
  }` : "";
3307
4394
  const stage1Specifier = toFileImportSpecifier(stage1Path);
3308
- const observeImport = options.observe ? OBSERVE_SLOT_IMPORT : "";
3309
- const observeBlock = options.observe ? emitObserveAssignment(options.observe) : "";
3310
4395
  const loaderImport = platform === "browser" ? MOIN_IMPORT : "";
3311
4396
  const loaderCall = platform === "browser" ? emitMoinCall() : "";
3312
- return `${loaderImport}${observeImport}import { startFlow, wireConfig } from '${stage1Specifier}';
4397
+ return `${loaderImport}import { startFlow, wireConfig } from '${stage1Specifier}';
3313
4398
 
3314
4399
  const __configData = ${dataPayload};
3315
4400
 
3316
4401
  (async () => {${loaderCall}
3317
- const config = wireConfig(__configData);${envBlock}${observeBlock}
4402
+ const config = wireConfig(__configData);${envBlock}
3318
4403
  const { collector, elb } = await startFlow(config);${assignmentCode}
3319
4404
  })();`;
3320
4405
  }
@@ -3326,39 +4411,10 @@ function generateWrapEntry(stage1Path, options = {}) {
3326
4411
  );
3327
4412
  }
3328
4413
  const assignmentCode = assignments.length > 0 ? "\n" + assignments.join("\n") : "";
3329
- if (options.preview !== void 0 && options.previewGrantTargets !== void 0) {
3330
- throw new Error(
3331
- "generateWrapEntry: `preview` (host activator) and `previewGrantTargets` (preview-artifact injection) are mutually exclusive. A host bundle activates a preview; an artifact injects its grant. Pass at most one."
3332
- );
3333
- }
3334
4414
  const preview = options.preview;
3335
4415
  const previewEnabled = !!preview?.enabled;
3336
4416
  const previewImport = previewEnabled ? `import { browserSwapActivator } from '@walkeros/web-core';
3337
4417
  ` : "";
3338
- const grantTargets = options.previewGrantTargets ?? [];
3339
- const previewGrantBlock = grantTargets.length > 0 ? `
3340
- // --- Preview grant injection ---
3341
- try {
3342
- const __previewGrant =
3343
- typeof localStorage !== 'undefined'
3344
- ? localStorage.getItem('elbPreviewSession')
3345
- : null;
3346
- if (__previewGrant && config.destinations) {
3347
- ${grantTargets.map((key) => {
3348
- const ref = `config.destinations[${singleQuoteLiteral(key)}]`;
3349
- return ` if (${ref}) {
3350
- const __dest = ${ref};
3351
- const __cfg = __dest.config || (__dest.config = {});
3352
- const __settings = __cfg.settings || (__cfg.settings = {});
3353
- const __headers = __settings.headers || (__settings.headers = {});
3354
- __headers['X-Walkeros-Preview'] = __previewGrant;
3355
- }`;
3356
- }).join("\n")}
3357
- }
3358
- } catch (__err) {
3359
- // localStorage can throw (Safari private mode); boot without the grant.
3360
- }
3361
- // --- End preview grant injection ---` : "";
3362
4418
  const previewBlock = previewEnabled && preview ? `
3363
4419
  // --- Preview activation ---
3364
4420
  if (await browserSwapActivator(${JSON.stringify({
@@ -3382,21 +4438,24 @@ ${grantTargets.map((key) => {
3382
4438
  env.document = env.document ?? (typeof document !== 'undefined' ? document : undefined);
3383
4439
  }
3384
4440
  }` : "";
4441
+ const observe = options.observe && {
4442
+ preview: {
4443
+ url: options.observe.preview.url,
4444
+ token: options.observe.preview.token
4445
+ }
4446
+ };
4447
+ const observeBlock = observe ? `
4448
+ config.observe = ${JSON.stringify(observe)};` : "";
3385
4449
  const stage1Specifier = toFileImportSpecifier(stage1Path);
3386
- const observeImport = options.observe ? OBSERVE_SLOT_IMPORT : "";
3387
- const observeBlock = options.observe ? emitObserveAssignment(options.observe) : "";
3388
4450
  const loaderImport = platform === "browser" ? MOIN_IMPORT : "";
3389
4451
  const loaderCall = platform === "browser" ? emitMoinCall(options.moin) : "";
3390
- return `${loaderImport}${previewImport}${observeImport}import { startFlow, wireConfig, __configData } from '${stage1Specifier}';
4452
+ return `${loaderImport}${previewImport}import { startFlow, wireConfig, __configData } from '${stage1Specifier}';
3391
4453
 
3392
4454
  (async () => {${loaderCall}${previewBlock}
3393
- const config = wireConfig(__configData);${envBlock}${previewGrantBlock}${observeBlock}
4455
+ const config = wireConfig(__configData);${envBlock}${observeBlock}
3394
4456
  const { collector, elb } = await startFlow(config);${assignmentCode}
3395
4457
  })();`;
3396
4458
  }
3397
- function singleQuoteLiteral(value) {
3398
- return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
3399
- }
3400
4459
  function generateWrapEntryServer(stage1Path) {
3401
4460
  const stage1Specifier = toFileImportSpecifier(stage1Path);
3402
4461
  return `import { startFlow, wireConfig, __configData } from '${stage1Specifier}';
@@ -3416,20 +4475,6 @@ ${HOST_LOGGER_MERGE}
3416
4475
 
3417
4476
  const result = await startFlow(config);
3418
4477
 
3419
- // Telemetry observer installation: the host (pipeline.ts) builds the
3420
- // observer functions via createTelemetryObserver + createBatchedPoster
3421
- // and forwards them through context. Added to collector.observers so the
3422
- // runtime self-emission loop drives them.
3423
- if (context.observers) {
3424
- for (const observer of context.observers) {
3425
- result.collector.observers.add(observer);
3426
- }
3427
- }
3428
-
3429
- // The host forwards the level supplier beside the observers; the collector
3430
- // capture path reads it to decide destination call capture at trace.
3431
- if (context.observeLevel) result.collector.observeLevel = context.observeLevel;
3432
-
3433
4478
  const httpSource = Object.values(result.collector.sources || {})
3434
4479
  .find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
3435
4480
 
@@ -4077,6 +5122,7 @@ function applyOverrides(config, overrides) {
4077
5122
  }
4078
5123
  }
4079
5124
  config.run = true;
5125
+ delete config.observe;
4080
5126
  if (overrides.transformerMocks) {
4081
5127
  const transformers = config.transformers;
4082
5128
  if (transformers) {
@@ -5311,9 +6357,11 @@ function collectFailedDestinations(collector) {
5311
6357
  }
5312
6358
  function buildFailureSummary(failedIds, collector) {
5313
6359
  const view = collector !== null && typeof collector === "object" ? collector : {};
5314
- const dests = view.destinations ?? {};
6360
+ const dests = Object.entries(view.destinations ?? {});
5315
6361
  const labels = failedIds.map((id) => {
5316
- const type = dests[id]?.type;
6362
+ const type = dests.find(
6363
+ ([key, dest]) => (dest.config?.id || key) === id
6364
+ )?.[1].type;
5317
6365
  return type ? `${id} (${type})` : id;
5318
6366
  });
5319
6367
  const noun = failedIds.length === 1 ? "destination" : "destinations";
@@ -6515,7 +7563,7 @@ async function readStepPackageInfo(resolved, options) {
6515
7563
  }
6516
7564
 
6517
7565
  // src/commands/validate/validators/settings.ts
6518
- var CLIENT_HEADER = "walkeros-cli/4.9.0";
7566
+ var CLIENT_HEADER = "walkeros-cli/4.10.0-next-1794522704623";
6519
7567
  var LOGGER = createLogger();
6520
7568
  function createSettingsContext(configDir) {
6521
7569
  return { configDir, cache: /* @__PURE__ */ new Map(), compiled: /* @__PURE__ */ new Map() };
@@ -7988,7 +9036,7 @@ function exitCodeOf(result, strict) {
7988
9036
  }
7989
9037
 
7990
9038
  // src/commands/auth/index.ts
7991
- import { z as z3 } from "zod";
9039
+ import { z as z6 } from "zod";
7992
9040
 
7993
9041
  // src/core/api-error.ts
7994
9042
  var EXIT_RETRYABLE = 75;
@@ -8017,191 +9065,57 @@ var ApiError = class extends Error {
8017
9065
  this.client = options?.client;
8018
9066
  this.upgrade = options?.upgrade;
8019
9067
  this.docs = options?.docs;
8020
- }
8021
- };
8022
- function machineReadableErrorLine(err) {
8023
- const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
8024
- const retryable = err instanceof ApiError ? err.retryable === true : false;
8025
- const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
8026
- const message = err instanceof Error ? err.message : String(err);
8027
- return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
8028
- }
8029
- function handleCliError(err) {
8030
- console.error(machineReadableErrorLine(err));
8031
- if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
8032
- console.error(`
8033
- ${err.message}
8034
- `);
8035
- if (err.upgrade) console.error(` Upgrade: ${err.upgrade}`);
8036
- if (err.docs) console.error(` Docs: ${err.docs}
8037
- `);
8038
- process.exit(2);
8039
- }
8040
- if (err instanceof ApiError && err.retryable) {
8041
- const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
8042
- console.error(`${err.message}${hint}`);
8043
- process.exit(EXIT_RETRYABLE);
8044
- }
8045
- console.error(err instanceof Error ? err.message : String(err));
8046
- process.exit(1);
8047
- }
8048
-
8049
- // src/core/app-fetch.ts
8050
- import { z } from "zod";
8051
-
8052
- // src/lib/config-file.ts
8053
- import {
8054
- readFileSync as readFileSync3,
8055
- writeFileSync,
8056
- mkdirSync,
8057
- unlinkSync,
8058
- chmodSync,
8059
- renameSync
8060
- } from "fs";
8061
- import { randomBytes } from "crypto";
8062
- import { join as join5 } from "path";
8063
- import { homedir } from "os";
8064
- function getConfigDir() {
8065
- const xdgConfig = process.env.XDG_CONFIG_HOME;
8066
- const base = xdgConfig || join5(homedir(), ".config");
8067
- return join5(base, "walkeros");
8068
- }
8069
- function getConfigPath() {
8070
- return join5(getConfigDir(), "config.json");
8071
- }
8072
- function readConfig() {
8073
- const configPath = getConfigPath();
8074
- try {
8075
- const content = readFileSync3(configPath, "utf-8");
8076
- return JSON.parse(content);
8077
- } catch {
8078
- return null;
8079
- }
8080
- }
8081
- function replaceConfigFile(config) {
8082
- const dir = getConfigDir();
8083
- mkdirSync(dir, { recursive: true, mode: 448 });
8084
- const configPath = getConfigPath();
8085
- const tempPath = `${configPath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
8086
- try {
8087
- writeFileSync(tempPath, JSON.stringify(config, null, 2), { mode: 384 });
8088
- chmodSync(tempPath, 384);
8089
- renameSync(tempPath, configPath);
8090
- } catch (error) {
8091
- try {
8092
- unlinkSync(tempPath);
8093
- } catch {
8094
- }
8095
- throw error;
8096
- }
8097
- }
8098
- function writeConfig(config) {
8099
- replaceConfigFile({ ...readConfig() ?? {}, ...config });
8100
- }
8101
- function writeTelemetryOnlyConfig(partial) {
8102
- writeConfig(partial);
8103
- }
8104
- function resolveAppUrl() {
8105
- const envUrl = process.env.WALKEROS_APP_URL;
8106
- if (envUrl) return envUrl;
8107
- const config = readConfig();
8108
- if (config?.appUrl) return config.appUrl;
8109
- return "https://app.walkeros.io";
8110
- }
8111
-
8112
- // src/lib/secure-url.ts
8113
- function isLoopback(hostname) {
8114
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
8115
- }
8116
- function requireSecureUrl(url) {
8117
- let parsed;
8118
- try {
8119
- parsed = new URL(url);
8120
- } catch {
8121
- return url;
8122
- }
8123
- if (parsed.protocol !== "http:" || isLoopback(parsed.hostname)) return url;
8124
- throw new Error(
8125
- `Refusing to send walkerOS credentials over plain http to ${url}. Use https, or localhost / 127.0.0.1 / [::1] for local development.`
8126
- );
8127
- }
8128
-
8129
- // src/core/client-context.ts
8130
- var context;
8131
- function setClientContext(input) {
8132
- const envType = process.env.WALKEROS_CLIENT_TYPE;
8133
- const type = envType ?? input.type ?? "cli";
8134
- context = { type, version: input.version };
8135
- }
8136
- function getClientContext() {
8137
- return context;
8138
- }
8139
- function resetClientContext() {
8140
- context = void 0;
8141
- }
8142
- function clientContextHeaders() {
8143
- if (!context) return {};
8144
- return {
8145
- "User-Agent": `walkeros-${context.type}/${context.version}`,
8146
- "X-WalkerOS-Client": context.type,
8147
- "X-WalkerOS-Client-Version": context.version
8148
- };
8149
- }
8150
-
8151
- // src/core/app-fetch.ts
8152
- var AppErrorSchema = z.object({
8153
- error: z.object({ message: z.string().min(1) })
8154
- });
8155
- function appErrorMessage(body) {
8156
- const parsed = AppErrorSchema.safeParse(body);
8157
- return parsed.success ? parsed.data.error.message : void 0;
9068
+ }
9069
+ };
9070
+ function machineReadableErrorLine(err) {
9071
+ const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
9072
+ const retryable = err instanceof ApiError ? err.retryable === true : false;
9073
+ const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
9074
+ const message = err instanceof Error ? err.message : String(err);
9075
+ return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
8158
9076
  }
8159
- async function appFetch(path23, init = {}) {
8160
- const url = `${(init.baseUrl ?? resolveAppUrl()).replace(/\/+$/, "")}${path23}`;
8161
- const headers = new Headers(clientContextHeaders());
8162
- for (const [name, value] of Object.entries(init.headers ?? {}))
8163
- headers.set(name, value);
8164
- if (init.token && !headers.has("Authorization"))
8165
- headers.set("Authorization", `Bearer ${init.token}`);
8166
- if (headers.has("Authorization")) requireSecureUrl(url);
8167
- const request = { headers };
8168
- if (init.method && init.method !== "GET") request.method = init.method;
8169
- if (init.body !== void 0) request.body = init.body;
8170
- if (init.signal) request.signal = init.signal;
8171
- if (init.redirect) request.redirect = init.redirect;
8172
- return (init.fetch ?? fetch)(url, request);
9077
+ function handleCliError(err) {
9078
+ console.error(machineReadableErrorLine(err));
9079
+ if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
9080
+ console.error(`
9081
+ ${err.message}
9082
+ `);
9083
+ if (err.upgrade) console.error(` Upgrade: ${err.upgrade}`);
9084
+ if (err.docs) console.error(` Docs: ${err.docs}
9085
+ `);
9086
+ process.exit(2);
9087
+ }
9088
+ if (err instanceof ApiError && err.retryable) {
9089
+ const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
9090
+ console.error(`${err.message}${hint}`);
9091
+ process.exit(EXIT_RETRYABLE);
9092
+ }
9093
+ console.error(err instanceof Error ? err.message : String(err));
9094
+ process.exit(1);
8173
9095
  }
8174
9096
 
8175
- // src/core/app-paths.ts
8176
- var APP_PATHS = {
8177
- whoami: "/api/auth/whoami",
8178
- health: "/api/health",
8179
- telemetry: "/api/telemetry",
8180
- openapi: "/api/openapi.json"
8181
- };
8182
-
8183
9097
  // src/commands/api/index.ts
8184
9098
  import { once } from "events";
8185
- import { readFile as readFile2 } from "fs/promises";
8186
- import { z as z2 } from "zod";
8187
- var NOT_SIGNED_IN = "no token: set WALKEROS_TOKEN to a token from Account > Automation tokens in the walkerOS app";
9099
+ import { readFile as readFile3 } from "fs/promises";
9100
+ import { z as z5 } from "zod";
9101
+ var NOT_SIGNED_IN = "not signed in: run walkeros login, or set WALKEROS_TOKEN to a token from Account > Automation tokens in the walkerOS app";
8188
9102
  var ERROR_BODY_LIMIT = 64 * 1024;
8189
- var DocumentSchema = z2.object({
8190
- paths: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())),
8191
- components: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())).optional()
9103
+ var DocumentSchema = z5.object({
9104
+ paths: z5.record(z5.string(), z5.record(z5.string(), z5.unknown())),
9105
+ components: z5.record(z5.string(), z5.record(z5.string(), z5.unknown())).optional()
8192
9106
  });
8193
- var OperationSchema = z2.object({
8194
- summary: z2.string().optional(),
8195
- description: z2.string().optional(),
8196
- parameters: z2.array(z2.unknown()).optional(),
8197
- requestBody: z2.unknown().optional(),
8198
- responses: z2.record(z2.string(), z2.unknown()).optional()
9107
+ var OperationSchema = z5.object({
9108
+ summary: z5.string().optional(),
9109
+ description: z5.string().optional(),
9110
+ parameters: z5.array(z5.unknown()).optional(),
9111
+ requestBody: z5.unknown().optional(),
9112
+ responses: z5.record(z5.string(), z5.unknown()).optional()
8199
9113
  });
8200
- var OptionsSchema = z2.object({
8201
- data: z2.string().optional(),
8202
- header: z2.array(z2.string()).default([]),
8203
- project: z2.string().optional(),
8204
- yes: z2.boolean().optional()
9114
+ var OptionsSchema = z5.object({
9115
+ data: z5.string().optional(),
9116
+ header: z5.array(z5.string()).default([]),
9117
+ project: z5.string().optional(),
9118
+ yes: z5.boolean().optional()
8205
9119
  });
8206
9120
 
8207
9121
  // src/commands/auth/index.ts
@@ -8212,34 +9126,44 @@ async function readJson2(response) {
8212
9126
  return null;
8213
9127
  }
8214
9128
  }
8215
- async function whoami() {
8216
- const token = resolveAccessToken();
8217
- if (!token) throw new Error(NOT_SIGNED_IN);
8218
- const response = await appFetch(APP_PATHS.whoami, { token });
9129
+ async function identify() {
9130
+ const credential = await resolveCredential();
9131
+ if (!credential) throw new Error(NOT_SIGNED_IN);
9132
+ const response = await sendWithCredential(
9133
+ credential,
9134
+ (token) => appFetch(APP_PATHS.whoami, { token })
9135
+ );
8219
9136
  const body = await readJson2(response);
8220
9137
  if (!response.ok)
8221
9138
  throw new Error(appErrorMessage(body) ?? `HTTP ${response.status}`);
8222
- return body;
9139
+ return { body, credential };
9140
+ }
9141
+ async function whoami() {
9142
+ return (await identify()).body;
8223
9143
  }
8224
- var IdentitySchema = z3.object({
8225
- email: z3.string().optional(),
8226
- userId: z3.string().optional()
9144
+ function credentialSourceLine(credential) {
9145
+ return credential.source === "env" ? "Credential: WALKEROS_TOKEN" : `Credential: signed in to ${credential.issuer}`;
9146
+ }
9147
+ var IdentitySchema = z6.object({
9148
+ email: z6.string().optional(),
9149
+ userId: z6.string().optional()
8227
9150
  });
8228
9151
  async function whoamiCommand(options) {
8229
9152
  const logger = createCLILogger(options);
8230
9153
  try {
8231
- const result = await whoami();
9154
+ const { body: result, credential } = await identify();
8232
9155
  if (options.json) {
8233
9156
  await writeResult(JSON.stringify(result, null, 2), options);
8234
9157
  } else {
8235
9158
  const identity = IdentitySchema.safeParse(result);
8236
9159
  if (!identity.success) {
8237
9160
  logger.info(JSON.stringify(result));
8238
- return;
9161
+ } else {
9162
+ const data = identity.data;
9163
+ if (data.email) logger.info(data.email);
9164
+ if (data.userId) logger.info(`User: ${data.userId}`);
8239
9165
  }
8240
- const data = identity.data;
8241
- if (data.email) logger.info(data.email);
8242
- if (data.userId) logger.info(`User: ${data.userId}`);
9166
+ logger.info(credentialSourceLine(credential));
8243
9167
  }
8244
9168
  } catch (error) {
8245
9169
  handleCliError(error);
@@ -8321,6 +9245,188 @@ import * as path21 from "path";
8321
9245
  import { fileURLToPath as fileURLToPath3 } from "url";
8322
9246
  import fs21 from "fs-extra";
8323
9247
  import * as esbuild3 from "esbuild";
9248
+
9249
+ // src/schemas/build-manifest.ts
9250
+ import { z as z7 } from "@walkeros/core/dev";
9251
+ var BUILD_MANIFEST_VERSION = 1;
9252
+ var HttpUrlSchema = z7.url({ protocol: /^https?$/ });
9253
+ var BuildWrapObserveSchema = z7.object({
9254
+ preview: z7.object({ url: HttpUrlSchema, token: z7.string().min(1) }).strict()
9255
+ }).strict();
9256
+ var OutputNameSchema = z7.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "Must be a bare file name").describe("Output file name; its extension picks the format (.tar.gz packs)");
9257
+ var BUNDLE_TARGET_NAMES = [
9258
+ "cdn",
9259
+ "cdn-skeleton",
9260
+ "runner",
9261
+ "simulate",
9262
+ "push"
9263
+ ];
9264
+ var RESERVED_HEADERS = /* @__PURE__ */ new Set([
9265
+ "content-type",
9266
+ "content-length",
9267
+ "host"
9268
+ ]);
9269
+ var OutputFields = {
9270
+ outputName: OutputNameSchema,
9271
+ putUrl: HttpUrlSchema.describe("Presigned PUT for exactly this output"),
9272
+ contentType: z7.string().min(1).optional().describe("Content-Type sent with the PUT (must match a signed header)"),
9273
+ headers: z7.record(z7.string().min(1), z7.string()).refine(
9274
+ (headers) => Object.keys(headers).every(
9275
+ (name) => !RESERVED_HEADERS.has(name.toLowerCase())
9276
+ ),
9277
+ "Content-Type, Content-Length and Host are set by the CLI"
9278
+ ).optional().describe(
9279
+ "Extra headers sent with the PUT (for headers a presign signs); never logged"
9280
+ )
9281
+ };
9282
+ var BuildBundleArtifactSchema = z7.object({
9283
+ target: z7.enum(BUNDLE_TARGET_NAMES).describe("Named bundle target"),
9284
+ ...OutputFields
9285
+ }).strict();
9286
+ var BuildWrapOptionsSchema = z7.object({
9287
+ windowCollector: z7.string().min(1).optional(),
9288
+ windowElb: z7.string().min(1).optional(),
9289
+ preview: z7.object({
9290
+ enabled: z7.boolean(),
9291
+ keyring: z7.array(
9292
+ z7.object({ kid: z7.string(), spki: z7.string() }).strict()
9293
+ ),
9294
+ iss: z7.string(),
9295
+ pb: z7.string().optional(),
9296
+ acceptForeign: z7.boolean().optional(),
9297
+ demoAllowlist: z7.array(z7.string()).optional(),
9298
+ previewOrigin: z7.string()
9299
+ }).strict().optional(),
9300
+ moin: z7.object({ app: z7.string(), base: z7.string() }).strict().optional().describe("Tag Mode loader target; absent, the production app"),
9301
+ observe: BuildWrapObserveSchema.optional().describe(
9302
+ "Browser only: Observe preview reporting, the preview route and its token. It replaces the flow's own collector.observe; a browser build never carries usage"
9303
+ ),
9304
+ minify: z7.boolean().optional(),
9305
+ minifyOptions: z7.object({
9306
+ identifiers: z7.boolean().optional(),
9307
+ syntax: z7.boolean().optional(),
9308
+ whitespace: z7.boolean().optional(),
9309
+ keepNames: z7.boolean().optional(),
9310
+ legalComments: z7.enum(["none", "inline", "eof", "linked", "external"]).optional()
9311
+ }).strict().optional(),
9312
+ target: z7.string().min(1).optional().describe("esbuild target")
9313
+ }).strict().describe("Options forwarded to wrapSkeleton");
9314
+ var OBSERVE_NOT_ON_NODE = "observe is a browser wrap option";
9315
+ var OBSERVE_NOT_WITH_PREVIEW = "observe is refused beside preview, which makes a host build";
9316
+ var BuildWrapArtifactSchema = z7.object({
9317
+ target: z7.literal("wrap"),
9318
+ platform: z7.enum(["browser", "node"]),
9319
+ skeleton: z7.union([
9320
+ z7.object({ url: HttpUrlSchema }).strict(),
9321
+ z7.object({ artifact: OutputNameSchema }).strict()
9322
+ ]).describe(
9323
+ "The skeleton to wrap: a GET URL, or the outputName of an earlier bundle artifact in this manifest"
9324
+ ),
9325
+ options: BuildWrapOptionsSchema.optional(),
9326
+ ...OutputFields
9327
+ }).strict().refine(
9328
+ (artifact) => artifact.platform === "browser" || artifact.options?.observe === void 0,
9329
+ { path: ["options", "observe"], message: OBSERVE_NOT_ON_NODE }
9330
+ ).refine(
9331
+ (artifact) => artifact.options?.observe === void 0 || artifact.options.preview === void 0,
9332
+ { path: ["options", "observe"], message: OBSERVE_NOT_WITH_PREVIEW }
9333
+ );
9334
+ var BuildArtifactSchema = z7.union([
9335
+ BuildBundleArtifactSchema,
9336
+ BuildWrapArtifactSchema
9337
+ ]);
9338
+ var BuildPackagesSchema = z7.union([
9339
+ z7.object({
9340
+ tree: PackageTreeSchema.describe(
9341
+ "Install exactly this tree for every bundle artifact"
9342
+ )
9343
+ }).strict(),
9344
+ z7.object({
9345
+ resolve: z7.object({
9346
+ before: z7.iso.datetime({ offset: true }).describe("Only versions published at or before this time"),
9347
+ exempt: z7.array(z7.string().min(1)).describe(
9348
+ "Name prefixes the cutoff skips, with the exact versions packages under them declare"
9349
+ )
9350
+ }).strict()
9351
+ }).strict()
9352
+ ]).describe("How bundle artifacts install their packages");
9353
+ var BuildManifestSchema = z7.object({
9354
+ version: z7.literal(BUILD_MANIFEST_VERSION),
9355
+ toolchain: z7.string().min(1).describe("Exact @walkeros/cli version; any other version refuses"),
9356
+ flowConfig: z7.record(z7.string(), z7.unknown()).optional().describe("Flow.Json to bundle; required when any bundle artifact is"),
9357
+ flowName: z7.string().min(1).optional(),
9358
+ buildEnv: z7.record(z7.string(), z7.string()).optional().describe(
9359
+ "Base env for web $env, beneath config.bundle.env. Never the CLI process's own env"
9360
+ ),
9361
+ artifacts: z7.array(BuildArtifactSchema).min(1),
9362
+ resultPutUrl: HttpUrlSchema.describe("Presigned PUT for the result JSON"),
9363
+ packages: BuildPackagesSchema.optional()
9364
+ }).loose().superRefine((manifest, ctx) => {
9365
+ const seen = /* @__PURE__ */ new Set();
9366
+ const bundled = /* @__PURE__ */ new Set();
9367
+ manifest.artifacts.forEach((artifact, index) => {
9368
+ if (seen.has(artifact.outputName)) {
9369
+ ctx.addIssue({
9370
+ code: "custom",
9371
+ path: ["artifacts", index, "outputName"],
9372
+ message: `Duplicate outputName "${artifact.outputName}"`
9373
+ });
9374
+ }
9375
+ seen.add(artifact.outputName);
9376
+ if (artifact.target !== "wrap") {
9377
+ bundled.add(artifact.outputName);
9378
+ if (!manifest.flowConfig) {
9379
+ ctx.addIssue({
9380
+ code: "custom",
9381
+ path: ["flowConfig"],
9382
+ message: "flowConfig is required for bundle artifacts"
9383
+ });
9384
+ }
9385
+ } else if ("artifact" in artifact.skeleton && !bundled.has(artifact.skeleton.artifact)) {
9386
+ ctx.addIssue({
9387
+ code: "custom",
9388
+ path: ["artifacts", index, "skeleton", "artifact"],
9389
+ message: `No earlier bundle artifact named "${artifact.skeleton.artifact}"`
9390
+ });
9391
+ }
9392
+ });
9393
+ }).meta({ id: "BuildManifest", title: "walkeros bundle --manifest input" });
9394
+ var BUILD_ERROR_CODES = [
9395
+ "INVALID_MANIFEST",
9396
+ "TOOLCHAIN_MISMATCH",
9397
+ "INVALID_CONFIG",
9398
+ "MISSING_BUILD_ENV",
9399
+ "WEB_SECRET_REF",
9400
+ "UNSUPPORTED_PACKAGE_SPEC",
9401
+ "LOCAL_PATH_NOT_ALLOWED",
9402
+ "BUILD_FAILED",
9403
+ "PACKAGE_UNAVAILABLE",
9404
+ "PACKAGE_TREE_INVALID",
9405
+ "UPLOAD_FAILED"
9406
+ ];
9407
+ var BuildResultSchema = z7.object({
9408
+ ok: z7.boolean(),
9409
+ toolchain: z7.string().describe("The CLI version that ran the build"),
9410
+ artifacts: z7.array(
9411
+ z7.object({
9412
+ target: z7.string(),
9413
+ outputName: z7.string(),
9414
+ bytes: z7.number().int().nonnegative(),
9415
+ sha256: z7.string()
9416
+ })
9417
+ ).describe("Outputs PUT successfully, in manifest order"),
9418
+ packages: z7.object({
9419
+ digest: z7.string().describe("sha256 of the canonical tree"),
9420
+ tree: PackageTreeSchema
9421
+ }).optional().describe("The tree the bundle artifacts installed, when asked for"),
9422
+ error: z7.object({
9423
+ code: z7.enum(BUILD_ERROR_CODES),
9424
+ message: z7.string(),
9425
+ outputName: z7.string().optional().describe("The artifact being built when the error occurred")
9426
+ }).optional()
9427
+ }).meta({ id: "BuildResult", title: "walkeros bundle --manifest result" });
9428
+
9429
+ // src/commands/bundle/wrap.ts
8324
9430
  function extractDevExternals(skeletonText) {
8325
9431
  const pattern = /import\(\s*['"]([^'"]+\/dev)['"]\s*\)/g;
8326
9432
  const found = /* @__PURE__ */ new Set();
@@ -8343,6 +9449,15 @@ function getNodeResolutionPaths() {
8343
9449
  }
8344
9450
  return candidates;
8345
9451
  }
9452
+ function observeProblem(observe, wrap) {
9453
+ if (wrap.platform !== "browser") return OBSERVE_NOT_ON_NODE;
9454
+ if (wrap.preview) return OBSERVE_NOT_WITH_PREVIEW;
9455
+ const parsed = BuildWrapObserveSchema.safeParse(observe);
9456
+ if (parsed.success) return void 0;
9457
+ const issue = parsed.error.issues[0];
9458
+ const at = ["observe", ...(issue?.path ?? []).map(String)].join(".");
9459
+ return `Invalid ${at}: ${issue?.message ?? "not an observe option"}`;
9460
+ }
8346
9461
  async function wrapSkeleton(options) {
8347
9462
  const {
8348
9463
  skeletonPath,
@@ -8354,16 +9469,18 @@ async function wrapSkeleton(options) {
8354
9469
  minify = true,
8355
9470
  minifyOptions
8356
9471
  } = options;
8357
- if (options.preview && options.previewGrantTargets) {
8358
- throw new Error(
8359
- "wrapSkeleton: `preview` (host activator) and `previewGrantTargets` (preview-artifact injection) are mutually exclusive. Pass at most one."
8360
- );
8361
- }
8362
9472
  if (options.preview && (typeof options.preview.previewOrigin !== "string" || !/^[a-z0-9.-]+$/.test(options.preview.previewOrigin))) {
8363
9473
  throw new Error(
8364
9474
  `Invalid previewOrigin "${options.preview.previewOrigin}". Must be a bare hostname matching /^[a-z0-9.-]+$/.`
8365
9475
  );
8366
9476
  }
9477
+ if (options.observe !== void 0) {
9478
+ const problem = observeProblem(options.observe, {
9479
+ platform,
9480
+ preview: options.preview !== void 0
9481
+ });
9482
+ if (problem) throw new Error(problem);
9483
+ }
8367
9484
  if (!await fs21.pathExists(skeletonPath)) {
8368
9485
  throw new Error(`wrapSkeleton: skeleton not found at ${skeletonPath}`);
8369
9486
  }
@@ -8374,9 +9491,8 @@ async function wrapSkeleton(options) {
8374
9491
  ...windowCollector ? { windowCollector } : {},
8375
9492
  ...windowElb ? { windowElb } : {},
8376
9493
  ...options.preview ? { preview: options.preview } : {},
8377
- ...options.previewGrantTargets ? { previewGrantTargets: options.previewGrantTargets } : {},
8378
- platform,
8379
9494
  ...options.observe ? { observe: options.observe } : {},
9495
+ platform,
8380
9496
  ...options.moin ? { moin: options.moin } : {}
8381
9497
  }) : generateWrapEntryServer(absoluteSkeletonPath);
8382
9498
  const entryDir = await tmpRunDir("wrap");
@@ -8411,15 +9527,12 @@ async function wrapSkeleton(options) {
8411
9527
  if (platform === "browser") {
8412
9528
  esbuildOptions.format = "iife";
8413
9529
  esbuildOptions.plugins = [cliPackagesPlugin()];
8414
- const needs = readNeedsMarker(skeletonText);
8415
9530
  esbuildOptions.define = {
8416
9531
  "process.env.NODE_ENV": '"production"',
8417
9532
  global: "globalThis",
8418
9533
  ...buildFlagDefines(
8419
- needs && {
8420
- ...needs,
8421
- observe: needs.observe || options.observe !== void 0
8422
- }
9534
+ readNeedsMarker(skeletonText),
9535
+ options.observe !== void 0
8423
9536
  )
8424
9537
  };
8425
9538
  esbuildOptions.external = devExternals;
@@ -8444,158 +9557,6 @@ import path22 from "path";
8444
9557
  import fs22 from "fs-extra";
8445
9558
  import { FlowReferenceError as FlowReferenceError2 } from "@walkeros/core";
8446
9559
 
8447
- // src/schemas/build-manifest.ts
8448
- import { z as z4 } from "@walkeros/core/dev";
8449
- var BUILD_MANIFEST_VERSION = 1;
8450
- var HttpUrlSchema = z4.url({ protocol: /^https?$/ });
8451
- var OutputNameSchema = z4.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "Must be a bare file name").describe("Output file name; its extension picks the format (.tar.gz packs)");
8452
- var BUNDLE_TARGET_NAMES = [
8453
- "cdn",
8454
- "cdn-skeleton",
8455
- "runner",
8456
- "simulate",
8457
- "push"
8458
- ];
8459
- var RESERVED_HEADERS = /* @__PURE__ */ new Set([
8460
- "content-type",
8461
- "content-length",
8462
- "host"
8463
- ]);
8464
- var OutputFields = {
8465
- outputName: OutputNameSchema,
8466
- putUrl: HttpUrlSchema.describe("Presigned PUT for exactly this output"),
8467
- contentType: z4.string().min(1).optional().describe("Content-Type sent with the PUT (must match a signed header)"),
8468
- headers: z4.record(z4.string().min(1), z4.string()).refine(
8469
- (headers) => Object.keys(headers).every(
8470
- (name) => !RESERVED_HEADERS.has(name.toLowerCase())
8471
- ),
8472
- "Content-Type, Content-Length and Host are set by the CLI"
8473
- ).optional().describe(
8474
- "Extra headers sent with the PUT (for headers a presign signs); never logged"
8475
- )
8476
- };
8477
- var BuildBundleArtifactSchema = z4.object({
8478
- target: z4.enum(BUNDLE_TARGET_NAMES).describe("Named bundle target"),
8479
- ...OutputFields
8480
- }).strict();
8481
- var BuildWrapOptionsSchema = z4.object({
8482
- windowCollector: z4.string().min(1).optional(),
8483
- windowElb: z4.string().min(1).optional(),
8484
- preview: z4.object({
8485
- enabled: z4.boolean(),
8486
- keyring: z4.array(
8487
- z4.object({ kid: z4.string(), spki: z4.string() }).strict()
8488
- ),
8489
- iss: z4.string(),
8490
- pb: z4.string().optional(),
8491
- acceptForeign: z4.boolean().optional(),
8492
- demoAllowlist: z4.array(z4.string()).optional(),
8493
- previewOrigin: z4.string()
8494
- }).strict().optional(),
8495
- previewGrantTargets: z4.array(z4.string()).optional(),
8496
- observe: z4.object({
8497
- url: z4.string(),
8498
- binding: z4.string(),
8499
- flowId: z4.string().optional(),
8500
- level: z4.enum(["off", "standard", "trace"]).optional(),
8501
- sample: z4.number().min(0).max(1).optional()
8502
- }).strict().optional(),
8503
- moin: z4.object({ app: z4.string(), base: z4.string() }).strict().optional().describe("Tag Mode loader target; absent, the production app"),
8504
- minify: z4.boolean().optional(),
8505
- minifyOptions: z4.object({
8506
- identifiers: z4.boolean().optional(),
8507
- syntax: z4.boolean().optional(),
8508
- whitespace: z4.boolean().optional(),
8509
- keepNames: z4.boolean().optional(),
8510
- legalComments: z4.enum(["none", "inline", "eof", "linked", "external"]).optional()
8511
- }).strict().optional(),
8512
- target: z4.string().min(1).optional().describe("esbuild target")
8513
- }).strict().describe("Options forwarded to wrapSkeleton");
8514
- var BuildWrapArtifactSchema = z4.object({
8515
- target: z4.literal("wrap"),
8516
- platform: z4.enum(["browser", "node"]),
8517
- skeleton: z4.union([
8518
- z4.object({ url: HttpUrlSchema }).strict(),
8519
- z4.object({ artifact: OutputNameSchema }).strict()
8520
- ]).describe(
8521
- "The skeleton to wrap: a GET URL, or the outputName of an earlier bundle artifact in this manifest"
8522
- ),
8523
- options: BuildWrapOptionsSchema.optional(),
8524
- ...OutputFields
8525
- }).strict();
8526
- var BuildArtifactSchema = z4.union([
8527
- BuildBundleArtifactSchema,
8528
- BuildWrapArtifactSchema
8529
- ]);
8530
- var BuildManifestSchema = z4.object({
8531
- version: z4.literal(BUILD_MANIFEST_VERSION),
8532
- toolchain: z4.string().min(1).describe("Exact @walkeros/cli version; any other version refuses"),
8533
- flowConfig: z4.record(z4.string(), z4.unknown()).optional().describe("Flow.Json to bundle; required when any bundle artifact is"),
8534
- flowName: z4.string().min(1).optional(),
8535
- buildEnv: z4.record(z4.string(), z4.string()).optional().describe(
8536
- "Base env for web $env, beneath config.bundle.env. Never the CLI process's own env"
8537
- ),
8538
- artifacts: z4.array(BuildArtifactSchema).min(1),
8539
- resultPutUrl: HttpUrlSchema.describe("Presigned PUT for the result JSON")
8540
- }).loose().superRefine((manifest, ctx) => {
8541
- const seen = /* @__PURE__ */ new Set();
8542
- const bundled = /* @__PURE__ */ new Set();
8543
- manifest.artifacts.forEach((artifact, index) => {
8544
- if (seen.has(artifact.outputName)) {
8545
- ctx.addIssue({
8546
- code: "custom",
8547
- path: ["artifacts", index, "outputName"],
8548
- message: `Duplicate outputName "${artifact.outputName}"`
8549
- });
8550
- }
8551
- seen.add(artifact.outputName);
8552
- if (artifact.target !== "wrap") {
8553
- bundled.add(artifact.outputName);
8554
- if (!manifest.flowConfig) {
8555
- ctx.addIssue({
8556
- code: "custom",
8557
- path: ["flowConfig"],
8558
- message: "flowConfig is required for bundle artifacts"
8559
- });
8560
- }
8561
- } else if ("artifact" in artifact.skeleton && !bundled.has(artifact.skeleton.artifact)) {
8562
- ctx.addIssue({
8563
- code: "custom",
8564
- path: ["artifacts", index, "skeleton", "artifact"],
8565
- message: `No earlier bundle artifact named "${artifact.skeleton.artifact}"`
8566
- });
8567
- }
8568
- });
8569
- }).meta({ id: "BuildManifest", title: "walkeros bundle --manifest input" });
8570
- var BUILD_ERROR_CODES = [
8571
- "INVALID_MANIFEST",
8572
- "TOOLCHAIN_MISMATCH",
8573
- "INVALID_CONFIG",
8574
- "MISSING_BUILD_ENV",
8575
- "WEB_SECRET_REF",
8576
- "UNSUPPORTED_PACKAGE_SPEC",
8577
- "LOCAL_PATH_NOT_ALLOWED",
8578
- "BUILD_FAILED",
8579
- "UPLOAD_FAILED"
8580
- ];
8581
- var BuildResultSchema = z4.object({
8582
- ok: z4.boolean(),
8583
- toolchain: z4.string().describe("The CLI version that ran the build"),
8584
- artifacts: z4.array(
8585
- z4.object({
8586
- target: z4.string(),
8587
- outputName: z4.string(),
8588
- bytes: z4.number().int().nonnegative(),
8589
- sha256: z4.string()
8590
- })
8591
- ).describe("Outputs PUT successfully, in manifest order"),
8592
- error: z4.object({
8593
- code: z4.enum(BUILD_ERROR_CODES),
8594
- message: z4.string(),
8595
- outputName: z4.string().optional().describe("The artifact being built when the error occurred")
8596
- }).optional()
8597
- }).meta({ id: "BuildResult", title: "walkeros bundle --manifest result" });
8598
-
8599
9560
  // src/commands/bundle/fetch-retry.ts
8600
9561
  var BACKOFF_MS = [1e3, 3e3];
8601
9562
  function defaultSleep(ms) {
@@ -8625,7 +9586,7 @@ function describeFetchError(failure, timeoutMs) {
8625
9586
  return "fetch failed";
8626
9587
  }
8627
9588
  async function fetchWithRetry(url, init, options) {
8628
- const { label, timeoutMs, warn, sleep = defaultSleep } = options;
9589
+ const { label, timeoutMs, warn, sleep: sleep2 = defaultSleep } = options;
8629
9590
  const maxAttempts = BACKOFF_MS.length + 1;
8630
9591
  for (let attempt = 1; ; attempt++) {
8631
9592
  let failure;
@@ -8652,7 +9613,7 @@ async function fetchWithRetry(url, init, options) {
8652
9613
  warn?.(
8653
9614
  `${label} attempt ${attempt}/${maxAttempts} failed: ${reason}, retrying in ${delay2 / 1e3}s`
8654
9615
  );
8655
- await sleep(delay2);
9616
+ await sleep2(delay2);
8656
9617
  }
8657
9618
  }
8658
9619
 
@@ -8710,6 +9671,8 @@ function classify(error, phase) {
8710
9671
  if (error instanceof UnsupportedPackageSpecError) {
8711
9672
  return "UNSUPPORTED_PACKAGE_SPEC";
8712
9673
  }
9674
+ if (error instanceof PackageTreeError) return "PACKAGE_TREE_INVALID";
9675
+ if (error instanceof PackageUnavailableError) return "PACKAGE_UNAVAILABLE";
8713
9676
  return phase === "config" ? "INVALID_CONFIG" : "BUILD_FAILED";
8714
9677
  }
8715
9678
  function isHttpUrl(source) {
@@ -8764,7 +9727,7 @@ async function download(url, dest, retry) {
8764
9727
  }
8765
9728
  await fs22.writeFile(dest, Buffer.from(await response.arrayBuffer()));
8766
9729
  }
8767
- async function buildArtifact(manifest, configPath, artifact, workDir, index, outputs, retry) {
9730
+ async function buildArtifact(manifest, configPath, artifact, workDir, index, outputs, retry, packageInstall) {
8768
9731
  const output = path22.join(workDir, "out", artifact.outputName);
8769
9732
  await fs22.ensureDir(path22.dirname(output));
8770
9733
  if (artifact.target === "wrap") {
@@ -8800,7 +9763,11 @@ async function buildArtifact(manifest, configPath, artifact, workDir, index, out
8800
9763
  cache: false,
8801
9764
  // The base env is the manifest's, NEVER this process's own.
8802
9765
  buildEnv: manifest.buildEnv ?? {},
8803
- buildOverrides: { output, tempDir: scratch }
9766
+ buildOverrides: {
9767
+ output,
9768
+ tempDir: scratch,
9769
+ ...packageInstall ? { packageInstall } : {}
9770
+ }
8804
9771
  });
8805
9772
  return output;
8806
9773
  }
@@ -8858,6 +9825,18 @@ async function runBuildManifest(source, retry = {}) {
8858
9825
  }
8859
9826
  }
8860
9827
  const outputs = /* @__PURE__ */ new Map();
9828
+ let tree = manifest.packages && "tree" in manifest.packages ? manifest.packages.tree : void 0;
9829
+ const resolve5 = manifest.packages && "resolve" in manifest.packages ? manifest.packages.resolve : void 0;
9830
+ const installFor = () => {
9831
+ if (tree) return { tree };
9832
+ if (!resolve5) return void 0;
9833
+ return {
9834
+ resolve: { before: new Date(resolve5.before), exempt: resolve5.exempt },
9835
+ onTree: (installed) => {
9836
+ tree = installed;
9837
+ }
9838
+ };
9839
+ };
8861
9840
  for (const [index, artifact] of manifest.artifacts.entries()) {
8862
9841
  try {
8863
9842
  const output = await buildArtifact(
@@ -8867,7 +9846,8 @@ async function runBuildManifest(source, retry = {}) {
8867
9846
  workDir,
8868
9847
  index,
8869
9848
  outputs,
8870
- retry
9849
+ retry,
9850
+ artifact.target === "wrap" ? void 0 : installFor()
8871
9851
  );
8872
9852
  outputs.set(artifact.outputName, output);
8873
9853
  const content = await fs22.readFile(output);
@@ -8896,6 +9876,9 @@ async function runBuildManifest(source, retry = {}) {
8896
9876
  };
8897
9877
  }
8898
9878
  }
9879
+ if (manifest.packages && tree) {
9880
+ result.packages = { digest: packageTreeDigest(tree), tree };
9881
+ }
8899
9882
  result.ok = true;
8900
9883
  return { result, reported: await report(manifest.resultPutUrl, result) };
8901
9884
  } finally {
@@ -9129,30 +10112,41 @@ export {
9129
10112
  BuildArtifactSchema,
9130
10113
  BuildBundleArtifactSchema,
9131
10114
  BuildManifestSchema,
10115
+ BuildPackagesSchema,
9132
10116
  BuildResultSchema,
9133
10117
  BuildWrapArtifactSchema,
9134
10118
  BuildWrapOptionsSchema,
10119
+ PACKAGE_TREE_MAX_ENTRIES,
10120
+ PACKAGE_TREE_VERSION,
10121
+ PackageTreeError,
10122
+ PackageTreeSchema,
9135
10123
  VALIDATE_CODES,
9136
10124
  VERSION,
9137
10125
  appErrorMessage,
9138
10126
  appFetch,
10127
+ assertPackageTree,
9139
10128
  buildDataPayload,
9140
10129
  bundle,
9141
10130
  bundleCommand,
10131
+ canonicalPackageTree,
9142
10132
  classifyStepProperties,
9143
10133
  clientContextHeaders,
9144
10134
  collectKnownSecrets,
9145
10135
  containsCodeMarkers,
10136
+ edgeIsExempt,
9146
10137
  formatPushResult,
9147
10138
  getClientContext,
9148
10139
  loadConfig,
9149
10140
  loadJsonConfig,
10141
+ packageTreeDigest,
10142
+ packageTreeProblems,
9150
10143
  push,
9151
10144
  pushCommand,
9152
10145
  readConfig,
9153
10146
  resetClientContext,
9154
10147
  resolveAccessToken,
9155
10148
  resolveAppUrl,
10149
+ resolveCredential,
9156
10150
  resolveExportName,
9157
10151
  resolveManifestSource,
9158
10152
  runBuildManifest,