@amalgm/shell 0.1.20 → 0.1.22

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/PURPOSE.md CHANGED
@@ -85,19 +85,20 @@ shell only assembles.
85
85
  tree remains intact. A failed install leaves a clean retry boundary, never
86
86
  a partial portable tree.
87
87
 
88
- 16. **The host owns shutdown.** A command returns its service result and the
89
- host then flushes every filesystem event Watch observed, closes its
90
- coverage, checks once more for events queued during that flush, and only
91
- then tears down the shared runtime tunnel. Closing an unchanged watcher
92
- does not invent a full scan. A command may not close transport underneath
93
- an in-flight registration. A gateway response belongs to the exact wire
94
- that delivered its request; if that source closes first, the late response
95
- is discarded cleanly rather than sent through a replacement or closed wire.
96
- Closing is a one-way lifecycle boundary: a rescan that was already in
97
- flight may finish its durable work, but it can never recreate watcher
98
- handles afterward. Each tracked root owns one recursive Node watcher, not
99
- one handle per descendant directory; the Node version floor supplies this
100
- primitive on macOS, Linux, and Windows.
88
+ 16. **The process that owns Watch owns its shutdown.** A running host flushes
89
+ every filesystem event Watch observed, closes coverage, checks once more
90
+ for queued events, and only then tears down its wire. The one-shot
91
+ `files register` command is different by design: it starts no Watch, SQLite,
92
+ or cloud work; it renders the durable reference and returns. A running host
93
+ observes that declaration, while a later resume covers an interval with no
94
+ running host. Closing an unchanged watcher does not invent a full scan. A
95
+ gateway response belongs to the exact wire that delivered its request; if
96
+ that source closes first, the late response is discarded cleanly rather
97
+ than sent through a replacement or closed wire. Closing is a one-way
98
+ lifecycle boundary: an in-flight rescan may finish its durable work, but it
99
+ can never recreate watcher handles afterward. Each tracked root owns one
100
+ recursive Node watcher, not one handle per descendant directory; the Node
101
+ version floor supplies this primitive on macOS, Linux, and Windows.
101
102
 
102
103
  17. **Independent content may travel concurrently; authority remains atomic.**
103
104
  The host bounds parallel uploads for small immutable objects and processes
@@ -117,9 +118,9 @@ shell only assembles.
117
118
  decides which entity types have artifacts, which chunks are missing, when
118
119
  an upload manifest may commit, and when a download is verified. The Shell
119
120
  adapter supplies seekable immutable-cache reads, the authority inventory
120
- probe, bounded R2 requests, and a machine-local content-addressed chunk
121
- cache; it never scans, registers, materializes, or publishes inside those
122
- operations.
121
+ probe, bounded R2 requests, and a machine-local content-addressed artifact
122
+ cache with reusable block locators; it never scans, registers,
123
+ materializes, or publishes inside those operations.
123
124
  Control and integrity metadata remains JSON, while immutable block and Git
124
125
  parcel bytes use the authenticated binary WebSocket rail without base64.
125
126
 
@@ -143,7 +144,7 @@ shell only assembles.
143
144
  untracked leaves. Git ignore never becomes Amalgm enrollment policy.
144
145
 
145
146
  23. **Cold add never rereads a repository graph.** Download verifies and
146
- assembles each content-addressed transport to a file, streams its Git pack
147
+ seals each content-addressed transport to a file, streams its Git pack
147
148
  range directly into `index-pack`, and derives local SQLite rows from the
148
149
  transported UUID/type/payload identity map plus filesystem stats. It does
149
150
  not buffer the complete repository parcel or reclassify checked-out files.
@@ -154,6 +155,14 @@ shell only assembles.
154
155
  recomputed from authoritative local ground; they never become a legacy
155
156
  compatibility requirement and never prevent the machine from resuming.
156
157
 
158
+ 25. **Cold transfer performs each byte operation once.** Registration stores
159
+ capture-derived block evidence beside the sealed source, so upload reads
160
+ that source once to send it. Download places verified network blocks
161
+ directly into one resumable artifact, derives the whole hash from those
162
+ in-flight buffers, and then hands the sealed file to the registered type's
163
+ adapter. Per-block duplicates followed by a second assembled file are not
164
+ a machine-cache design.
165
+
157
166
  ## Relationship to amalgm-engine
158
167
 
159
168
  Stage 1 cut the bricks out of the engine. Stage 2 proves them here, composed.
package/dist/cli.js CHANGED
@@ -64,11 +64,12 @@ export async function runCli(argv = process.argv) {
64
64
  if (parsed.positionals.length !== 2) {
65
65
  throw new Error(`Usage: amalgm files ${filesCommand} <${filesCommand === "register" ? "path" : "workspace-id"}>`);
66
66
  }
67
- await host.resume();
68
- const ports = host.filesCommandPorts();
69
67
  const result = filesCommand === "register"
70
- ? await registerFilesWorkspace(parsed.positionals[1], ports.register)
71
- : await addFilesWorkspace(parsed.positionals[1], process.cwd(), ports.add);
68
+ ? await registerFilesWorkspace(parsed.positionals[1], await host.filesRegisterPorts())
69
+ : await (async () => {
70
+ await host.resume();
71
+ return addFilesWorkspace(parsed.positionals[1], process.cwd(), host.filesCommandPorts().add);
72
+ })();
72
73
  process.stdout.write(`AMALGM_FILES ${JSON.stringify({ command: filesCommand, ...result })}\n`);
73
74
  return 0;
74
75
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Machine-local immutable content cache.
3
+ *
4
+ * Network blocks land directly at their final offset in one partial artifact.
5
+ * Small marker files make an interrupted write resumable, and sealed-artifact
6
+ * block locators let another file reuse equal bytes without storing a second
7
+ * copy of every block. The portable transfer law remains in @amalgm/live;
8
+ * this module owns only Node filesystem effects.
9
+ */
10
+ import { type ContentManifest, type TransferArtifact } from "@amalgm/live";
11
+ export interface SealedContentFile {
12
+ readonly file: string;
13
+ readonly bytes: number;
14
+ readonly contentHash: string;
15
+ }
16
+ /** One artifact download. Calls may write different block indexes in
17
+ * parallel; sealing is legal only after the portable download operation has
18
+ * awaited them all. */
19
+ export declare class ContentCacheDownload {
20
+ private readonly cacheDir;
21
+ readonly artifact: TransferArtifact;
22
+ readonly manifest: ContentManifest;
23
+ private readonly finalFile;
24
+ private readonly temporaryFile;
25
+ private readonly markerDir;
26
+ private readonly offsets;
27
+ private readonly complete;
28
+ private readonly wholeHash;
29
+ private readonly pendingHash;
30
+ private nextHashIndex;
31
+ private handle;
32
+ private sealed;
33
+ constructor(cacheDir: string, artifact: TransferArtifact, manifest: ContentManifest);
34
+ private partialHandle;
35
+ private acceptForWholeHash;
36
+ private markerFile;
37
+ /** Verify and stage one already-local block. A false result means the
38
+ * authority must provide it. */
39
+ hasChunk(index: number): Promise<boolean>;
40
+ /** Persist one block already verified by @amalgm/live. */
41
+ putChunk(index: number, bytes: Uint8Array): Promise<void>;
42
+ /** Commit the complete artifact and its tiny reusable-block index. Cold
43
+ * downloads verify the whole hash from the received buffers, so sealing
44
+ * performs no second complete disk read. */
45
+ seal(): Promise<SealedContentFile>;
46
+ /** Keep partial bytes and markers for the next attempt, but release the
47
+ * descriptor held by this process. */
48
+ close(): Promise<void>;
49
+ }
50
+ /** Hash one file without buffering it. Used only when evidence predates the
51
+ * sealed-manifest cache. */
52
+ export declare function hashContentFile(file: string): Promise<string>;
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Machine-local immutable content cache.
3
+ *
4
+ * Network blocks land directly at their final offset in one partial artifact.
5
+ * Small marker files make an interrupted write resumable, and sealed-artifact
6
+ * block locators let another file reuse equal bytes without storing a second
7
+ * copy of every block. The portable transfer law remains in @amalgm/live;
8
+ * this module owns only Node filesystem effects.
9
+ */
10
+ import { createHash } from "node:crypto";
11
+ import { chmodSync, createReadStream, existsSync, readFileSync, renameSync, rmSync, statSync, } from "node:fs";
12
+ import { open } from "node:fs/promises";
13
+ import { join } from "node:path";
14
+ import { stableJson, } from "@amalgm/live";
15
+ import { atomicWrite, ensurePrivateDir } from "./filesystem.js";
16
+ const sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
17
+ const contentFile = (cacheDir, contentHash) => join(cacheDir, `${contentHash}.bin`);
18
+ const manifestFile = (cacheDir, contentHash) => join(cacheDir, "manifests", `${contentHash}.json`);
19
+ const partialFile = (cacheDir, contentHash) => join(cacheDir, "partials", `${contentHash}.bin`);
20
+ const partialMarkers = (cacheDir, contentHash) => join(cacheDir, "partials", contentHash);
21
+ const blockLocatorFile = (cacheDir, blockHash) => join(cacheDir, "blocks", `${blockHash}.json`);
22
+ function parseLocator(file) {
23
+ try {
24
+ const value = JSON.parse(readFileSync(file, "utf8"));
25
+ return typeof value.contentHash === "string"
26
+ && Number.isSafeInteger(value.offset) && value.offset >= 0
27
+ && Number.isSafeInteger(value.bytes) && value.bytes > 0
28
+ ? value
29
+ : null;
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ async function readRange(file, offset, bytes) {
36
+ const handle = await open(file, "r");
37
+ const output = Buffer.allocUnsafe(bytes);
38
+ try {
39
+ let cursor = 0;
40
+ while (cursor < bytes) {
41
+ const result = await handle.read(output, cursor, bytes - cursor, offset + cursor);
42
+ if (result.bytesRead === 0)
43
+ throw new Error("content cache range is truncated");
44
+ cursor += result.bytesRead;
45
+ }
46
+ return output;
47
+ }
48
+ finally {
49
+ await handle.close();
50
+ }
51
+ }
52
+ async function writeRange(handle, offset, bytes) {
53
+ let cursor = 0;
54
+ while (cursor < bytes.byteLength) {
55
+ const result = await handle.write(bytes, cursor, bytes.byteLength - cursor, offset + cursor);
56
+ if (result.bytesWritten === 0)
57
+ throw new Error("content cache write made no progress");
58
+ cursor += result.bytesWritten;
59
+ }
60
+ }
61
+ /** One artifact download. Calls may write different block indexes in
62
+ * parallel; sealing is legal only after the portable download operation has
63
+ * awaited them all. */
64
+ export class ContentCacheDownload {
65
+ cacheDir;
66
+ artifact;
67
+ manifest;
68
+ finalFile;
69
+ temporaryFile;
70
+ markerDir;
71
+ offsets;
72
+ complete;
73
+ wholeHash = createHash("sha256");
74
+ pendingHash = new Map();
75
+ nextHashIndex = 0;
76
+ handle = null;
77
+ sealed = false;
78
+ constructor(cacheDir, artifact, manifest) {
79
+ this.cacheDir = cacheDir;
80
+ this.artifact = artifact;
81
+ this.manifest = manifest;
82
+ this.finalFile = contentFile(cacheDir, artifact.contentHash);
83
+ this.temporaryFile = partialFile(cacheDir, artifact.contentHash);
84
+ this.markerDir = partialMarkers(cacheDir, artifact.contentHash);
85
+ let offset = 0;
86
+ this.offsets = manifest.chunks.map((chunk) => {
87
+ const result = offset;
88
+ offset += chunk.bytes;
89
+ return result;
90
+ });
91
+ this.complete = existsSync(this.finalFile)
92
+ && statSync(this.finalFile).size === manifest.bytes;
93
+ }
94
+ partialHandle() {
95
+ if (!this.handle) {
96
+ this.handle = (async () => {
97
+ ensurePrivateDir(this.markerDir);
98
+ let handle;
99
+ try {
100
+ handle = await open(this.temporaryFile, "r+");
101
+ }
102
+ catch (error) {
103
+ if (error.code !== "ENOENT")
104
+ throw error;
105
+ handle = await open(this.temporaryFile, "wx+", 0o600);
106
+ }
107
+ await handle.truncate(this.manifest.bytes);
108
+ return handle;
109
+ })();
110
+ }
111
+ return this.handle;
112
+ }
113
+ acceptForWholeHash(index, bytes) {
114
+ this.pendingHash.set(index, bytes);
115
+ while (true) {
116
+ const next = this.pendingHash.get(this.nextHashIndex);
117
+ if (!next)
118
+ return;
119
+ this.wholeHash.update(next);
120
+ this.pendingHash.delete(this.nextHashIndex);
121
+ this.nextHashIndex += 1;
122
+ }
123
+ }
124
+ markerFile(index) {
125
+ return join(this.markerDir, `${index}.json`);
126
+ }
127
+ /** Verify and stage one already-local block. A false result means the
128
+ * authority must provide it. */
129
+ async hasChunk(index) {
130
+ const expected = this.manifest.chunks[index];
131
+ const offset = this.offsets[index];
132
+ if (!expected || offset === undefined)
133
+ throw new Error(`content manifest has no chunk ${index}`);
134
+ if (this.complete)
135
+ return true;
136
+ try {
137
+ const marker = JSON.parse(readFileSync(this.markerFile(index), "utf8"));
138
+ if (marker.sha256 === expected.sha256 && marker.bytes === expected.bytes) {
139
+ const bytes = await readRange(this.temporaryFile, offset, expected.bytes);
140
+ if (sha256Hex(bytes) === expected.sha256) {
141
+ this.acceptForWholeHash(index, bytes);
142
+ return true;
143
+ }
144
+ }
145
+ }
146
+ catch {
147
+ // A missing or invalid partial marker is an ordinary cache miss.
148
+ }
149
+ const locator = parseLocator(blockLocatorFile(this.cacheDir, expected.sha256));
150
+ if (!locator || locator.bytes !== expected.bytes)
151
+ return false;
152
+ try {
153
+ const source = contentFile(this.cacheDir, locator.contentHash);
154
+ const bytes = await readRange(source, locator.offset, locator.bytes);
155
+ if (sha256Hex(bytes) !== expected.sha256)
156
+ return false;
157
+ await writeRange(await this.partialHandle(), offset, bytes);
158
+ atomicWrite(this.markerFile(index), stableJson(expected));
159
+ this.acceptForWholeHash(index, bytes);
160
+ return true;
161
+ }
162
+ catch {
163
+ return false;
164
+ }
165
+ }
166
+ /** Persist one block already verified by @amalgm/live. */
167
+ async putChunk(index, bytes) {
168
+ const expected = this.manifest.chunks[index];
169
+ const offset = this.offsets[index];
170
+ if (!expected || offset === undefined)
171
+ throw new Error(`content manifest has no chunk ${index}`);
172
+ await writeRange(await this.partialHandle(), offset, bytes);
173
+ atomicWrite(this.markerFile(index), stableJson(expected));
174
+ this.acceptForWholeHash(index, bytes);
175
+ }
176
+ /** Commit the complete artifact and its tiny reusable-block index. Cold
177
+ * downloads verify the whole hash from the received buffers, so sealing
178
+ * performs no second complete disk read. */
179
+ async seal() {
180
+ if (this.sealed)
181
+ throw new Error("content artifact is already sealed");
182
+ this.sealed = true;
183
+ if (this.complete) {
184
+ return {
185
+ file: this.finalFile,
186
+ bytes: this.manifest.bytes,
187
+ contentHash: this.manifest.contentHash,
188
+ };
189
+ }
190
+ const handle = await this.partialHandle();
191
+ if (this.nextHashIndex !== this.manifest.chunks.length || this.pendingHash.size !== 0) {
192
+ throw new Error("content artifact is missing verified chunks");
193
+ }
194
+ if (this.wholeHash.digest("hex") !== this.manifest.contentHash) {
195
+ throw new Error(`cloud content ${this.manifest.contentHash} is corrupt`);
196
+ }
197
+ await handle.sync();
198
+ await handle.close();
199
+ this.handle = null;
200
+ renameSync(this.temporaryFile, this.finalFile);
201
+ chmodSync(this.finalFile, 0o600);
202
+ atomicWrite(manifestFile(this.cacheDir, this.artifact.contentHash), stableJson(this.manifest));
203
+ for (const [index, chunk] of this.manifest.chunks.entries()) {
204
+ atomicWrite(blockLocatorFile(this.cacheDir, chunk.sha256), stableJson({
205
+ contentHash: this.artifact.contentHash,
206
+ offset: this.offsets[index],
207
+ bytes: chunk.bytes,
208
+ }));
209
+ }
210
+ rmSync(this.markerDir, { recursive: true, force: true });
211
+ return {
212
+ file: this.finalFile,
213
+ bytes: this.manifest.bytes,
214
+ contentHash: this.manifest.contentHash,
215
+ };
216
+ }
217
+ /** Keep partial bytes and markers for the next attempt, but release the
218
+ * descriptor held by this process. */
219
+ async close() {
220
+ if (!this.handle)
221
+ return;
222
+ const handle = await this.handle;
223
+ this.handle = null;
224
+ await handle.close();
225
+ }
226
+ }
227
+ /** Hash one file without buffering it. Used only when evidence predates the
228
+ * sealed-manifest cache. */
229
+ export async function hashContentFile(file) {
230
+ const hash = createHash("sha256");
231
+ for await (const chunk of createReadStream(file))
232
+ hash.update(chunk);
233
+ return hash.digest("hex");
234
+ }
235
+ //# sourceMappingURL=content-cache-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-cache-host.js","sourceRoot":"","sources":["../src/content-cache-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { FilesRegisterPorts } from "@amalgm/live/files";
2
+ export declare const WORKSPACE_UUID: RegExp;
3
+ export declare function workspaceBindingDir(userRoot: string, deviceId: string): string;
4
+ /** Local effects for Live's declaration-only register operation. This host has
5
+ * no SQLite, Watch, content, or cloud dependency by design. */
6
+ export declare function createFilesRegisterPorts(input: {
7
+ readonly userRoot: string;
8
+ readonly deviceId: string;
9
+ readonly onReferenceDeclared?: () => void;
10
+ }): FilesRegisterPorts;
11
+ export declare function pathExists(path: string): boolean;
12
+ export declare function pathWithin(root: string, candidate: string): boolean;
13
+ export declare function referenceWorkspaceId(referencePath: string, bindingDir: string): string | null;
@@ -0,0 +1,134 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { existsSync, lstatSync, realpathSync, readdirSync, readlinkSync, rmSync, statSync, symlinkSync, } from "node:fs";
3
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from "node:path";
4
+ import { liveMachineStateDir } from "@amalgm/core/identity";
5
+ import { ensurePrivateDir } from "./filesystem.js";
6
+ export const WORKSPACE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
7
+ export function workspaceBindingDir(userRoot, deviceId) {
8
+ return join(liveMachineStateDir(userRoot, deviceId), "bindings");
9
+ }
10
+ /** Local effects for Live's declaration-only register operation. This host has
11
+ * no SQLite, Watch, content, or cloud dependency by design. */
12
+ export function createFilesRegisterPorts(input) {
13
+ const bindingDir = workspaceBindingDir(input.userRoot, input.deviceId);
14
+ return {
15
+ inspectExternalWorkspace: async (requestedPath) => {
16
+ const resolved = realpathSync(resolve(requestedPath));
17
+ const stats = statSync(resolved);
18
+ if (!stats.isDirectory())
19
+ throw new Error("workspace path must name a directory");
20
+ if (pathWithin(input.userRoot, resolved)) {
21
+ throw new Error("files register is only for a workspace outside the Amalgm user ground");
22
+ }
23
+ let workspaceId = null;
24
+ if (existsSync(bindingDir)) {
25
+ for (const entry of readdirSync(bindingDir, { withFileTypes: true })) {
26
+ if (!entry.isSymbolicLink() || !WORKSPACE_UUID.test(entry.name))
27
+ continue;
28
+ try {
29
+ const found = statSync(join(bindingDir, entry.name));
30
+ if (found.dev === stats.dev && found.ino === stats.ino) {
31
+ workspaceId = entry.name.toLowerCase();
32
+ break;
33
+ }
34
+ }
35
+ catch {
36
+ // A broken binding is not evidence about this physical root.
37
+ }
38
+ }
39
+ }
40
+ const name = basename(resolved);
41
+ if (!name)
42
+ throw new Error("workspace path has no name");
43
+ return { absolutePath: resolved, name, workspaceId };
44
+ },
45
+ randomUuid: randomUUID,
46
+ declareWorkspaceReference: async ({ workspaceId, workspaceName, absolutePath }) => {
47
+ ensurePrivateDir(bindingDir);
48
+ const binding = join(bindingDir, workspaceId);
49
+ let createdBinding = false;
50
+ if (pathExists(binding)) {
51
+ if (realpathSync(binding) !== realpathSync(absolutePath)) {
52
+ throw new Error(`workspace ${workspaceId} is already bound to different ground`);
53
+ }
54
+ }
55
+ else {
56
+ symlinkSync(absolutePath, binding, "dir");
57
+ createdBinding = true;
58
+ }
59
+ try {
60
+ const workspaceDir = join(input.userRoot, "workspaces");
61
+ ensurePrivateDir(workspaceDir);
62
+ const existing = findReference(workspaceDir, bindingDir, workspaceId);
63
+ if (existing) {
64
+ if (createdBinding)
65
+ input.onReferenceDeclared?.();
66
+ return {
67
+ created: false,
68
+ referenceName: basename(existing),
69
+ referencePath: existing,
70
+ };
71
+ }
72
+ const referencePath = availableReferencePath(workspaceDir, workspaceName);
73
+ symlinkSync(relative(dirname(referencePath), binding), referencePath);
74
+ input.onReferenceDeclared?.();
75
+ return {
76
+ created: true,
77
+ referenceName: basename(referencePath),
78
+ referencePath,
79
+ };
80
+ }
81
+ catch (error) {
82
+ if (createdBinding)
83
+ rmSync(binding, { force: true });
84
+ throw error;
85
+ }
86
+ },
87
+ };
88
+ }
89
+ export function pathExists(path) {
90
+ try {
91
+ lstatSync(path);
92
+ return true;
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ }
98
+ export function pathWithin(root, candidate) {
99
+ const rel = relative(resolve(root), resolve(candidate));
100
+ return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
101
+ }
102
+ export function referenceWorkspaceId(referencePath, bindingDir) {
103
+ try {
104
+ const target = resolve(dirname(referencePath), readlinkSync(referencePath));
105
+ const id = basename(target).toLowerCase();
106
+ return resolve(dirname(target)) === resolve(bindingDir) && WORKSPACE_UUID.test(id) ? id : null;
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ function findReference(workspaceDir, bindingDir, workspaceId) {
113
+ for (const entry of readdirSync(workspaceDir, { withFileTypes: true })) {
114
+ if (!entry.isSymbolicLink())
115
+ continue;
116
+ const path = join(workspaceDir, entry.name);
117
+ if (referenceWorkspaceId(path, bindingDir) === workspaceId)
118
+ return path;
119
+ }
120
+ return null;
121
+ }
122
+ function availableReferencePath(workspaceDir, requestedName) {
123
+ const name = requestedName.trim();
124
+ if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\\")) {
125
+ throw new Error("workspace name must be one path segment");
126
+ }
127
+ for (let suffix = 1; suffix < 10_000; suffix += 1) {
128
+ const candidate = join(workspaceDir, suffix === 1 ? name : `${name} (${suffix})`);
129
+ if (!pathExists(candidate))
130
+ return candidate;
131
+ }
132
+ throw new Error(`no available reference name for ${name}`);
133
+ }
134
+ //# sourceMappingURL=files-register-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files-register-host.js","sourceRoot":"","sources":["../src/files-register-host.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { type JoinMachineInput, type MachineSetupPorts, type ReadyMachine } from "@amalgm/core/identity";
2
+ import type { FilesRegisterPorts } from "@amalgm/live/files";
2
3
  import { UserGroundHost } from "./user-ground-host.js";
3
4
  export interface MachineHostOptions {
4
5
  readonly homedir?: string;
@@ -45,6 +46,9 @@ export declare class MachineHost {
45
46
  resume(): Promise<ReadyMachine>;
46
47
  selectedPaths(): MachinePaths;
47
48
  filesCommandPorts(): ReturnType<UserGroundHost["filesCommandPorts"]>;
49
+ /** Registration is a local declaration, so it proves the selected machine
50
+ * from disk without starting Watch, SQLite convergence, or a cloud wire. */
51
+ filesRegisterPorts(): Promise<FilesRegisterPorts>;
48
52
  close(): Promise<void>;
49
53
  private ensureInstallationRoot;
50
54
  private paths;
@@ -1,10 +1,11 @@
1
1
  import { randomBytes, randomUUID } from "node:crypto";
2
2
  import { existsSync, readdirSync, rmSync } from "node:fs";
3
3
  import { homedir as osHomedir, hostname, platform, release } from "node:os";
4
- import { join } from "node:path";
4
+ import { basename, join } from "node:path";
5
5
  import { buildRootManifest, joinMachine, mergeRecordAuth, resumeMachine, scopedAmalgmDir, userMachineDir, } from "@amalgm/core/identity";
6
6
  import { atomicJson, atomicJsonSet, ensurePrivateDir, readJson, } from "./filesystem.js";
7
7
  import { createGateway } from "./gateway.js";
8
+ import { createFilesRegisterPorts } from "./files-register-host.js";
8
9
  import { shellReleaseCatalog, shellReleaseVerifier } from "./release-catalog.js";
9
10
  import { UserGroundHost } from "./user-ground-host.js";
10
11
  /** The production effects around Core's machine-setup operation. It does not
@@ -136,6 +137,20 @@ export class MachineHost {
136
137
  throw new Error("Files commands require a resumed authenticated machine");
137
138
  return this.groundHost.filesCommandPorts();
138
139
  }
140
+ /** Registration is a local declaration, so it proves the selected machine
141
+ * from disk without starting Watch, SQLite convergence, or a cloud wire. */
142
+ async filesRegisterPorts() {
143
+ this.resolveUserForResume();
144
+ const machine = await this.loadMachine();
145
+ if (!machine?.ready)
146
+ throw new Error("Files commands require a ready authenticated machine");
147
+ const paths = this.paths();
148
+ const deviceId = text(machine.publicRecord.device_id);
149
+ if (!deviceId || deviceId !== basename(paths.machineDir)) {
150
+ throw new Error("stored machine identity does not match this physical device");
151
+ }
152
+ return createFilesRegisterPorts({ userRoot: paths.userRoot, deviceId });
153
+ }
139
154
  async close() {
140
155
  const runtimes = [...this.running];
141
156
  this.running.clear();