@lark-apaas/fullstack-cli 1.1.68-alpha.20260930060426 → 1.1.68-alpha.20261002212123

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
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import fs31 from "fs";
3
- import path26 from "path";
2
+ import fs32 from "fs";
3
+ import path27 from "path";
4
4
  import { fileURLToPath as fileURLToPath5 } from "url";
5
5
  import { config as dotenvConfig } from "dotenv";
6
6
 
@@ -5201,7 +5201,7 @@ var PROMPT_PATTERNS = [
5201
5201
  { pattern: /proceed\?/i, answer: "y\n" }
5202
5202
  ];
5203
5203
  async function executeShadcnAdd(registryItemPath) {
5204
- return new Promise((resolve2) => {
5204
+ return new Promise((resolve3) => {
5205
5205
  let output = "";
5206
5206
  const args = ["--yes", "shadcn@3.8.2", "add", registryItemPath];
5207
5207
  const ptyProcess = pty.spawn("npx", args, {
@@ -5227,7 +5227,7 @@ async function executeShadcnAdd(registryItemPath) {
5227
5227
  });
5228
5228
  const timeoutId = setTimeout(() => {
5229
5229
  ptyProcess.kill();
5230
- resolve2({
5230
+ resolve3({
5231
5231
  success: false,
5232
5232
  files: [],
5233
5233
  error: "\u6267\u884C\u8D85\u65F6"
@@ -5238,7 +5238,7 @@ async function executeShadcnAdd(registryItemPath) {
5238
5238
  const success = exitCode === 0;
5239
5239
  const filePaths = parseOutput(output);
5240
5240
  const files = filePaths.map(toFileInfo);
5241
- resolve2({
5241
+ resolve3({
5242
5242
  success,
5243
5243
  files,
5244
5244
  error: success ? void 0 : output || `Process exited with code ${exitCode}`
@@ -5249,12 +5249,12 @@ async function executeShadcnAdd(registryItemPath) {
5249
5249
 
5250
5250
  // src/commands/component/add.handler.ts
5251
5251
  function runActionPluginInit() {
5252
- return new Promise((resolve2) => {
5252
+ return new Promise((resolve3) => {
5253
5253
  execFile("fullstack-cli", ["action-plugin", "init"], { cwd: process.cwd(), stdio: "ignore" }, (error) => {
5254
5254
  if (error) {
5255
5255
  debug("action-plugin init \u5931\u8D25: %s", error.message);
5256
5256
  }
5257
- resolve2();
5257
+ resolve3();
5258
5258
  });
5259
5259
  });
5260
5260
  }
@@ -8429,6 +8429,875 @@ var buildCommandGroup = {
8429
8429
  commands: [getTokenCommand, uploadStaticCommand, preUploadStaticCommand]
8430
8430
  };
8431
8431
 
8432
+ // src/output-lease.ts
8433
+ import * as crypto from "crypto";
8434
+ import * as fs31 from "fs";
8435
+ import * as os3 from "os";
8436
+ import * as path26 from "path";
8437
+ import { execFileSync as execFileSync2 } from "child_process";
8438
+ var OUTPUT_LEASE_PROTOCOL_VERSION = 1;
8439
+ var OUTPUT_LEASE_ENV = "MIAODA_PREVIEW_OUTPUT_LEASE_V1";
8440
+ var OUTPUT_LEASE_CONFLICT = "MIAODA_PREVIEW_OUTPUT_CONFLICT";
8441
+ var OUTPUT_LEASE_STATE_UNKNOWN = "MIAODA_PREVIEW_STATE_UNKNOWN";
8442
+ var DEFAULT_HEARTBEAT_INTERVAL_MS = 2e3;
8443
+ var DEFAULT_STALE_AFTER_MS = 15e3;
8444
+ var TRANSITION_RETRY_COUNT = 200;
8445
+ var TRANSITION_RETRY_MS = 5;
8446
+ var OWNER_FILE = "owner.json";
8447
+ var TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/;
8448
+ var KEY_PATTERN = /^[a-f0-9]{64}$/;
8449
+ var TEMP_OWNER_PATTERN = /^\.owner-[0-9]+-[a-f0-9]{16}\.tmp$/;
8450
+ var OutputLeaseProtocolError = class extends Error {
8451
+ constructor(code, detail) {
8452
+ super(`${code}: ${detail}`);
8453
+ this.name = "OutputLeaseProtocolError";
8454
+ this.code = code;
8455
+ }
8456
+ };
8457
+ function unknown(detail, cause) {
8458
+ const suffix = cause instanceof Error && cause.message ? ` (${cause.message})` : "";
8459
+ return new OutputLeaseProtocolError(
8460
+ OUTPUT_LEASE_STATE_UNKNOWN,
8461
+ `${detail}${suffix}`
8462
+ );
8463
+ }
8464
+ function conflict(target, owner) {
8465
+ return new OutputLeaseProtocolError(
8466
+ OUTPUT_LEASE_CONFLICT,
8467
+ `${target.canonicalOutDir} is held by an active ${owner.kind} owner`
8468
+ );
8469
+ }
8470
+ function errnoCode(error) {
8471
+ return error instanceof Error && "code" in error ? error.code : void 0;
8472
+ }
8473
+ function isMissing(error) {
8474
+ const code = errnoCode(error);
8475
+ return code === "ENOENT" || code === "ESRCH";
8476
+ }
8477
+ function sleepSync(milliseconds) {
8478
+ const signal = new Int32Array(new SharedArrayBuffer(4));
8479
+ Atomics.wait(signal, 0, 0, milliseconds);
8480
+ }
8481
+ function canonicalizeWithoutCreating(input) {
8482
+ let cursor = path26.resolve(input);
8483
+ const missing = [];
8484
+ for (; ; ) {
8485
+ try {
8486
+ fs31.lstatSync(cursor);
8487
+ } catch (error) {
8488
+ if (!isMissing(error))
8489
+ throw unknown(`cannot inspect path ${cursor}`, error);
8490
+ const parent = path26.dirname(cursor);
8491
+ if (parent === cursor) {
8492
+ throw unknown(`cannot find an existing ancestor for ${input}`);
8493
+ }
8494
+ missing.unshift(path26.basename(cursor));
8495
+ cursor = parent;
8496
+ continue;
8497
+ }
8498
+ try {
8499
+ return path26.resolve(fs31.realpathSync.native(cursor), ...missing);
8500
+ } catch (error) {
8501
+ throw unknown(`cannot canonicalize existing ancestor ${cursor}`, error);
8502
+ }
8503
+ }
8504
+ }
8505
+ function currentUserComponent() {
8506
+ if (typeof process.getuid === "function") return `uid-${process.getuid()}`;
8507
+ return `user-${crypto.createHash("sha256").update(os3.userInfo().username).digest("hex").slice(0, 16)}`;
8508
+ }
8509
+ function defaultLockRootForCurrentUser(temporaryRoot = os3.tmpdir()) {
8510
+ return path26.join(
8511
+ temporaryRoot,
8512
+ `miaoda-preview-output-leases-v${OUTPUT_LEASE_PROTOCOL_VERSION}-${currentUserComponent()}`
8513
+ );
8514
+ }
8515
+ function validatePrivateDirectory(directory, label, expected) {
8516
+ let link;
8517
+ let stat;
8518
+ try {
8519
+ link = fs31.lstatSync(directory);
8520
+ if (link.isSymbolicLink() || !link.isDirectory()) {
8521
+ throw new Error("not a direct directory");
8522
+ }
8523
+ stat = fs31.statSync(directory);
8524
+ } catch (error) {
8525
+ throw unknown(`untrusted ${label} ${directory}`, error);
8526
+ }
8527
+ if (!stat.isDirectory() || stat.dev !== link.dev || stat.ino !== link.ino) {
8528
+ throw unknown(`untrusted ${label} identity ${directory}`);
8529
+ }
8530
+ if (expected && (stat.dev !== expected.device || stat.ino !== expected.inode)) {
8531
+ throw unknown(`${label} changed identity ${directory}`);
8532
+ }
8533
+ if (process.platform !== "win32") {
8534
+ if (typeof process.getuid !== "function" || stat.uid !== process.getuid()) {
8535
+ throw unknown(`unexpected owner for ${label} ${directory}`);
8536
+ }
8537
+ if ((stat.mode & 63) !== 0) {
8538
+ throw unknown(`insecure permissions for ${label} ${directory}`);
8539
+ }
8540
+ }
8541
+ return { device: stat.dev, inode: stat.ino };
8542
+ }
8543
+ function prepareTrustedLockRoot(lockRoot) {
8544
+ try {
8545
+ fs31.mkdirSync(lockRoot, { mode: 448 });
8546
+ } catch (error) {
8547
+ if (errnoCode(error) !== "EEXIST") {
8548
+ throw unknown(`cannot prepare lease storage ${lockRoot}`, error);
8549
+ }
8550
+ }
8551
+ return validatePrivateDirectory(lockRoot, "lease root");
8552
+ }
8553
+ function assertTrustedRoot(target) {
8554
+ validatePrivateDirectory(target.lockRoot, "lease root", {
8555
+ device: target.lockRootDevice,
8556
+ inode: target.lockRootInode
8557
+ });
8558
+ }
8559
+ function resolveOutputLeaseTarget(rootDir, outDir, lockRoot = defaultLockRootForCurrentUser()) {
8560
+ const canonicalRoot = canonicalizeWithoutCreating(rootDir);
8561
+ const absoluteOutDir = path26.isAbsolute(outDir) ? outDir : path26.resolve(canonicalRoot, outDir);
8562
+ const canonicalOutDir = canonicalizeWithoutCreating(absoluteOutDir);
8563
+ const key = crypto.createHash("sha256").update(`miaoda-preview-output-lease:v${OUTPUT_LEASE_PROTOCOL_VERSION}\0`).update(canonicalOutDir).digest("hex");
8564
+ const canonicalLockRoot = path26.resolve(lockRoot);
8565
+ const identity = prepareTrustedLockRoot(canonicalLockRoot);
8566
+ const lockDir = path26.join(canonicalLockRoot, `${key}.lease`);
8567
+ return {
8568
+ key,
8569
+ canonicalRoot,
8570
+ canonicalOutDir,
8571
+ lockRoot: canonicalLockRoot,
8572
+ lockRootDevice: identity.device,
8573
+ lockRootInode: identity.inode,
8574
+ lockDir,
8575
+ ownerFile: path26.join(lockDir, OWNER_FILE),
8576
+ transitionDir: path26.join(canonicalLockRoot, `${key}.transition`)
8577
+ };
8578
+ }
8579
+ function acquireTransitionGuard(target) {
8580
+ for (let attempt = 0; attempt < TRANSITION_RETRY_COUNT; attempt += 1) {
8581
+ assertTrustedRoot(target);
8582
+ try {
8583
+ fs31.mkdirSync(target.transitionDir, { mode: 448 });
8584
+ const identity = validatePrivateDirectory(
8585
+ target.transitionDir,
8586
+ "transition guard"
8587
+ );
8588
+ assertTrustedRoot(target);
8589
+ return {
8590
+ path: target.transitionDir,
8591
+ device: identity.device,
8592
+ inode: identity.inode,
8593
+ poisoned: false
8594
+ };
8595
+ } catch (error) {
8596
+ if (errnoCode(error) !== "EEXIST") {
8597
+ if (error instanceof OutputLeaseProtocolError) throw error;
8598
+ throw unknown(
8599
+ `cannot acquire transition guard for ${target.key}`,
8600
+ error
8601
+ );
8602
+ }
8603
+ try {
8604
+ validatePrivateDirectory(target.transitionDir, "transition guard");
8605
+ } catch (validationError) {
8606
+ if (!fs31.existsSync(target.transitionDir)) continue;
8607
+ throw validationError;
8608
+ }
8609
+ sleepSync(TRANSITION_RETRY_MS);
8610
+ }
8611
+ }
8612
+ throw unknown(
8613
+ `transition guard is unavailable for ${target.canonicalOutDir}`
8614
+ );
8615
+ }
8616
+ function assertTransitionGuard(target, guard) {
8617
+ assertTrustedRoot(target);
8618
+ validatePrivateDirectory(guard.path, "transition guard", guard);
8619
+ }
8620
+ function releaseTransitionGuard(target, guard) {
8621
+ assertTransitionGuard(target, guard);
8622
+ try {
8623
+ fs31.rmdirSync(guard.path);
8624
+ } catch (error) {
8625
+ throw unknown(`cannot release transition guard for ${target.key}`, error);
8626
+ }
8627
+ }
8628
+ function withTransitionGuard(target, operation) {
8629
+ const guard = acquireTransitionGuard(target);
8630
+ let result;
8631
+ let failure;
8632
+ try {
8633
+ result = operation(guard);
8634
+ } catch (error) {
8635
+ failure = error;
8636
+ }
8637
+ if (!guard.poisoned) {
8638
+ try {
8639
+ releaseTransitionGuard(target, guard);
8640
+ } catch (error) {
8641
+ failure = error;
8642
+ }
8643
+ }
8644
+ if (failure !== void 0) throw failure;
8645
+ return result;
8646
+ }
8647
+ function parseLinuxStat(contents) {
8648
+ const end = contents.lastIndexOf(")");
8649
+ if (end < 0) throw new Error("missing process command terminator");
8650
+ const fields = contents.slice(end + 2).trim().split(/\s+/);
8651
+ if (fields.length < 20) throw new Error("incomplete process stat");
8652
+ const parentPid = Number(fields[1]);
8653
+ const startTime = fields[19];
8654
+ if (!Number.isSafeInteger(parentPid) || parentPid < 0 || !/^\d+$/.test(startTime)) {
8655
+ throw new Error("invalid process stat");
8656
+ }
8657
+ return { parentPid, startIdentity: `linux-proc-start:${startTime}` };
8658
+ }
8659
+ function inspectProcess(pid) {
8660
+ if (!Number.isSafeInteger(pid) || pid <= 0) return { state: "unknown" };
8661
+ if (process.platform === "linux") {
8662
+ try {
8663
+ return {
8664
+ state: "alive",
8665
+ startIdentity: parseLinuxStat(
8666
+ fs31.readFileSync(`/proc/${pid}/stat`, "utf8")
8667
+ ).startIdentity
8668
+ };
8669
+ } catch (error) {
8670
+ return isMissing(error) ? { state: "dead" } : { state: "unknown" };
8671
+ }
8672
+ }
8673
+ if (process.platform !== "win32") {
8674
+ try {
8675
+ const value = execFileSync2("ps", ["-o", "lstart=", "-p", String(pid)], {
8676
+ encoding: "utf8",
8677
+ stdio: ["ignore", "pipe", "ignore"]
8678
+ }).trim();
8679
+ return value ? { state: "alive", startIdentity: `posix-ps-start:${value}` } : { state: "dead" };
8680
+ } catch {
8681
+ try {
8682
+ process.kill(pid, 0);
8683
+ return { state: "unknown" };
8684
+ } catch (error) {
8685
+ return isMissing(error) ? { state: "dead" } : { state: "unknown" };
8686
+ }
8687
+ }
8688
+ }
8689
+ return { state: "unknown" };
8690
+ }
8691
+ function readParentPid(pid) {
8692
+ if (process.platform === "linux") {
8693
+ try {
8694
+ return parseLinuxStat(fs31.readFileSync(`/proc/${pid}/stat`, "utf8")).parentPid;
8695
+ } catch {
8696
+ return null;
8697
+ }
8698
+ }
8699
+ if (process.platform !== "win32") {
8700
+ try {
8701
+ const parent = Number(
8702
+ execFileSync2("ps", ["-o", "ppid=", "-p", String(pid)], {
8703
+ encoding: "utf8",
8704
+ stdio: ["ignore", "pipe", "ignore"]
8705
+ }).trim()
8706
+ );
8707
+ return Number.isSafeInteger(parent) && parent >= 0 ? parent : null;
8708
+ } catch {
8709
+ return null;
8710
+ }
8711
+ }
8712
+ return null;
8713
+ }
8714
+ function isProcessOrDescendant(pid, ancestorPid) {
8715
+ let cursor = pid;
8716
+ const seen = /* @__PURE__ */ new Set();
8717
+ for (let depth = 0; depth < 256; depth += 1) {
8718
+ if (cursor === ancestorPid) return true;
8719
+ if (cursor <= 1 || seen.has(cursor)) return false;
8720
+ seen.add(cursor);
8721
+ const parent = readParentPid(cursor);
8722
+ if (parent === null) return false;
8723
+ cursor = parent;
8724
+ }
8725
+ return false;
8726
+ }
8727
+ function validateOwner(value, target) {
8728
+ if (!value || typeof value !== "object") {
8729
+ throw unknown(`malformed owner for ${target.canonicalOutDir}`);
8730
+ }
8731
+ const owner = value;
8732
+ const valid = owner.version === OUTPUT_LEASE_PROTOCOL_VERSION && (owner.kind === "dev" || owner.kind === "production") && owner.key === target.key && typeof owner.canonicalRoot === "string" && path26.isAbsolute(owner.canonicalRoot) && owner.canonicalOutDir === target.canonicalOutDir && typeof owner.generation === "string" && TOKEN_PATTERN.test(owner.generation) && typeof owner.token === "string" && TOKEN_PATTERN.test(owner.token) && Number.isSafeInteger(owner.pid) && Number(owner.pid) > 0 && typeof owner.processStartIdentity === "string" && owner.processStartIdentity.length > 0 && Number.isFinite(owner.createdAt) && Number(owner.createdAt) > 0 && Number.isFinite(owner.heartbeatAt) && Number(owner.heartbeatAt) > 0;
8733
+ if (!valid) throw unknown(`malformed owner for ${target.canonicalOutDir}`);
8734
+ return owner;
8735
+ }
8736
+ function validateRegularPrivateFile(file, label) {
8737
+ try {
8738
+ const link = fs31.lstatSync(file);
8739
+ const stat = fs31.statSync(file);
8740
+ if (link.isSymbolicLink() || !link.isFile() || !stat.isFile() || link.dev !== stat.dev || link.ino !== stat.ino || stat.nlink !== 1) {
8741
+ throw new Error("not a direct single-link file");
8742
+ }
8743
+ if (process.platform !== "win32") {
8744
+ if (typeof process.getuid !== "function" || stat.uid !== process.getuid()) {
8745
+ throw new Error("unexpected file owner");
8746
+ }
8747
+ if ((stat.mode & 63) !== 0) throw new Error("insecure file mode");
8748
+ }
8749
+ } catch (error) {
8750
+ throw unknown(`untrusted ${label} ${file}`, error);
8751
+ }
8752
+ }
8753
+ function readOwnerFromDirectory(target, directory) {
8754
+ assertTrustedRoot(target);
8755
+ validatePrivateDirectory(directory, "lease generation");
8756
+ const ownerFile = path26.join(directory, OWNER_FILE);
8757
+ validateRegularPrivateFile(ownerFile, "owner record");
8758
+ try {
8759
+ return validateOwner(
8760
+ JSON.parse(fs31.readFileSync(ownerFile, "utf8")),
8761
+ target
8762
+ );
8763
+ } catch (error) {
8764
+ if (error instanceof OutputLeaseProtocolError) throw error;
8765
+ throw unknown(`cannot read owner for ${target.canonicalOutDir}`, error);
8766
+ }
8767
+ }
8768
+ function readOwner(target) {
8769
+ return readOwnerFromDirectory(target, target.lockDir);
8770
+ }
8771
+ function sameGeneration(owner, expected) {
8772
+ return owner.generation === expected.generation && owner.token === expected.token;
8773
+ }
8774
+ function writeOwnerAtomically(target, owner, guard) {
8775
+ assertTransitionGuard(target, guard);
8776
+ const temporary = path26.join(
8777
+ target.lockDir,
8778
+ `.owner-${process.pid}-${crypto.randomBytes(8).toString("hex")}.tmp`
8779
+ );
8780
+ try {
8781
+ fs31.writeFileSync(temporary, `${JSON.stringify(owner)}
8782
+ `, {
8783
+ encoding: "utf8",
8784
+ flag: "wx",
8785
+ mode: 384
8786
+ });
8787
+ validateRegularPrivateFile(temporary, "temporary owner record");
8788
+ assertTransitionGuard(target, guard);
8789
+ fs31.renameSync(temporary, target.ownerFile);
8790
+ } catch (error) {
8791
+ try {
8792
+ assertTransitionGuard(target, guard);
8793
+ if (fs31.existsSync(temporary)) {
8794
+ validateRegularPrivateFile(temporary, "temporary owner record");
8795
+ fs31.unlinkSync(temporary);
8796
+ }
8797
+ } catch {
8798
+ guard.poisoned = true;
8799
+ }
8800
+ throw unknown(`cannot update owner for ${target.canonicalOutDir}`, error);
8801
+ }
8802
+ }
8803
+ function createOwner(target, kind, ownerPid, now) {
8804
+ const inspection = inspectProcess(ownerPid);
8805
+ if (inspection.state !== "alive") {
8806
+ throw unknown(`cannot verify lease owner process ${ownerPid}`);
8807
+ }
8808
+ return {
8809
+ version: OUTPUT_LEASE_PROTOCOL_VERSION,
8810
+ kind,
8811
+ key: target.key,
8812
+ canonicalRoot: target.canonicalRoot,
8813
+ canonicalOutDir: target.canonicalOutDir,
8814
+ generation: crypto.randomBytes(32).toString("base64url"),
8815
+ token: crypto.randomBytes(32).toString("base64url"),
8816
+ pid: ownerPid,
8817
+ processStartIdentity: inspection.startIdentity,
8818
+ createdAt: now,
8819
+ heartbeatAt: now
8820
+ };
8821
+ }
8822
+ function serializeInheritedToken(owner) {
8823
+ return Buffer.from(
8824
+ JSON.stringify({
8825
+ version: OUTPUT_LEASE_PROTOCOL_VERSION,
8826
+ key: owner.key,
8827
+ generation: owner.generation,
8828
+ token: owner.token
8829
+ }),
8830
+ "utf8"
8831
+ ).toString("base64url");
8832
+ }
8833
+ function parseInheritedToken(value) {
8834
+ let parsed;
8835
+ try {
8836
+ parsed = JSON.parse(
8837
+ Buffer.from(value, "base64url").toString("utf8")
8838
+ );
8839
+ } catch (error) {
8840
+ throw unknown("malformed inherited output lease token", error);
8841
+ }
8842
+ if (!parsed || typeof parsed !== "object") {
8843
+ throw unknown("malformed inherited output lease token");
8844
+ }
8845
+ const token = parsed;
8846
+ if (token.version !== OUTPUT_LEASE_PROTOCOL_VERSION || typeof token.key !== "string" || !KEY_PATTERN.test(token.key) || typeof token.generation !== "string" || !TOKEN_PATTERN.test(token.generation) || typeof token.token !== "string" || !TOKEN_PATTERN.test(token.token)) {
8847
+ throw unknown("malformed inherited output lease token");
8848
+ }
8849
+ return {
8850
+ version: token.version,
8851
+ key: token.key,
8852
+ generation: token.generation,
8853
+ token: token.token
8854
+ };
8855
+ }
8856
+ function verifyOwnerIsCurrent(owner) {
8857
+ const inspection = inspectProcess(owner.pid);
8858
+ return inspection.state === "alive" && inspection.startIdentity === owner.processStartIdentity;
8859
+ }
8860
+ function verifyInheritedGuarded(target, kind, encoded, guard) {
8861
+ const inherited = parseInheritedToken(encoded);
8862
+ if (inherited.key !== target.key) return null;
8863
+ assertTransitionGuard(target, guard);
8864
+ const owner = readOwner(target);
8865
+ if (owner.generation !== inherited.generation || owner.token !== inherited.token || owner.kind !== kind) {
8866
+ throw conflict(target, owner);
8867
+ }
8868
+ if (owner.canonicalRoot !== target.canonicalRoot) {
8869
+ throw unknown(
8870
+ `inherited owner root does not match ${target.canonicalRoot}`
8871
+ );
8872
+ }
8873
+ if (!verifyOwnerIsCurrent(owner)) {
8874
+ throw unknown(
8875
+ `inherited owner is not current for ${target.canonicalOutDir}`
8876
+ );
8877
+ }
8878
+ if (!isProcessOrDescendant(process.pid, owner.pid)) {
8879
+ throw unknown(
8880
+ `inherited token is not in the owner process tree for ${target.canonicalOutDir}`
8881
+ );
8882
+ }
8883
+ return owner;
8884
+ }
8885
+ function ownerState(owner, now, staleAfterMs) {
8886
+ const heartbeatAge = now - owner.heartbeatAt;
8887
+ if (!Number.isFinite(heartbeatAge) || heartbeatAge < -staleAfterMs) {
8888
+ return "unknown";
8889
+ }
8890
+ const inspection = inspectProcess(owner.pid);
8891
+ if (inspection.state === "alive" && inspection.startIdentity === owner.processStartIdentity) {
8892
+ return "active";
8893
+ }
8894
+ if (heartbeatAge <= staleAfterMs) return "unknown";
8895
+ if (inspection.state === "dead") return "stale";
8896
+ if (inspection.state === "alive" && inspection.startIdentity !== owner.processStartIdentity) {
8897
+ return "stale";
8898
+ }
8899
+ return "unknown";
8900
+ }
8901
+ function safeRemoveLeaseDirectory(target, directory, guard) {
8902
+ assertTransitionGuard(target, guard);
8903
+ if (path26.dirname(directory) !== target.lockRoot) {
8904
+ throw unknown("refusing to remove lease path outside trusted root");
8905
+ }
8906
+ validatePrivateDirectory(directory, "retired lease generation");
8907
+ for (const entry of fs31.readdirSync(directory)) {
8908
+ if (entry !== OWNER_FILE && !TEMP_OWNER_PATTERN.test(entry)) {
8909
+ throw unknown(`unexpected entry in retired lease generation ${entry}`);
8910
+ }
8911
+ const file = path26.join(directory, entry);
8912
+ validateRegularPrivateFile(file, "retired lease file");
8913
+ fs31.unlinkSync(file);
8914
+ }
8915
+ fs31.rmdirSync(directory);
8916
+ }
8917
+ function retireStaleGeneration(target, expected, guard) {
8918
+ assertTransitionGuard(target, guard);
8919
+ const current = readOwner(target);
8920
+ if (!sameGeneration(current, expected)) throw conflict(target, current);
8921
+ const retiredDir = path26.join(
8922
+ target.lockRoot,
8923
+ `${target.key}.stale-${expected.generation.slice(0, 12)}-${crypto.randomBytes(8).toString("hex")}`
8924
+ );
8925
+ try {
8926
+ fs31.renameSync(target.lockDir, retiredDir);
8927
+ guard.poisoned = true;
8928
+ const moved = readOwnerFromDirectory(target, retiredDir);
8929
+ if (!sameGeneration(moved, expected)) {
8930
+ throw unknown(`owner changed while retiring ${target.canonicalOutDir}`);
8931
+ }
8932
+ safeRemoveLeaseDirectory(target, retiredDir, guard);
8933
+ guard.poisoned = false;
8934
+ } catch (error) {
8935
+ if (error instanceof OutputLeaseProtocolError) throw error;
8936
+ throw unknown(
8937
+ `cannot atomically retire stale owner for ${target.canonicalOutDir}`,
8938
+ error
8939
+ );
8940
+ }
8941
+ }
8942
+ function releaseGenerationGuarded(target, expected, guard) {
8943
+ assertTransitionGuard(target, guard);
8944
+ if (!fs31.existsSync(target.lockDir)) return false;
8945
+ const current = readOwner(target);
8946
+ if (!sameGeneration(current, expected)) return false;
8947
+ const releasedDir = path26.join(
8948
+ target.lockRoot,
8949
+ `${target.key}.released-${expected.generation.slice(0, 12)}-${crypto.randomBytes(8).toString("hex")}`
8950
+ );
8951
+ try {
8952
+ assertTransitionGuard(target, guard);
8953
+ const exact = readOwner(target);
8954
+ if (!sameGeneration(exact, expected)) return false;
8955
+ fs31.renameSync(target.lockDir, releasedDir);
8956
+ guard.poisoned = true;
8957
+ const moved = readOwnerFromDirectory(target, releasedDir);
8958
+ if (!sameGeneration(moved, expected)) {
8959
+ throw unknown(`owner changed while releasing ${target.canonicalOutDir}`);
8960
+ }
8961
+ safeRemoveLeaseDirectory(target, releasedDir, guard);
8962
+ guard.poisoned = false;
8963
+ return true;
8964
+ } catch (error) {
8965
+ if (error instanceof OutputLeaseProtocolError) throw error;
8966
+ throw unknown(`cannot release owner for ${target.canonicalOutDir}`, error);
8967
+ }
8968
+ }
8969
+ function releaseByGeneration(target, expected) {
8970
+ return withTransitionGuard(
8971
+ target,
8972
+ (guard) => releaseGenerationGuarded(target, expected, guard)
8973
+ );
8974
+ }
8975
+ function refreshGenerationGuarded(target, expected, now, guard) {
8976
+ assertTransitionGuard(target, guard);
8977
+ const current = readOwner(target);
8978
+ if (!sameGeneration(current, expected)) {
8979
+ throw unknown(`owner changed while refreshing ${target.canonicalOutDir}`);
8980
+ }
8981
+ writeOwnerAtomically(target, { ...current, heartbeatAt: now }, guard);
8982
+ }
8983
+ function refreshByGeneration(target, expected, now) {
8984
+ withTransitionGuard(
8985
+ target,
8986
+ (guard) => refreshGenerationGuarded(target, expected, now, guard)
8987
+ );
8988
+ }
8989
+ function createLeaseHandle(target, owner, reentrant, options) {
8990
+ const now = options.now ?? Date.now;
8991
+ const manageLifetime = options.manageLifetime !== false && !reentrant;
8992
+ const heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
8993
+ const generation = {
8994
+ generation: owner.generation,
8995
+ token: owner.token
8996
+ };
8997
+ let released = false;
8998
+ let heartbeat;
8999
+ const signalHandlers = /* @__PURE__ */ new Map();
9000
+ const release = () => {
9001
+ if (released || reentrant) return false;
9002
+ released = true;
9003
+ if (heartbeat) clearInterval(heartbeat);
9004
+ process.removeListener("exit", onExit);
9005
+ for (const [signal, handler] of signalHandlers) {
9006
+ process.removeListener(signal, handler);
9007
+ }
9008
+ return releaseByGeneration(target, generation);
9009
+ };
9010
+ const onExit = () => {
9011
+ try {
9012
+ releaseByGeneration(target, generation);
9013
+ } catch {
9014
+ }
9015
+ };
9016
+ if (manageLifetime) {
9017
+ heartbeat = setInterval(() => {
9018
+ try {
9019
+ refreshByGeneration(target, generation, now());
9020
+ } catch {
9021
+ setImmediate(() => process.exit(1));
9022
+ }
9023
+ }, heartbeatIntervalMs);
9024
+ heartbeat.unref();
9025
+ process.once("exit", onExit);
9026
+ const signalExitCodes = {
9027
+ SIGHUP: 129,
9028
+ SIGINT: 130,
9029
+ SIGTERM: 143
9030
+ };
9031
+ for (const signal of Object.keys(signalExitCodes)) {
9032
+ const handler = () => {
9033
+ try {
9034
+ release();
9035
+ } finally {
9036
+ setImmediate(() => process.exit(signalExitCodes[signal] ?? 1));
9037
+ }
9038
+ };
9039
+ signalHandlers.set(signal, handler);
9040
+ process.once(signal, handler);
9041
+ }
9042
+ }
9043
+ return {
9044
+ target,
9045
+ owner,
9046
+ inheritedToken: serializeInheritedToken(owner),
9047
+ reentrant,
9048
+ refresh() {
9049
+ if (!reentrant && !released) {
9050
+ refreshByGeneration(target, generation, now());
9051
+ }
9052
+ },
9053
+ release
9054
+ };
9055
+ }
9056
+ function acquireOutputLease(options) {
9057
+ const now = options.now ?? Date.now;
9058
+ const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
9059
+ const env = options.env ?? process.env;
9060
+ const target = resolveOutputLeaseTarget(
9061
+ options.rootDir,
9062
+ options.outDir,
9063
+ options.lockRoot
9064
+ );
9065
+ return withTransitionGuard(target, (guard) => {
9066
+ const inheritedValue = env[OUTPUT_LEASE_ENV];
9067
+ if (inheritedValue) {
9068
+ const inheritedOwner = verifyInheritedGuarded(
9069
+ target,
9070
+ options.kind,
9071
+ inheritedValue,
9072
+ guard
9073
+ );
9074
+ if (inheritedOwner) {
9075
+ return createLeaseHandle(target, inheritedOwner, true, options);
9076
+ }
9077
+ }
9078
+ const ownerPid = options.ownerPid ?? process.pid;
9079
+ if (ownerPid !== process.pid) {
9080
+ if (options.manageLifetime !== false) {
9081
+ throw unknown("an external owner requires externally managed lifetime");
9082
+ }
9083
+ if (!isProcessOrDescendant(process.pid, ownerPid)) {
9084
+ throw unknown(`process ${ownerPid} is not an ancestor of the acquirer`);
9085
+ }
9086
+ }
9087
+ if (fs31.existsSync(target.lockDir)) {
9088
+ const existing = readOwner(target);
9089
+ const state = ownerState(existing, now(), staleAfterMs);
9090
+ if (state === "active") throw conflict(target, existing);
9091
+ if (state === "unknown") {
9092
+ throw unknown(`cannot prove owner state for ${target.canonicalOutDir}`);
9093
+ }
9094
+ retireStaleGeneration(target, existing, guard);
9095
+ }
9096
+ assertTransitionGuard(target, guard);
9097
+ try {
9098
+ fs31.mkdirSync(target.lockDir, { mode: 448 });
9099
+ validatePrivateDirectory(target.lockDir, "lease generation");
9100
+ const owner = createOwner(target, options.kind, ownerPid, now());
9101
+ fs31.writeFileSync(target.ownerFile, `${JSON.stringify(owner)}
9102
+ `, {
9103
+ encoding: "utf8",
9104
+ flag: "wx",
9105
+ mode: 384
9106
+ });
9107
+ validateRegularPrivateFile(target.ownerFile, "owner record");
9108
+ return createLeaseHandle(target, owner, false, options);
9109
+ } catch (error) {
9110
+ if (error instanceof OutputLeaseProtocolError) throw error;
9111
+ throw unknown(`cannot create owner for ${target.canonicalOutDir}`, error);
9112
+ }
9113
+ });
9114
+ }
9115
+ function targetAndEncoded(options) {
9116
+ const encoded = (options.env ?? process.env)[OUTPUT_LEASE_ENV];
9117
+ if (!encoded) throw unknown("inherited output lease token is missing");
9118
+ return {
9119
+ target: resolveOutputLeaseTarget(
9120
+ options.rootDir,
9121
+ options.outDir,
9122
+ options.lockRoot
9123
+ ),
9124
+ encoded
9125
+ };
9126
+ }
9127
+ function verifyInheritedOutputLease(options) {
9128
+ const { target, encoded } = targetAndEncoded(options);
9129
+ return withTransitionGuard(target, (guard) => {
9130
+ const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
9131
+ if (!owner) {
9132
+ throw unknown("inherited token targets a different output directory");
9133
+ }
9134
+ return owner;
9135
+ });
9136
+ }
9137
+ function refreshInheritedOutputLease(options) {
9138
+ const { target, encoded } = targetAndEncoded(options);
9139
+ withTransitionGuard(target, (guard) => {
9140
+ const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
9141
+ if (!owner) {
9142
+ throw unknown("inherited token targets a different output directory");
9143
+ }
9144
+ refreshGenerationGuarded(
9145
+ target,
9146
+ { generation: owner.generation, token: owner.token },
9147
+ (options.now ?? Date.now)(),
9148
+ guard
9149
+ );
9150
+ });
9151
+ }
9152
+ function poisonInheritedOutputLease(options) {
9153
+ const { target, encoded } = targetAndEncoded(options);
9154
+ withTransitionGuard(target, (guard) => {
9155
+ const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
9156
+ if (!owner) {
9157
+ throw unknown("inherited token targets a different output directory");
9158
+ }
9159
+ guard.poisoned = true;
9160
+ });
9161
+ }
9162
+ function releaseInheritedOutputLease(options) {
9163
+ const env = options.env ?? process.env;
9164
+ const encoded = env[OUTPUT_LEASE_ENV];
9165
+ if (!encoded) return false;
9166
+ const target = resolveOutputLeaseTarget(
9167
+ options.rootDir,
9168
+ options.outDir,
9169
+ options.lockRoot
9170
+ );
9171
+ return withTransitionGuard(target, (guard) => {
9172
+ const owner = verifyInheritedGuarded(target, options.kind, encoded, guard);
9173
+ if (!owner) return false;
9174
+ return releaseGenerationGuarded(
9175
+ target,
9176
+ { generation: owner.generation, token: owner.token },
9177
+ guard
9178
+ );
9179
+ });
9180
+ }
9181
+
9182
+ // src/commands/output-lease/index.ts
9183
+ function leaseOptions(options) {
9184
+ return {
9185
+ rootDir: options.root,
9186
+ outDir: options.outDir,
9187
+ kind: "production"
9188
+ };
9189
+ }
9190
+ function parseOwnerPid(value) {
9191
+ const pid = Number(value);
9192
+ if (!Number.isSafeInteger(pid) || pid <= 0) {
9193
+ throw new Error("MIAODA_PREVIEW_STATE_UNKNOWN: invalid wrapper owner PID");
9194
+ }
9195
+ return pid;
9196
+ }
9197
+ function acquireWrapperOutputLease(options) {
9198
+ const lease = acquireOutputLease({
9199
+ ...leaseOptions(options),
9200
+ ownerPid: parseOwnerPid(options.ownerPid),
9201
+ manageLifetime: false,
9202
+ // Only Vite children may reenter. A second wrapper must contend normally.
9203
+ env: {}
9204
+ });
9205
+ return lease.inheritedToken;
9206
+ }
9207
+ function verifyWrapperOutputLease(options) {
9208
+ verifyInheritedOutputLease(leaseOptions(options));
9209
+ }
9210
+ function releaseWrapperOutputLease(options) {
9211
+ return releaseInheritedOutputLease(leaseOptions(options));
9212
+ }
9213
+ function poisonWrapperOutputLease(options) {
9214
+ poisonInheritedOutputLease(leaseOptions(options));
9215
+ }
9216
+ function startWrapperOutputLeaseHeartbeat(options, intervalMs = 2e3) {
9217
+ refreshInheritedOutputLease(leaseOptions(options));
9218
+ const heartbeat = setInterval(() => {
9219
+ try {
9220
+ refreshInheritedOutputLease(leaseOptions(options));
9221
+ } catch (error) {
9222
+ console.error(error instanceof Error ? error.message : String(error));
9223
+ process.exit(1);
9224
+ }
9225
+ }, intervalMs);
9226
+ return heartbeat;
9227
+ }
9228
+ function addTargetOptions(command) {
9229
+ return command.requiredOption("--root <dir>", "Project root").requiredOption("--out-dir <dir>", "Managed output directory");
9230
+ }
9231
+ var acquireCommand = {
9232
+ name: "acquire",
9233
+ description: "Acquire a private production output lease for the build wrapper",
9234
+ register(program) {
9235
+ addTargetOptions(program.command(this.name)).requiredOption("--owner-pid <pid>", "PID of the wrapper shell").action((options) => {
9236
+ process.stdout.write(`${acquireWrapperOutputLease(options)}
9237
+ `);
9238
+ });
9239
+ }
9240
+ };
9241
+ var verifyCommand = {
9242
+ name: "verify",
9243
+ description: "Verify the inherited wrapper lease",
9244
+ register(program) {
9245
+ addTargetOptions(program.command(this.name)).action(
9246
+ (options) => {
9247
+ verifyWrapperOutputLease(options);
9248
+ }
9249
+ );
9250
+ }
9251
+ };
9252
+ var heartbeatCommand = {
9253
+ name: "heartbeat",
9254
+ description: "Maintain the inherited wrapper lease",
9255
+ register(program) {
9256
+ addTargetOptions(program.command(this.name)).action(
9257
+ (options) => {
9258
+ startWrapperOutputLeaseHeartbeat(options);
9259
+ }
9260
+ );
9261
+ }
9262
+ };
9263
+ var poisonCommand = {
9264
+ name: "poison",
9265
+ description: "Durably quarantine an inherited wrapper lease",
9266
+ register(program) {
9267
+ addTargetOptions(program.command(this.name)).action(
9268
+ (options) => {
9269
+ poisonWrapperOutputLease(options);
9270
+ }
9271
+ );
9272
+ }
9273
+ };
9274
+ var releaseCommand = {
9275
+ name: "release",
9276
+ description: "Release the inherited wrapper lease",
9277
+ register(program) {
9278
+ addTargetOptions(program.command(this.name)).action(
9279
+ (options) => {
9280
+ if (!releaseWrapperOutputLease(options)) {
9281
+ throw new Error(
9282
+ `${OUTPUT_LEASE_ENV}: the current token does not own this lease`
9283
+ );
9284
+ }
9285
+ }
9286
+ );
9287
+ }
9288
+ };
9289
+ var outputLeaseCommandGroup = {
9290
+ name: "output-lease",
9291
+ description: "Private build output ownership protocol",
9292
+ commands: [
9293
+ acquireCommand,
9294
+ verifyCommand,
9295
+ heartbeatCommand,
9296
+ poisonCommand,
9297
+ releaseCommand
9298
+ ]
9299
+ };
9300
+
8432
9301
  // src/commands/index.ts
8433
9302
  var commands = [
8434
9303
  genDbSchemaCommand,
@@ -8439,18 +9308,19 @@ var commands = [
8439
9308
  componentCommandGroup,
8440
9309
  migrationCommand,
8441
9310
  readLogsCommand,
8442
- buildCommandGroup
9311
+ buildCommandGroup,
9312
+ outputLeaseCommandGroup
8443
9313
  ];
8444
9314
 
8445
9315
  // src/index.ts
8446
9316
  for (const filename of [".env.local", ".env"]) {
8447
- const envPath = path26.join(process.cwd(), filename);
8448
- if (fs31.existsSync(envPath)) {
9317
+ const envPath = path27.join(process.cwd(), filename);
9318
+ if (fs32.existsSync(envPath)) {
8449
9319
  dotenvConfig({ path: envPath });
8450
9320
  }
8451
9321
  }
8452
- var __dirname = path26.dirname(fileURLToPath5(import.meta.url));
8453
- var pkg = JSON.parse(fs31.readFileSync(path26.join(__dirname, "../package.json"), "utf-8"));
9322
+ var __dirname = path27.dirname(fileURLToPath5(import.meta.url));
9323
+ var pkg = JSON.parse(fs32.readFileSync(path27.join(__dirname, "../package.json"), "utf-8"));
8454
9324
  var cli = new FullstackCLI(pkg.version);
8455
9325
  cli.useAll(commands);
8456
9326
  cli.run();