agentic-drive 0.1.0 → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import { createRequire } from "module";
3
- var VERSION = true ? "0.1.0" : readPackageVersion();
3
+ var VERSION = true ? "0.2.0" : readPackageVersion();
4
4
 
5
5
  export {
6
6
  VERSION
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  VERSION
4
- } from "./chunk-4QE7E5UQ.js";
4
+ } from "./chunk-RWMBGIAS.js";
5
5
  import {
6
6
  DriveError,
7
7
  buildExport,
8
8
  openLocal,
9
9
  publicNode,
10
10
  upload
11
- } from "./chunk-QRGKXPLX.js";
11
+ } from "./chunk-6CWBHSJ7.js";
12
12
 
13
13
  // src/cli.ts
14
14
  import { createReadStream, realpathSync, writeFileSync } from "fs";
@@ -33,7 +33,10 @@ Commands:
33
33
  purge <id> Delete a trashed item for good.
34
34
  search Find items. Flags: --name --mime --kind --min-size --max-size --created-by-kind --limit
35
35
  export <out.zip> Write everything you own to a zip with a JSON manifest.
36
- delete-all --yes Delete everything you own for good.
36
+ home [--app <name>] List files and app documents, newest change first.
37
+ register <ref> <name> Put an app document in the drive. Flag: --parent <folder>
38
+ forget <ref> Move the references to an app document to the trash.
39
+ delete-all --yes Erase you for good: your drive, and what you shared or held.
37
40
  mcp Start the MCP server on stdio.
38
41
 
39
42
  Flags:
@@ -62,6 +65,7 @@ var options = {
62
65
  "max-size": { type: "string" },
63
66
  "created-by-kind": { type: "string" },
64
67
  limit: { type: "string" },
68
+ app: { type: "string" },
65
69
  yes: { type: "boolean" },
66
70
  help: { type: "boolean", short: "h" },
67
71
  version: { type: "boolean", short: "V" }
@@ -164,9 +168,22 @@ async function runCli(argv, io) {
164
168
  print({ saved: resolve(args[0]), size: zip.byteLength });
165
169
  break;
166
170
  }
171
+ case "home": {
172
+ const home = await drive.home(ctx, { app: values.app, limit: num(values.limit, "limit") });
173
+ print({ apps: home.apps, files: home.files, items: home.items.map((i) => ({ ...publicNode(i.node, i.path), app: i.app })) });
174
+ break;
175
+ }
176
+ case "register":
177
+ need(2, "register <ref> <name>");
178
+ print(publicNode(await drive.registerDocument(ctx, { ref: args[0], name: args[1], folder: values.parent })));
179
+ break;
180
+ case "forget":
181
+ need(1, "forget <ref>");
182
+ print(await drive.forgetDocument(ctx, args[0]));
183
+ break;
167
184
  case "delete-all":
168
- if (!values.yes) throw new UsageError("delete-all removes everything you own for good. Add --yes to confirm.");
169
- print(await drive.deleteAll(ctx));
185
+ if (!values.yes) throw new UsageError("delete-all erases you for good. Add --yes to confirm.");
186
+ print(await drive.erasePerson(ctx));
170
187
  break;
171
188
  case "mcp": {
172
189
  const { startMcp } = await import("./mcp.js");
@@ -0,0 +1,325 @@
1
+ import * as agentic_identity from 'agentic-identity';
2
+ import { PrincipalContext, Store, Blobs, createSharing, Role } from 'agentic-identity';
3
+
4
+ declare const ROOT_ID = "root";
5
+ declare const ACTOR_KINDS: readonly ["human", "agent_of_human", "agent_of_agent", "agent", "user_token"];
6
+ type ActorKind = (typeof ACTOR_KINDS)[number];
7
+ type Actor = {
8
+ id: string;
9
+ kind: ActorKind;
10
+ };
11
+ /**
12
+ * `owner` is the drive's owner, and the root of the caller's chain. `actor` is who acts for them.
13
+ * `principal` is the resolved caller. Drive needs it to check roles when `sharing` is set.
14
+ */
15
+ type Ctx = {
16
+ owner: string;
17
+ actor: Actor;
18
+ principal?: PrincipalContext;
19
+ };
20
+ /** `delete` is for the owner only: purge and erase. Trash needs `write`. */
21
+ type Action = "read" | "write" | "delete";
22
+ type Sharing = ReturnType<typeof createSharing>;
23
+ /** The seam for access. With `sharing`, the default is a role check through agentic-identity. */
24
+ type Can = (ctx: Ctx, action: Action, node: Node) => boolean | Promise<boolean>;
25
+ type Node = {
26
+ id: string;
27
+ owner: string;
28
+ parentId: string;
29
+ kind: "folder" | "file" | "ref";
30
+ name: string;
31
+ nameKey: string;
32
+ size: number;
33
+ mime: string | null;
34
+ hash: string | null;
35
+ /** A typed reference such as `docs:<id>`. Set when kind is "ref". */
36
+ ref: string | null;
37
+ blobKey: string | null;
38
+ createdAt: number;
39
+ updatedAt: number;
40
+ createdBy: Actor;
41
+ updatedBy: Actor;
42
+ /** The ids of `createdBy` and `updatedBy`, at the top level so a store can find what a person made. */
43
+ createdById: string;
44
+ updatedById: string;
45
+ trashed: boolean;
46
+ trashedAt: number | null;
47
+ /** Set while a purge deletes the item. Nothing can see or change it then. */
48
+ purging: boolean;
49
+ };
50
+ type ErrorCode = "NOT_FOUND" | "NAME_TAKEN" | "BLOB_EXISTS" | "INVALID_NAME" | "INVALID_PATH" | "INVALID_REF" | "NOT_A_FOLDER" | "NOT_A_FILE" | "MOVE_INTO_SELF" | "TOO_DEEP" | "PARENT_GONE" | "TRASHED" | "NOT_TRASHED" | "IS_ROOT" | "FORBIDDEN" | "BLOB_MISSING" | "TOO_LARGE" | "INVALID_EXPIRY" | "BUSY" | "CROSS_DRIVE" | "INVALID_SHARE" | "TOO_MANY_ITEMS";
51
+ declare class DriveError extends Error {
52
+ readonly code: ErrorCode;
53
+ constructor(code: ErrorCode, message: string);
54
+ }
55
+ interface SearchQuery {
56
+ /** Part of the name. Case and Unicode form do not matter. */
57
+ name?: string;
58
+ /** An exact type, or a family such as `image/*`. */
59
+ mime?: string;
60
+ kind?: Node["kind"];
61
+ minSize?: number;
62
+ maxSize?: number;
63
+ /** Items first made by this kind of actor, such as `agent_of_human`. */
64
+ createdByKind?: ActorKind;
65
+ /** Only references to documents of this app, such as `docs`. */
66
+ app?: string;
67
+ /** At most this many results. Defaults to 100. */
68
+ limit?: number;
69
+ }
70
+ type OnConflict = "fail" | "rename" | "replace";
71
+ interface DriveDeps {
72
+ store: Store;
73
+ blobs: Blobs;
74
+ now?: () => number;
75
+ newId?: () => string;
76
+ can?: Can;
77
+ /** Turns on roles. Folders and files register as resources, and `can()` decides every request. Make it on the same `store`. */
78
+ sharing?: Sharing;
79
+ /** Most parent steps any ancestor walk takes. A walk that hits it fails closed. */
80
+ maxDepth?: number;
81
+ /** Called with a blob key that no node points at and that could not be deleted. */
82
+ onOrphan?: (key: string, error: unknown) => void;
83
+ }
84
+ type Drive = ReturnType<typeof createDrive>;
85
+ /** Validates a name. Names are NFC-normalized. Two names clash when their keys match. */
86
+ declare function parseName(raw: string): {
87
+ name: string;
88
+ key: string;
89
+ };
90
+ declare function createDrive(deps: DriveDeps): {
91
+ stat(ctx: Ctx, loc: string): Promise<Node>;
92
+ /** Live children, sorted by name. */
93
+ list(ctx: Ctx, loc?: string): Promise<Node[]>;
94
+ mkdir(ctx: Ctx, path: string, opts?: {
95
+ parents?: boolean;
96
+ }): Promise<Node>;
97
+ /**
98
+ * Stores one blob and one node. The blob is written first. The new node commits before
99
+ * any old blob goes. A failed old-blob delete leaves an orphan and never undoes the commit.
100
+ */
101
+ putFile(ctx: Ctx, path: string, data: Uint8Array, opts?: {
102
+ mime?: string;
103
+ parents?: boolean;
104
+ onConflict?: OnConflict;
105
+ }): Promise<Node>;
106
+ /** A reference points at a document in another app, such as `docs:<id>`. */
107
+ putRef(ctx: Ctx, path: string, ref: string, opts?: {
108
+ parents?: boolean;
109
+ onConflict?: OnConflict;
110
+ }): Promise<Node>;
111
+ read(ctx: Ctx, loc: string): Promise<{
112
+ node: Node;
113
+ data: Uint8Array;
114
+ contentType?: string;
115
+ }>;
116
+ /**
117
+ * A time-limited URL to fetch the bytes. The URL signs an attachment header and the stored
118
+ * type, so the link holder cannot change how the bytes are served. Trashing an item does
119
+ * not revoke a link already issued.
120
+ */
121
+ downloadUrl(ctx: Ctx, loc: string, expiresInSeconds?: number): Promise<string>;
122
+ move(ctx: Ctx, src: string, destLoc: string, opts?: {
123
+ name?: string;
124
+ onConflict?: OnConflict;
125
+ }): Promise<Node>;
126
+ rename(ctx: Ctx, loc: string, name: string): Promise<Node>;
127
+ /** Hides an item and, for a folder, everything under it. Nothing else changes. */
128
+ trash(ctx: Ctx, loc: string): Promise<Node>;
129
+ /** Items trashed on their own, newest first. */
130
+ listTrash(ctx: Ctx): Promise<Node[]>;
131
+ /** Puts a trashed item back. It needs a live parent folder. */
132
+ restore(ctx: Ctx, id: string, opts?: {
133
+ parentId?: string;
134
+ onConflict?: OnConflict;
135
+ }): Promise<Node>;
136
+ /** Live items that match every field given, sorted by path. */
137
+ search(ctx: Ctx, q?: SearchQuery): Promise<{
138
+ node: Node;
139
+ path: string;
140
+ }[]>;
141
+ /** Every item of the owner, trashed or not, with its original path and bytes. For export. */
142
+ exportData(ctx: Ctx): Promise<{
143
+ node: Node;
144
+ path: string;
145
+ data: Uint8Array | null;
146
+ missing: boolean;
147
+ }[]>;
148
+ /** Deletes every item and blob of the owner for good. Only the owner may do this, never an agent for them. A failed call can run again. */
149
+ deleteAll(ctx: Ctx): Promise<{
150
+ removed: number;
151
+ }>;
152
+ /** Deletes a trashed item for good, with everything under it and all their bytes. */
153
+ purge(ctx: Ctx, id: string): Promise<{
154
+ removed: number;
155
+ }>;
156
+ /**
157
+ * What the home view shows: every file and every reference to an app's document, newest change first.
158
+ * `apps` counts references per app, and `files` counts plain files, before `app` and `limit` apply.
159
+ */
160
+ home(ctx: Ctx, q?: {
161
+ app?: string;
162
+ limit?: number;
163
+ }): Promise<{
164
+ items: {
165
+ node: Node;
166
+ path: string;
167
+ app: string | null;
168
+ }[];
169
+ apps: Record<string, number>;
170
+ files: number;
171
+ }>;
172
+ /**
173
+ * An app puts one of its documents in this drive, as a reference such as `docs:<id>`. A document that is
174
+ * already in the drive comes back as it is, so an app can register again. The reference sits in `folder`
175
+ * (a path or a folder id, "/" by default) under `name`, and gets a free name when that one is taken.
176
+ */
177
+ registerDocument(ctx: Ctx, input: {
178
+ ref: string;
179
+ name: string;
180
+ folder?: string;
181
+ }): Promise<Node>;
182
+ /** An app deleted a document. Its references in this drive go to the trash. Returns how many. */
183
+ forgetDocument(ctx: Ctx, ref: string): Promise<{
184
+ trashed: number;
185
+ }>;
186
+ /**
187
+ * What drive holds about the person behind a call, besides their own drive (`exportData` has that). It is
188
+ * only for the person in person: an agent gets `partial: true` and nothing else. It holds the ids of the
189
+ * items they or their agents made or changed in other drives (no names, and nothing of the people who own
190
+ * them), the shares they hold, the invites they sent, and the audit entries of what they and their agents
191
+ * did. Invitee emails and link tokens are not in it.
192
+ */
193
+ exportPerson(ctx: Ctx): Promise<{
194
+ subject: {
195
+ id: string;
196
+ kind: "human" | "agent_of_human" | "agent_of_agent" | "agent" | "user_token" | null;
197
+ };
198
+ partial: boolean;
199
+ authored: {
200
+ id: string;
201
+ kind: "folder" | "file" | "ref";
202
+ created: boolean;
203
+ updated: boolean;
204
+ createdAt: number | null;
205
+ updatedAt: number | null;
206
+ by: any[];
207
+ }[];
208
+ agents: {
209
+ id: string;
210
+ items: {
211
+ id: string;
212
+ kind: "folder" | "file" | "ref";
213
+ name: string;
214
+ size: number;
215
+ mime: string | null;
216
+ createdAt: number;
217
+ updatedAt: number;
218
+ trashed: boolean;
219
+ }[];
220
+ }[];
221
+ shares: {
222
+ held: {
223
+ subjectId: unknown;
224
+ resourceId: unknown;
225
+ role: unknown;
226
+ }[];
227
+ invitesCreated: {
228
+ id: unknown;
229
+ resourceId: unknown;
230
+ role: unknown;
231
+ expiresAt: unknown;
232
+ accepted: boolean;
233
+ }[];
234
+ };
235
+ audit: {
236
+ at: unknown;
237
+ action: unknown;
238
+ resourceId: unknown;
239
+ outcome: unknown;
240
+ principalId: unknown;
241
+ principalKind: unknown;
242
+ machineMade: unknown;
243
+ }[];
244
+ }>;
245
+ /**
246
+ * Erases a person for good. Only the person in person may. Their drive and its shares go. So do the shares
247
+ * they hold and the invites they or their agents sent, on drive items only: the grants of other apps stay for
248
+ * those apps and for identity. What they or their agents made in other drives stays, because it belongs to
249
+ * those drives: the id becomes "erased", and the kind stays, so machine-made work keeps its label. The audit
250
+ * trail stays: it holds ids and kinds only. A failed call can run again.
251
+ */
252
+ erasePerson(ctx: Ctx): Promise<{
253
+ removed: number;
254
+ grants: number;
255
+ invites: number;
256
+ anonymized: number;
257
+ }>;
258
+ /** Gives a person or an agent a role on an item and everything under it. Only an owner may. */
259
+ shareWith(ctx: Ctx, loc: string, subjectId: string, role: Role): Promise<void>;
260
+ unshare(ctx: Ctx, loc: string, subjectId: string): Promise<void>;
261
+ /** Invite by `email` (a person) or `handle` (an agent). The invite lasts `expiresInMs`, if given. It sends no message. */
262
+ invite(ctx: Ctx, loc: string, input: {
263
+ role: Role;
264
+ email?: string;
265
+ handle?: string;
266
+ expiresInMs?: number;
267
+ }): Promise<{
268
+ id: string;
269
+ }>;
270
+ /** `identity` is the verified email of a person, or the handle of an agent. */
271
+ acceptInvite(ctx: Ctx, inviteId: string, identity: {
272
+ email?: string;
273
+ handle?: string;
274
+ }): Promise<void>;
275
+ /** The token comes back once. Only its hash is stored. */
276
+ createLink(ctx: Ctx, loc: string, input: {
277
+ role: Role;
278
+ expiresAt?: number;
279
+ signedInOnly?: boolean;
280
+ }): Promise<{
281
+ id: string;
282
+ token: string;
283
+ }>;
284
+ revokeLink(ctx: Ctx, linkId: string): Promise<void>;
285
+ /** Who can reach an item, and by which links and invites. Only an owner may look. Link tokens are never shown. */
286
+ shares(ctx: Ctx, loc: string): Promise<{
287
+ grants: {
288
+ subjectId: agentic_identity.Json;
289
+ role: agentic_identity.Json;
290
+ }[];
291
+ links: {
292
+ id: agentic_identity.Json;
293
+ role: agentic_identity.Json;
294
+ signedInOnly: boolean;
295
+ expiresAt: string | number | boolean | agentic_identity.Json[] | {
296
+ [key: string]: agentic_identity.Json;
297
+ } | null;
298
+ revoked: boolean;
299
+ }[];
300
+ invites: {
301
+ id: agentic_identity.Json;
302
+ role: agentic_identity.Json;
303
+ email: string | number | boolean | agentic_identity.Json[] | {
304
+ [key: string]: agentic_identity.Json;
305
+ } | null;
306
+ handle: string | number | boolean | agentic_identity.Json[] | {
307
+ [key: string]: agentic_identity.Json;
308
+ } | null;
309
+ expiresAt: string | number | boolean | agentic_identity.Json[] | {
310
+ [key: string]: agentic_identity.Json;
311
+ } | null;
312
+ accepted: boolean;
313
+ }[];
314
+ }>;
315
+ openShared: (principal: PrincipalContext | null, id: string, linkToken?: string) => Promise<Node>;
316
+ /** Live children of a folder that a link visitor or a signed-in person may read, sorted by name. */
317
+ listShared(principal: PrincipalContext | null, id: string, linkToken?: string): Promise<Node[]>;
318
+ readShared(principal: PrincipalContext | null, id: string, linkToken?: string): Promise<{
319
+ node: Node;
320
+ data: Uint8Array;
321
+ contentType?: string;
322
+ }>;
323
+ };
324
+
325
+ export { type ActorKind as A, type Ctx as C, type Drive as D, type ErrorCode as E, type Node as N, type OnConflict as O, ROOT_ID as R, type SearchQuery as S, type Actor as a, ACTOR_KINDS as b, type Action as c, type Can as d, type DriveDeps as e, DriveError as f, createDrive as g, parseName as p };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { O as OnConflict, D as Drive, C as Ctx, N as Node, A as ActorKind, a as Actor } from './drive-DRQ8LiHl.js';
2
- export { b as ACTOR_KINDS, c as Action, d as Can, e as DriveDeps, f as DriveError, E as ErrorCode, R as ROOT_ID, S as SearchQuery, g as createDrive, p as parseName } from './drive-DRQ8LiHl.js';
1
+ import { O as OnConflict, D as Drive, C as Ctx, N as Node, A as ActorKind, a as Actor } from './drive-DKOs1FyM.js';
2
+ export { b as ACTOR_KINDS, c as Action, d as Can, e as DriveDeps, f as DriveError, E as ErrorCode, R as ROOT_ID, S as SearchQuery, g as createDrive, p as parseName } from './drive-DKOs1FyM.js';
3
3
  import { AdapterOptions, Blobs, Store } from 'agentic-identity';
4
+ export { B as BackfillOptions, a as BackfillResult, b as backfillAuthorIds, c as backfillResources } from './migrate-B79gRPMu.js';
4
5
 
5
6
  declare const VERSION: string;
6
7
 
@@ -33,8 +34,11 @@ interface UploadOptions {
33
34
  declare function upload(drive: Drive, ctx: Ctx, path: string, source: AsyncIterable<Uint8Array> | ReadableStream<Uint8Array>, opts?: UploadOptions): Promise<Node>;
34
35
 
35
36
  /**
36
- * Everything one owner has, as a zip. `manifest.json` lists each item with its
37
- * metadata and, for a file, the entry that holds its bytes. Trashed items are included and flagged.
37
+ * Everything one owner has, as a zip. `manifest.json` names the person (id and kind), lists each item with its
38
+ * metadata and, for a file, the entry that holds its bytes, and adds what the person made in other drives, the
39
+ * shares they hold, the invites they sent, and the audit entries of what they did. Only the person in person gets
40
+ * those parts, and the items of their agents that have an identity of their own (no bytes). For an agent the
41
+ * manifest says `partial: true`. Trashed items are included and flagged.
38
42
  * Entries are named by item id, never by file name. A file whose bytes are gone is marked `missing`.
39
43
  */
40
44
  declare function buildExport(drive: Drive, ctx: Ctx, now?: () => number): Promise<Uint8Array>;
@@ -76,6 +80,8 @@ declare function publicNode(node: Node, path?: string): {
76
80
  updatedAt: number;
77
81
  createdBy: Actor;
78
82
  updatedBy: Actor;
83
+ createdById: string;
84
+ updatedById: string;
79
85
  trashed: boolean;
80
86
  trashedAt: number | null;
81
87
  } | {
@@ -92,6 +98,8 @@ declare function publicNode(node: Node, path?: string): {
92
98
  updatedAt: number;
93
99
  createdBy: Actor;
94
100
  updatedBy: Actor;
101
+ createdById: string;
102
+ updatedById: string;
95
103
  trashed: boolean;
96
104
  trashedAt: number | null;
97
105
  path: string;
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-4QE7E5UQ.js";
3
+ } from "./chunk-RWMBGIAS.js";
4
+ import {
5
+ backfillAuthorIds,
6
+ backfillResources
7
+ } from "./chunk-3LISWG7H.js";
4
8
  import {
5
9
  ACTOR_KINDS,
6
10
  DEFAULT_MAX_BYTES,
@@ -17,13 +21,15 @@ import {
17
21
  sniffMime,
18
22
  upload,
19
23
  writeZip
20
- } from "./chunk-QRGKXPLX.js";
24
+ } from "./chunk-6CWBHSJ7.js";
21
25
  export {
22
26
  ACTOR_KINDS,
23
27
  DEFAULT_MAX_BYTES,
24
28
  DriveError,
25
29
  ROOT_ID,
26
30
  VERSION,
31
+ backfillAuthorIds,
32
+ backfillResources,
27
33
  buildExport,
28
34
  createDrive,
29
35
  createFileBlobs,
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { D as Drive, C as Ctx } from './drive-DRQ8LiHl.js';
2
+ import { D as Drive, C as Ctx } from './drive-DKOs1FyM.js';
3
3
  import 'agentic-identity';
4
4
 
5
5
  declare function createMcpServer(drive: Drive, ctx: Ctx, exportDir: string): McpServer;
package/dist/mcp.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-4QE7E5UQ.js";
3
+ } from "./chunk-RWMBGIAS.js";
4
4
  import {
5
5
  ACTOR_KINDS,
6
6
  DriveError,
7
7
  buildExport,
8
8
  publicNode,
9
9
  upload
10
- } from "./chunk-QRGKXPLX.js";
10
+ } from "./chunk-6CWBHSJ7.js";
11
11
 
12
12
  // src/mcp.ts
13
13
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -16,7 +16,7 @@ import { mkdirSync, writeFileSync } from "fs";
16
16
  import { join } from "path";
17
17
  import { z } from "zod";
18
18
  var conflict = z.enum(["fail", "rename", "replace"]).optional();
19
- var loc = z.string().describe('A path that starts with "/", or an item id.');
19
+ var loc = z.string().describe('A path, such as "/x" or "<folder id>/x", or an item id.');
20
20
  var ok = (value) => ({ content: [{ type: "text", text: JSON.stringify(value) }] });
21
21
  function failure(error) {
22
22
  if (!(error instanceof DriveError)) throw error;
@@ -69,6 +69,23 @@ function createMcpServer(drive, ctx, exportDir) {
69
69
  async (q) => (await drive.search(ctx, q)).map((r) => publicNode(r.node, r.path)),
70
70
  { readOnlyHint: true }
71
71
  );
72
+ tool(
73
+ "drive_home",
74
+ "List files and app documents together, newest change first. Pass `app` (such as docs) to see one app's documents.",
75
+ { app: z.string().optional(), limit: z.number().int().min(1).optional() },
76
+ async ({ app, limit }) => {
77
+ const home = await drive.home(ctx, { app, limit });
78
+ return { apps: home.apps, files: home.files, items: home.items.map((i) => ({ ...publicNode(i.node, i.path), app: i.app })) };
79
+ },
80
+ { readOnlyHint: true }
81
+ );
82
+ tool(
83
+ "drive_register_document",
84
+ "Put an app document in the drive, as a reference such as docs:<id>. A document that is already there comes back as it is.",
85
+ { ref: z.string(), name: z.string(), folder: z.string().optional() },
86
+ async ({ ref, name, folder }) => publicNode(await drive.registerDocument(ctx, { ref, name, folder }))
87
+ );
88
+ tool("drive_forget_document", "Move every reference to an app document to the trash.", { ref: z.string() }, async ({ ref }) => drive.forgetDocument(ctx, ref));
72
89
  tool(
73
90
  "drive_export",
74
91
  "Write everything the owner has to a new zip file in the export folder. Give a plain file name that ends in .zip. It never overwrites a file.",
@@ -0,0 +1,29 @@
1
+ import { Store } from 'agentic-identity';
2
+
3
+ type BackfillOptions = {
4
+ /** Where the last call stopped. Pass the `nextCursor` it returned. */
5
+ cursor?: string;
6
+ /** How many pages this call runs. The default runs them all. */
7
+ maxPages?: number;
8
+ };
9
+ type BackfillResult = {
10
+ fixed: number;
11
+ nextCursor?: string;
12
+ };
13
+ /**
14
+ * Items made before drive kept `createdById` and `updatedById` have no such fields, so an export or an erase
15
+ * cannot find what a person made in other drives. It sets the two fields from `createdBy` and `updatedBy`. It
16
+ * changes nothing else. Run it on the store once, after the old Worker has stopped writing. It returns how
17
+ * many items it fixed, and `nextCursor` while items remain.
18
+ */
19
+ declare function backfillAuthorIds(store: Store, options?: BackfillOptions): Promise<BackfillResult>;
20
+ /**
21
+ * Items made before sharing was on have no `resources` doc. With sharing on, their owner gets NOT_FOUND. This
22
+ * writes the doc of each such item, with the parent a new item gets: its folder, or the drive root. An item
23
+ * that has a doc is left alone, so a second run changes nothing. A damaged item stops the run, because a doc
24
+ * with a guessed parent would give access that nobody granted. Run it after the old Worker has stopped
25
+ * writing, and before the new Worker serves the drive.
26
+ */
27
+ declare function backfillResources(store: Store, options?: BackfillOptions): Promise<BackfillResult>;
28
+
29
+ export { type BackfillOptions as B, type BackfillResult as a, backfillAuthorIds as b, backfillResources as c };
package/dist/worker.d.ts CHANGED
@@ -1,17 +1,12 @@
1
1
  import { Hono } from 'hono';
2
2
  import { D1Like, R2Like } from 'agentic-identity/cloudflare';
3
- import { C as Ctx, D as Drive } from './drive-DRQ8LiHl.js';
4
- import 'agentic-identity';
3
+ import { Store } from 'agentic-identity';
4
+ import { C as Ctx, D as Drive } from './drive-DKOs1FyM.js';
5
+ export { B as BackfillOptions, a as BackfillResult, b as backfillAuthorIds, c as backfillResources } from './migrate-B79gRPMu.js';
5
6
 
6
7
  interface WorkerEnv {
7
8
  DB: D1Like;
8
9
  FILES: R2Like;
9
- /**
10
- * JSON: `{ "<token>": { "owner": "u1", "actor": "u1", "kind": "human" } }`. A token is at least 32 characters.
11
- * Every grant needs an owner, an actor id, and an actor kind. A token with a missing or bad part gets 401.
12
- * Without the secret, every request fails with 401.
13
- */
14
- DRIVE_API_TOKENS?: string;
15
10
  DRIVE_MAX_BYTES?: string;
16
11
  /** The origin of this Worker, such as `https://drive.example.com`. Signed download URLs start with it. */
17
12
  DRIVE_PUBLIC_URL?: string;
@@ -23,11 +18,16 @@ type App = {
23
18
  Bindings: WorkerEnv;
24
19
  Variables: {
25
20
  drive: Drive;
21
+ store: Store;
26
22
  ctx: Ctx;
27
23
  };
28
24
  };
29
- /** The seam for sign-in. A later release swaps this for agentic-identity. */
30
- declare function authenticate(request: Request, env: WorkerEnv): Ctx | null;
25
+ /**
26
+ * The caller behind a bearer token, resolved by the store-backed Auth of agentic-identity: the credential
27
+ * names a principal, and the whole delegation chain must be live. Only a bearer token counts. A session
28
+ * cookie is refused, because no check of the request origin stands behind it here. Any problem gives null.
29
+ */
30
+ declare function authenticate(request: Request, store: Store, now?: () => number): Promise<Ctx | null>;
31
31
  declare function createApp(): Hono<App>;
32
32
  declare const _default: {
33
33
  fetch: (request: Request, env: WorkerEnv, ctx: unknown) => Response | Promise<Response>;