@volter/twin-world 0.1.4 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/twin-world",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "World configs for twins: boot named local runtimes, allocate ports, generate world.env/instance.json, and run apps against fake-key twin worlds.",
5
5
  "keywords": [
6
6
  "twin",
package/src/resources.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statfsSync, writeFileSync } from 'node:fs';
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statfsSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { homedir, hostname, totalmem } from 'node:os';
4
4
  import { dirname, join, resolve } from 'node:path';
@@ -13,6 +13,7 @@ export const DEFAULT_WORLD_RESOURCES: WorldResourceRequirements = {
13
13
 
14
14
  export type WorldResourceClaim = {
15
15
  world: string;
16
+ root?: string;
16
17
  identity: string;
17
18
  ownerPid: number;
18
19
  holderPids: number[];
@@ -77,7 +78,17 @@ function writePrivate(path: string, contents: string): void {
77
78
  writeFileSync(path, contents, { mode: 0o600 });
78
79
  }
79
80
 
80
- function capacity(root: string, claims: WorldResourceClaim[]): WorldResourceRequirements {
81
+ /** Resolve old claims without rewriting them. Unknown locations stay charged on every
82
+ * filesystem rather than silently allowing a reservation we cannot locate. */
83
+ function sharesStorage(root: string, claim: WorldResourceClaim): boolean {
84
+ try {
85
+ const claimRoot = claim.root ?? resolve(dirname(claim.log), '../../..');
86
+ if (claimIdentity(claimRoot, claim.world) !== claim.identity) return true;
87
+ return statSync(claimRoot).dev === statSync(root).dev;
88
+ } catch { return true; }
89
+ }
90
+
91
+ export function worldResourceCapacity(root: string, claims: WorldResourceClaim[]): WorldResourceRequirements {
81
92
  const fs = statfsSync(resolve(root));
82
93
  const diskFreeMiB = Math.floor((Number(fs.bavail) * Number(fs.bsize)) / MIB);
83
94
  const memoryTotalMiB = Math.floor(totalmem() / MIB);
@@ -85,7 +96,8 @@ function capacity(root: string, claims: WorldResourceClaim[]): WorldResourceRequ
85
96
  const diskTotalMiB = Math.floor((Number(fs.blocks) * Number(fs.bsize)) / MIB);
86
97
  const diskSafetyMiB = Math.max(512, Math.min(2048, Math.floor(diskTotalMiB * 0.05)));
87
98
  const reservedMemoryMiB = claims.reduce((sum, claim) => sum + claim.resources.memoryMiB, 0);
88
- const reservedDiskMiB = claims.reduce((sum, claim) => sum + claim.resources.writableStorageMiB, 0);
99
+ const reservedDiskMiB = claims.filter((claim) => sharesStorage(root, claim))
100
+ .reduce((sum, claim) => sum + claim.resources.writableStorageMiB, 0);
89
101
  return {
90
102
  memoryMiB: Math.max(0, memoryTotalMiB - memorySafetyMiB - reservedMemoryMiB),
91
103
  writableStorageMiB: Math.max(0, diskFreeMiB - diskSafetyMiB - reservedDiskMiB),
@@ -106,7 +118,7 @@ export function claimWorldResources(root: string, world: string, requested: Worl
106
118
  return withFileLock(join(dir, 'claims.lock'), () => {
107
119
  const identity = claimIdentity(root, world);
108
120
  const claims = liveClaims(identity);
109
- const available = capacity(root, claims);
121
+ const available = worldResourceCapacity(root, claims);
110
122
  const shortages: string[] = [];
111
123
  if (requested.memoryMiB > available.memoryMiB) shortages.push(`memory requires ${requested.memoryMiB} MiB, ${available.memoryMiB} MiB available`);
112
124
  if (requested.writableStorageMiB > available.writableStorageMiB) {
@@ -120,6 +132,7 @@ export function claimWorldResources(root: string, world: string, requested: Worl
120
132
  }
121
133
  const claim: WorldResourceClaim = {
122
134
  world,
135
+ root: resolve(root),
123
136
  identity,
124
137
  ownerPid: process.pid,
125
138
  holderPids: [],
package/src/runtime.ts CHANGED
@@ -2125,7 +2125,7 @@ function worldAttachedCommandEnv(name: string, root: string): { instance: Return
2125
2125
  if (sealed) throw new Error(`sealed world "${name}": attachment proxy failed; refusing to run the command (${error instanceof Error ? error.message : String(error)})`);
2126
2126
  // Keep env-only redirect if the ambient proxy cannot start.
2127
2127
  }
2128
- return { instance: status, env: { ...admittedEnv(stripEnvOf(status.env)), ...status.env, ...proxyEnv } };
2128
+ return { instance: status, env: { ...admittedEnv(stripEnvOf(status.env)), ...status.env, ...proxyEnv, VOLTER_WORLD: name } };
2129
2129
  }
2130
2130
 
2131
2131
  export function runWithWorldEnv(name: string, command: string[], root = process.cwd(), options: { cwd?: string } = {}): number {