@telepath-computer/autofile 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,69 +1,98 @@
1
- # Autofile
1
+ <p align="center">
2
+ <img src="assets/icon.svg" width="72" height="72" alt="Autofile icon">
3
+ </p>
2
4
 
3
- Autofile makes it easy for agents to store information in a structured, unambiguous way, and makes that information available to the services and interfaces that want to use it and show it.
5
+ <h1 align="center">Autofile</h1>
4
6
 
5
- Autofile is designed for:
7
+ <p align="center">
8
+ <strong>A portable, open filesystem for agents.</strong>
9
+ </p>
6
10
 
7
- - **No ambiguity on input.** Every kind of information has one defined place and type, with clear instructions for how it is filed.
8
- - **Efficient retrieval.** Agents find what they need by keyword search, by matching on typed properties, and by following links between records.
9
- - **Plain files, no lock-in.** Records are JSON files you own, and any agent, MCP server or interface can read, use and show them.
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@telepath-computer/autofile"><img src="https://img.shields.io/npm/v/@telepath-computer/autofile" alt="npm version"></a>
13
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/telepath-computer/autofile" alt="MIT licence"></a>
14
+ <img src="https://img.shields.io/badge/node-%E2%89%A524-339933" alt="Node 24 or later">
15
+ </p>
10
16
 
11
- Autofile is an [MCP](https://modelcontextprotocol.io) server. Agents use it through the tools and resources it serves.
17
+ <p align="center">
18
+ <img src="assets/autofile-view.png" width="760" alt="A grid of records in Autofile: journal entries, contacts, events, topics and saved references">
19
+ <br>
20
+ <em>GUI coming soon</em>
21
+ </p>
12
22
 
13
- ## Install
23
+ **Agents file everything, and it never turns into a mess.** Every kind of information has one place and one shape, and anything that doesn't fit is turned away. A year of filing by any number of agents stays as coherent as the first day, and anything in it can still be found straight away.
14
24
 
15
- Autofile needs Node.js 24 or later.
25
+ **The live data layer for the apps you vibe-code.** Point an app at Autofile and it has storage, search, links, file attachments and live updates, with nothing else to build. The screens agents put up on [Television](https://github.com/telepath-computer/television) work this way: they change the moment an agent files something.
16
26
 
17
- ```sh
18
- npm install -g @telepath-computer/autofile
19
- ```
27
+ **Yours, wherever you take it.** Your records are plain files on your own machine, readable without Autofile and easy to move, back up or sync. Autofile is open source, and any agent that speaks [MCP](https://modelcontextprotocol.io) can use it.
20
28
 
21
- This installs the `autofile` command.
29
+ ## Get started
22
30
 
23
- ## Connect an agent
31
+ You need Node.js 24 or later.
24
32
 
25
- An MCP client starts Autofile itself by running `autofile`, and talks to it over stdio. For Claude Code:
33
+ 1. Install Autofile:
26
34
 
27
- ```sh
28
- claude mcp add autofile --scope user -- autofile
29
- ```
35
+ ```sh
36
+ npm install -g @telepath-computer/autofile
37
+ ```
30
38
 
31
- For another client, add an MCP server whose command is `autofile`, with no arguments.
39
+ 2. Add it to your agent. For Claude Code:
32
40
 
33
- Autofile starts with no types. Ask your agent to file something: it proposes a type, such as `contact` with the fields a contact needs, and creates it once you agree. It then files records of that type, and finds them again by search, by field value, or by following links between records.
41
+ ```sh
42
+ claude mcp add autofile --scope user -- autofile
43
+ ```
34
44
 
35
- ## Where records are kept
45
+ For another MCP client, add a server whose command is `autofile`, with no arguments.
36
46
 
37
- Autofile keeps records in `~/.autofile/data`, as one JSON file per record in a folder for each type:
47
+ 3. Ask your agent to remember something.
38
48
 
39
- ```
40
- ~/.autofile/data/
41
- _types/contact.json the declaration of the type contact
42
- contact/priya-narayan.json a record of that type
43
- _blobs/9f86d081…0a08.pdf a file stored with a record
44
- ```
49
+ Autofile starts empty. Your agent proposes types as it needs them, and creates each one once you agree.
45
50
 
46
- The files are plain JSON, so you can read them, back them up, or edit them by hand. To keep them in another folder:
51
+ ## File over app
47
52
 
48
- ```sh
49
- autofile set storage ~/Documents/Autofile
50
- ```
53
+ Autofile follows [File over app](https://stephango.com/file-over-app), the philosophy of Steph Ango (kepano), CEO of Obsidian: "Apps are ephemeral, but your files have a chance to last."
51
54
 
52
- Setting `storage` moves no files. Clients use the new folder the next time they start Autofile, and `autofile serve` once it is restarted. [Commands](spec/index.md#commands) lists the other settings.
55
+ Your records are plain JSON files in one Autofile folder, with attachments kept as the original files. Put the folder anywhere, such as Dropbox to sync it between machines, with `autofile set storage ~/Dropbox/Autofile`. Any program can read it, with or without Autofile, and the files are yours forever.
53
56
 
54
57
  ## Serve over HTTP
55
58
 
56
- For clients that connect over HTTP, such as a web page, run:
59
+ For apps and agents that connect over HTTP, run:
57
60
 
58
61
  ```sh
59
62
  autofile serve
60
63
  ```
61
64
 
62
- This serves the same tools and resources at `http://127.0.0.1:7319/mcp` until stopped. Autofile does not authenticate HTTP clients: anything that can reach the address can read and change every record. It therefore listens only on this machine by default, and accepts requests from web pages only when they are served from this machine. [Serving over HTTP](spec/index.md#serving-over-http) explains how to change the address, the port, and the web pages it accepts.
65
+ This serves the same tools and resources at `http://127.0.0.1:7319/mcp`, and attachments next to it. Autofile does not authenticate HTTP clients, so anything that can reach the address can read and change every record. It listens only on this machine by default. [Serving over HTTP](spec/index.md#serving-over-http) explains how to change the address and which web pages it accepts.
66
+
67
+ ## Use it as a library
68
+
69
+ Programs can use the same store directly:
70
+
71
+ ```sh
72
+ npm install @telepath-computer/autofile
73
+ ```
74
+
75
+ ```js
76
+ import { Autofile } from "@telepath-computer/autofile";
77
+
78
+ const store = new Autofile({ folder: "./records" });
79
+ try {
80
+ const { records } = await store.records.find({ q: "Ada Park" });
81
+ for (const record of records) console.log(record.title ?? record.uri);
82
+ } finally {
83
+ await store.close();
84
+ }
85
+ ```
86
+
87
+ The [Library API](spec/index.md#library-api) covers writing records, declaring types, attachments and watching for changes.
88
+
89
+ ## Status
90
+
91
+ Autofile is new and changing quickly. Until version 1.0, addresses, tools and the library API may change between minor versions.
63
92
 
64
93
  ## Specification
65
94
 
66
- [spec/index.md](spec/index.md) describes Autofile in full: records, types, links, stored files, the MCP tools and resources, and the commands.
95
+ [spec/index.md](spec/index.md) describes records, types, links, attachments, the MCP tools and resources, and the commands. [Storage](spec/storage.md) specifies the folder format, and [Implementation](spec/implementation.md) describes the code.
67
96
 
68
97
  ## Development
69
98
 
@@ -73,3 +102,7 @@ npm test
73
102
  ```
74
103
 
75
104
  `npm test` builds Autofile and runs the tests.
105
+
106
+ ## Licence
107
+
108
+ [MIT](LICENSE)
package/dist/blobs.d.ts CHANGED
@@ -1,34 +1,19 @@
1
- /**
2
- * Parses, checks, and persists blob-field inputs. Candidate preparation is
3
- * deliberately separate from persistence so a refused record write cannot
4
- * leave a blob behind.
5
- */
6
- /** Largest blob that fits in one MCP message after base64 encoding. */
7
- export declare const MAX_BLOB_BYTES = 7340032;
1
+ /** Stores explicitly uploaded attachments and checks their canonical addresses. */
8
2
  /** Canonical names Autofile recognizes in its blob folder. */
9
3
  export declare const BLOB_NAME_PATTERN: RegExp;
10
4
  /** Canonical URIs Autofile recognizes for stored blobs. */
11
5
  export declare const BLOB_URI_PATTERN: RegExp;
12
- /** One checked blob which may still need to be copied into the store. */
13
- export interface PreparedBlob {
14
- uri: string;
15
- name: string;
16
- bytes?: Buffer;
17
- }
18
- /** A user-correctable refusal for one blob field. */
19
- interface BlobRefusal {
20
- message: string;
21
- }
22
- /** Returns whether the value has one of the three syntactic blob input forms. */
23
- export declare function isBlobInput(value: unknown): value is string;
24
- /** Checks and materializes one candidate without changing the blob folder. */
25
- export declare function prepareBlob(value: string, blobFolder: string, allowLocalPaths: boolean): Promise<PreparedBlob | BlobRefusal>;
26
- /** Stores checked blobs atomically, skipping any destination already present. */
27
- export declare function writePreparedBlobs(blobFolder: string, blobs: Iterable<PreparedBlob>): Promise<void>;
6
+ /** Stores uploaded bytes through the same incremental writer as streamed sources. */
7
+ export declare function writeBlob(blobFolder: string, bytes: Buffer, mediaType: string): Promise<string>;
8
+ /** Hashes and stores chunks in order, publishing only a complete attachment. */
9
+ export declare function writeBlobStream(blobFolder: string, chunks: AsyncIterable<Uint8Array>, mediaType: string): Promise<string>;
10
+ /** Checks attachment existence without opening or reading its bytes. */
11
+ export declare function blobReferenceProblem(blobFolder: string, uri: string): Promise<string | undefined>;
12
+ /** Checks the bare media type accepted by explicit uploads. */
13
+ export declare function isMediaType(value: unknown): value is string;
28
14
  /** Deletes the named canonical blob resources when they still exist. */
29
15
  export declare function deleteBlobUris(blobFolder: string, uris: Iterable<string>): Promise<void>;
30
16
  /** Returns the canonical blob name from a URI. */
31
17
  export declare function blobNameFromUri(uri: string): string | undefined;
32
18
  /** Builds the canonical URI for a recognized blob name. */
33
19
  export declare function blobUri(name: string): string;
34
- export {};
package/dist/blobs.js CHANGED
@@ -1,83 +1,75 @@
1
- /**
2
- * Parses, checks, and persists blob-field inputs. Candidate preparation is
3
- * deliberately separate from persistence so a refused record write cannot
4
- * leave a blob behind.
5
- */
1
+ /** Stores explicitly uploaded attachments and checks their canonical addresses. */
6
2
  import { createHash, randomUUID } from "node:crypto";
7
- import { constants } from "node:fs";
8
- import { lstat, mkdir, open, rename, unlink, writeFile } from "node:fs/promises";
9
- import { basename, dirname, extname, isAbsolute, join } from "node:path";
3
+ import { chmod, lstat, mkdir, rename, unlink, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
10
5
  import { extension as extensionForMimeType } from "mime-types";
11
6
  const BLOB_EXTENSION_PATTERN_SOURCE = "[a-z0-9_-]+";
12
- /** Largest blob that fits in one MCP message after base64 encoding. */
13
- export const MAX_BLOB_BYTES = 7_340_032;
14
7
  /** Canonical names Autofile recognizes in its blob folder. */
15
8
  export const BLOB_NAME_PATTERN = new RegExp(`^[a-f0-9]{64}(?:\\.${BLOB_EXTENSION_PATTERN_SOURCE})?$`, "u");
16
9
  /** Canonical URIs Autofile recognizes for stored blobs. */
17
- export const BLOB_URI_PATTERN = new RegExp(`^autofile:///_blobs/([a-f0-9]{64}(?:\\.${BLOB_EXTENSION_PATTERN_SOURCE})?)$`, "u");
18
- const SOURCE_READ_CHUNK_BYTES = 64 * 1024;
10
+ export const BLOB_URI_PATTERN = new RegExp(`^autofile:///blobs/([a-f0-9]{64}(?:\\.${BLOB_EXTENSION_PATTERN_SOURCE})?)$`, "u");
19
11
  const blobExtensionPattern = new RegExp(`^${BLOB_EXTENSION_PATTERN_SOURCE}$`, "u");
20
- /** Returns whether the value has one of the three syntactic blob input forms. */
21
- export function isBlobInput(value) {
22
- return typeof value === "string"
23
- && (BLOB_URI_PATTERN.test(value) || isAbsolute(value) || parseDataUri(value) !== undefined);
24
- }
25
- /** Checks and materializes one candidate without changing the blob folder. */
26
- export async function prepareBlob(value, blobFolder, allowLocalPaths) {
27
- const blobName = blobNameFromUri(value);
28
- if (blobName !== undefined) {
29
- const details = await storedBlobDetails(blobFolder, blobName);
30
- if (details === undefined)
31
- return { message: `references a blob that does not exist: ${value}` };
32
- if (details.size > MAX_BLOB_BYTES)
33
- return sizeRefusal(details.size);
34
- return { uri: value, name: blobName };
12
+ /** Stores uploaded bytes through the same incremental writer as streamed sources. */
13
+ export async function writeBlob(blobFolder, bytes, mediaType) {
14
+ async function* chunks() { yield bytes; }
15
+ return writeBlobStream(blobFolder, chunks(), mediaType);
16
+ }
17
+ /** Hashes and stores chunks in order, publishing only a complete attachment. */
18
+ export async function writeBlobStream(blobFolder, chunks, mediaType) {
19
+ const extension = extensionForMimeType(mediaType.toLowerCase());
20
+ const suffix = extension === false ? undefined : normalizeExtension(extension);
21
+ const digest = createHash("sha256");
22
+ await mkdir(blobFolder, { recursive: true });
23
+ if (!await isDirectory(blobFolder)) {
24
+ throw Object.assign(new Error("blob folder is not a directory"), { code: "ENOTDIR" });
25
+ }
26
+ const temporaryPath = join(blobFolder, `.upload.${String(process.pid)}.${randomUUID()}`);
27
+ async function* hashedChunks() {
28
+ for await (const chunk of chunks) {
29
+ digest.update(chunk);
30
+ yield chunk;
31
+ }
35
32
  }
36
- if (isAbsolute(value)) {
37
- if (!allowLocalPaths) {
38
- return { message: "local paths are accepted only over stdio, not HTTP" };
33
+ try {
34
+ // writeFile awaits each chunk write before advancing the iterable, so the
35
+ // source is backpressured and attachments never accumulate in memory.
36
+ await writeFile(temporaryPath, hashedChunks(), { flag: "wx" });
37
+ const name = `${digest.digest("hex")}${suffix === undefined ? "" : `.${suffix}`}`;
38
+ const path = join(blobFolder, name);
39
+ if (await isRegularFile(path)) {
40
+ await unlink(temporaryPath);
39
41
  }
40
- const source = await openRegularFile(value, false);
41
- if (source === undefined)
42
- return { message: `path does not exist or is not a file: ${value}` };
42
+ else {
43
+ // Set the exact read-only mode before publishing the immutable attachment.
44
+ await chmod(temporaryPath, 0o444);
45
+ await rename(temporaryPath, path);
46
+ }
47
+ return blobUri(name);
48
+ }
49
+ catch (error) {
43
50
  try {
44
- if (source.size > MAX_BLOB_BYTES)
45
- return sizeRefusal(source.size);
46
- const bytes = await readSourceFile(source.handle);
47
- if ("message" in bytes)
48
- return bytes;
49
- return preparedBytes(bytes, pathExtension(value));
51
+ await unlink(temporaryPath);
50
52
  }
51
- finally {
52
- await source.handle.close();
53
+ catch (cleanupError) {
54
+ if (!hasCode(cleanupError, "ENOENT"))
55
+ throw cleanupError;
53
56
  }
57
+ throw error;
54
58
  }
55
- const data = parseDataUri(value);
56
- if (data === undefined)
57
- return { message: "must be a blob URI, an absolute path, or a base64 data URI" };
58
- if (data.size > MAX_BLOB_BYTES)
59
- return sizeRefusal(data.size);
60
- return preparedBytes(Buffer.from(data.base64, "base64"), data.extension);
61
59
  }
62
- /** Stores checked blobs atomically, skipping any destination already present. */
63
- export async function writePreparedBlobs(blobFolder, blobs) {
64
- const pending = [...blobs].filter((blob) => blob.bytes !== undefined);
65
- if (pending.length === 0)
66
- return;
67
- await mkdir(blobFolder, { recursive: true });
68
- if (!await isDirectory(blobFolder)) {
69
- throw Object.assign(new Error("blob folder is not a directory"), { code: "ENOTDIR" });
70
- }
71
- const written = new Set();
72
- for (const blob of pending) {
73
- if (written.has(blob.name))
74
- continue;
75
- written.add(blob.name);
76
- const path = join(blobFolder, blob.name);
77
- if (await isRegularFile(path))
78
- continue;
79
- await writeBlobAtomically(path, blob.bytes ?? Buffer.alloc(0));
80
- }
60
+ /** Checks attachment existence without opening or reading its bytes. */
61
+ export async function blobReferenceProblem(blobFolder, uri) {
62
+ const name = blobNameFromUri(uri);
63
+ return name !== undefined && await isDirectory(blobFolder) && await isRegularFile(join(blobFolder, name))
64
+ ? undefined
65
+ : `references a blob that does not exist: ${uri}`;
66
+ }
67
+ /** Checks the bare media type accepted by explicit uploads. */
68
+ export function isMediaType(value) {
69
+ if (typeof value !== "string")
70
+ return false;
71
+ const token = "[!#$%&'*+.^_`|~0-9A-Za-z-]+";
72
+ return new RegExp(`^${token}/${token}$`, "u").test(value);
81
73
  }
82
74
  /** Deletes the named canonical blob resources when they still exist. */
83
75
  export async function deleteBlobUris(blobFolder, uris) {
@@ -98,177 +90,16 @@ export async function deleteBlobUris(blobFolder, uris) {
98
90
  }
99
91
  /** Returns the canonical blob name from a URI. */
100
92
  export function blobNameFromUri(uri) {
101
- return BLOB_URI_PATTERN.exec(uri)?.[1];
93
+ return typeof uri === "string" ? BLOB_URI_PATTERN.exec(uri)?.[1] : undefined;
102
94
  }
103
95
  /** Builds the canonical URI for a recognized blob name. */
104
96
  export function blobUri(name) {
105
- return `autofile:///_blobs/${name}`;
106
- }
107
- function preparedBytes(bytes, extension) {
108
- const digest = createHash("sha256").update(bytes).digest("hex");
109
- const name = `${digest}${extension === undefined ? "" : `.${extension}`}`;
110
- return { uri: blobUri(name), name, bytes };
111
- }
112
- function parseDataUri(value) {
113
- if (value.slice(0, "data:".length).toLowerCase() !== "data:")
114
- return undefined;
115
- const comma = value.indexOf(",", "data:".length);
116
- if (comma === -1)
117
- return undefined;
118
- const header = value.slice("data:".length, comma);
119
- const marker = ";base64";
120
- if (header.slice(-marker.length).toLowerCase() !== marker)
121
- return undefined;
122
- const metadata = header.slice(0, -marker.length);
123
- const base64 = value.slice(comma + 1);
124
- if (!isCanonicalBase64(base64))
125
- return undefined;
126
- const [mediaType = "", ...parameters] = metadata.split(";");
127
- if ((mediaType !== "" && !isMediaType(mediaType))
128
- || parameters.some((parameter) => !isMediaTypeParameter(parameter))) {
129
- return undefined;
130
- }
131
- const normalizedMediaType = mediaType === "" ? "text/plain" : mediaType.toLowerCase();
132
- const usualExtension = extensionForMimeType(normalizedMediaType);
133
- const extension = usualExtension === false ? undefined : normalizeExtension(usualExtension);
134
- return {
135
- base64,
136
- ...(extension === undefined ? {} : { extension }),
137
- size: decodedBase64Size(base64),
138
- };
139
- }
140
- function isCanonicalBase64(value) {
141
- if (value.length % 4 !== 0)
142
- return false;
143
- const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
144
- const contentLength = value.length - padding;
145
- if (padding === 2 && contentLength % 4 !== 2)
146
- return false;
147
- if (padding === 1 && contentLength % 4 !== 3)
148
- return false;
149
- for (let index = 0; index < contentLength; index += 1) {
150
- const code = value.charCodeAt(index);
151
- if (!(code >= 0x41 && code <= 0x5a)
152
- && !(code >= 0x61 && code <= 0x7a)
153
- && !(code >= 0x30 && code <= 0x39)
154
- && code !== 0x2b
155
- && code !== 0x2f) {
156
- return false;
157
- }
158
- }
159
- if (padding > 0) {
160
- const finalCode = value.charCodeAt(contentLength - 1);
161
- const finalSextet = finalCode >= 0x41 && finalCode <= 0x5a
162
- ? finalCode - 0x41
163
- : finalCode >= 0x61 && finalCode <= 0x7a
164
- ? finalCode - 0x61 + 26
165
- : finalCode >= 0x30 && finalCode <= 0x39
166
- ? finalCode - 0x30 + 52
167
- : finalCode === 0x2b ? 62 : 63;
168
- const unusedBits = padding === 2 ? 4 : 2;
169
- if ((finalSextet & ((1 << unusedBits) - 1)) !== 0)
170
- return false;
171
- }
172
- return true;
173
- }
174
- function decodedBase64Size(value) {
175
- if (value.length === 0)
176
- return 0;
177
- const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
178
- return value.length / 4 * 3 - padding;
179
- }
180
- function isMediaType(value) {
181
- const token = "[!#$%&'*+.^_`|~0-9A-Za-z-]+";
182
- return new RegExp(`^${token}/${token}$`, "u").test(value);
183
- }
184
- function isMediaTypeParameter(value) {
185
- return /^[!#$%&'*+.^_`|~0-9A-Za-z-]+=[^\s,;]+$/u.test(value);
186
- }
187
- function pathExtension(path) {
188
- return normalizeExtension(extname(basename(path)).slice(1));
97
+ return `autofile:///blobs/${name}`;
189
98
  }
190
99
  function normalizeExtension(extension) {
191
100
  const normalized = extension.toLowerCase();
192
101
  return blobExtensionPattern.test(normalized) ? normalized : undefined;
193
102
  }
194
- function sizeRefusal(size) {
195
- return { message: `blob is ${String(size)} bytes; limit is ${String(MAX_BLOB_BYTES)} bytes` };
196
- }
197
- async function storedBlobDetails(blobFolder, name) {
198
- if (!await isDirectory(blobFolder))
199
- return undefined;
200
- const file = await openRegularFile(join(blobFolder, name), true);
201
- if (file === undefined)
202
- return undefined;
203
- try {
204
- return { size: file.size };
205
- }
206
- finally {
207
- await file.handle.close();
208
- }
209
- }
210
- async function readSourceFile(handle) {
211
- const chunks = [];
212
- let size = 0;
213
- while (true) {
214
- // The extra byte proves the source is too large without allowing a file that grew after stat,
215
- // or a regular special file with an inaccurate size, to make this read unbounded.
216
- const remainingWithSentinel = MAX_BLOB_BYTES + 1 - size;
217
- const buffer = Buffer.alloc(Math.min(SOURCE_READ_CHUNK_BYTES, remainingWithSentinel));
218
- const { bytesRead } = await handle.read(buffer, 0, buffer.byteLength, null);
219
- if (bytesRead === 0)
220
- return Buffer.concat(chunks, size);
221
- size += bytesRead;
222
- if (size > MAX_BLOB_BYTES)
223
- return sizeRefusal(size);
224
- chunks.push(bytesRead === buffer.byteLength ? buffer : buffer.subarray(0, bytesRead));
225
- }
226
- }
227
- async function openRegularFile(path, noFollow) {
228
- let handle;
229
- try {
230
- handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK | (noFollow ? constants.O_NOFOLLOW : 0));
231
- }
232
- catch (error) {
233
- if (hasCode(error, "ENOENT")
234
- || hasCode(error, "ENOTDIR")
235
- || hasCode(error, "ELOOP")
236
- || hasCode(error, "ENXIO")
237
- || hasCode(error, "ENODEV")) {
238
- return undefined;
239
- }
240
- throw error;
241
- }
242
- try {
243
- const details = await handle.stat();
244
- if (!details.isFile()) {
245
- await handle.close();
246
- return undefined;
247
- }
248
- return { handle, size: details.size };
249
- }
250
- catch (error) {
251
- await handle.close();
252
- throw error;
253
- }
254
- }
255
- async function writeBlobAtomically(path, bytes) {
256
- const temporaryPath = join(dirname(path), `.${basename(path)}.${String(process.pid)}.${randomUUID()}`);
257
- try {
258
- await writeFile(temporaryPath, bytes, { flag: "wx" });
259
- await rename(temporaryPath, path);
260
- }
261
- catch (error) {
262
- try {
263
- await unlink(temporaryPath);
264
- }
265
- catch (cleanupError) {
266
- if (!hasCode(cleanupError, "ENOENT"))
267
- throw cleanupError;
268
- }
269
- throw error;
270
- }
271
- }
272
103
  async function isDirectory(path) {
273
104
  try {
274
105
  return (await lstat(path)).isDirectory();
package/dist/blobs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"blobs.js","sourceRoot":"","sources":["../src/blobs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blobs.js","sourceRoot":"","sources":["../src/blobs.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -4,8 +4,8 @@ import { StdioServerTransport, serveStdio } from "@modelcontextprotocol/server/s
4
4
  import { getSetting, getSettings, isSettingName, parsePort, setSetting, unsetSetting, } from "./config.js";
5
5
  import { startHttpServer } from "./http.js";
6
6
  import { buildServer } from "./server.js";
7
- import { openStore } from "./store.js";
8
- import { resourceNotificationUris, watchStore, } from "./watch.js";
7
+ import { Autofile } from "./index.js";
8
+ import { watchResources, } from "./mcp-watch.js";
9
9
  const usage = `Usage:
10
10
  autofile
11
11
  autofile serve [--host <host>] [--port <port>]
@@ -64,24 +64,27 @@ async function main(args) {
64
64
  }
65
65
  async function runStdio() {
66
66
  const settings = await getSettings();
67
- const store = await openStore({ dataFolder: settings.storage });
67
+ const store = new Autofile({ folder: settings.storage });
68
68
  let connectedServer;
69
69
  let watcher;
70
70
  let handle;
71
71
  let stopping;
72
72
  const stop = () => {
73
73
  stopping ??= Promise.all([
74
- watcher?.close() ?? Promise.resolve(),
74
+ (async () => {
75
+ await watcher?.close();
76
+ await store.close();
77
+ })(),
75
78
  handle?.close() ?? Promise.resolve(),
76
79
  ]).then(() => undefined);
77
80
  return stopping;
78
81
  };
79
- watcher = await watchStore(settings.storage, store, async (batch) => {
82
+ watcher = await watchResources(store, async (batch) => {
80
83
  const server = connectedServer;
81
84
  if (server === undefined || !server.isConnected())
82
85
  return;
83
86
  try {
84
- const uris = new Set(batch.changes.flatMap(({ uri }) => resourceNotificationUris(uri)));
87
+ const uris = new Set(batch.changes.map(({ uri }) => uri));
85
88
  for (const uri of uris) {
86
89
  await server.server.sendResourceUpdated({ uri });
87
90
  }
@@ -93,9 +96,12 @@ async function runStdio() {
93
96
  if (server.isConnected())
94
97
  throw error;
95
98
  }
96
- }, reportError);
99
+ }, reportError).catch(async (error) => {
100
+ await store.close();
101
+ throw error;
102
+ });
97
103
  handle = serveStdio(async () => {
98
- const server = await buildServer(store);
104
+ const server = await buildServer(store, { blobLocation: "stdio" });
99
105
  connectedServer = server;
100
106
  return server;
101
107
  }, {
@@ -124,8 +130,8 @@ async function serve(args) {
124
130
  ...(overrides.host === undefined ? {} : { host: overrides.host }),
125
131
  ...(overrides.port === undefined ? {} : { port: overrides.port }),
126
132
  };
127
- const store = await openStore({ dataFolder: settings.storage });
128
- const running = await startHttpServer(store, settings.storage, http, reportError);
133
+ const store = new Autofile({ folder: settings.storage });
134
+ const running = await startHttpServer(store, http, reportError);
129
135
  process.stderr.write(`autofile: listening on ${running.endpoint.href}\n`);
130
136
  const stop = () => {
131
137
  void running.close().catch((error) => {