agentic-drive 0.0.0-stage → 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.
- package/CHANGELOG.md +112 -0
- package/LICENSE +21 -0
- package/README.md +134 -2
- package/SKILL.md +25 -0
- package/dist/chunk-3LISWG7H.js +65 -0
- package/dist/chunk-6CWBHSJ7.js +1351 -0
- package/dist/chunk-RWMBGIAS.js +7 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +235 -0
- package/dist/drive-DKOs1FyM.d.ts +325 -0
- package/dist/index.d.ts +108 -0
- package/dist/index.js +44 -0
- package/dist/mcp.d.ts +12 -0
- package/dist/mcp.js +116 -0
- package/dist/migrate-B79gRPMu.d.ts +29 -0
- package/dist/worker.d.ts +36 -0
- package/dist/worker.js +307 -0
- package/llms.txt +14 -0
- package/package.json +75 -4
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface CliIo {
|
|
3
|
+
out(text: string): void;
|
|
4
|
+
err(text: string): void;
|
|
5
|
+
env: Record<string, string | undefined>;
|
|
6
|
+
}
|
|
7
|
+
declare class UsageError extends Error {
|
|
8
|
+
}
|
|
9
|
+
declare function runCli(argv: string[], io: CliIo): Promise<number>;
|
|
10
|
+
|
|
11
|
+
export { type CliIo, UsageError, runCli };
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
VERSION
|
|
4
|
+
} from "./chunk-RWMBGIAS.js";
|
|
5
|
+
import {
|
|
6
|
+
DriveError,
|
|
7
|
+
buildExport,
|
|
8
|
+
openLocal,
|
|
9
|
+
publicNode,
|
|
10
|
+
upload
|
|
11
|
+
} from "./chunk-6CWBHSJ7.js";
|
|
12
|
+
|
|
13
|
+
// src/cli.ts
|
|
14
|
+
import { createReadStream, realpathSync, writeFileSync } from "fs";
|
|
15
|
+
import { basename, join, resolve } from "path";
|
|
16
|
+
import { pathToFileURL } from "url";
|
|
17
|
+
import { parseArgs } from "util";
|
|
18
|
+
var HELP = `agentic-drive ${VERSION}
|
|
19
|
+
Store, sort, and share files and folders.
|
|
20
|
+
|
|
21
|
+
Usage: agentic-drive <command> [flags]
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
ls [path] List a folder.
|
|
25
|
+
stat <path|id> Show one item.
|
|
26
|
+
mkdir <path> [-p] Make a folder.
|
|
27
|
+
upload <local> <remote> Upload a file. End remote with "/" to keep the name.
|
|
28
|
+
download <remote> [out] Save a file. Use "-" for stdout.
|
|
29
|
+
mv <src> <folder> Move an item. Use --name to rename it.
|
|
30
|
+
rm <path|id> Move an item to the trash.
|
|
31
|
+
trash List the trash.
|
|
32
|
+
restore <id> Put a trashed item back.
|
|
33
|
+
purge <id> Delete a trashed item for good.
|
|
34
|
+
search Find items. Flags: --name --mime --kind --min-size --max-size --created-by-kind --limit
|
|
35
|
+
export <out.zip> Write everything you own to a zip with a JSON manifest.
|
|
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.
|
|
40
|
+
mcp Start the MCP server on stdio.
|
|
41
|
+
|
|
42
|
+
Flags:
|
|
43
|
+
--dir <path> Data folder. Env AGENTIC_DRIVE_DIR. Default ./.agentic-drive
|
|
44
|
+
--owner <id> Drive owner. Env AGENTIC_DRIVE_OWNER. Default "me"
|
|
45
|
+
--actor <id> Who acts. Env AGENTIC_DRIVE_ACTOR. Default the owner
|
|
46
|
+
--actor-kind <k> Actor kind. Env AGENTIC_DRIVE_KIND. Default human
|
|
47
|
+
--on-conflict <m> fail, rename, or replace
|
|
48
|
+
-p, --parents Create missing parent folders
|
|
49
|
+
`;
|
|
50
|
+
var UsageError = class extends Error {
|
|
51
|
+
};
|
|
52
|
+
var options = {
|
|
53
|
+
dir: { type: "string" },
|
|
54
|
+
owner: { type: "string" },
|
|
55
|
+
actor: { type: "string" },
|
|
56
|
+
"actor-kind": { type: "string" },
|
|
57
|
+
"on-conflict": { type: "string" },
|
|
58
|
+
parents: { type: "boolean", short: "p" },
|
|
59
|
+
mime: { type: "string" },
|
|
60
|
+
"max-bytes": { type: "string" },
|
|
61
|
+
kind: { type: "string" },
|
|
62
|
+
name: { type: "string" },
|
|
63
|
+
parent: { type: "string" },
|
|
64
|
+
"min-size": { type: "string" },
|
|
65
|
+
"max-size": { type: "string" },
|
|
66
|
+
"created-by-kind": { type: "string" },
|
|
67
|
+
limit: { type: "string" },
|
|
68
|
+
app: { type: "string" },
|
|
69
|
+
yes: { type: "boolean" },
|
|
70
|
+
help: { type: "boolean", short: "h" },
|
|
71
|
+
version: { type: "boolean", short: "V" }
|
|
72
|
+
};
|
|
73
|
+
function num(value, flag) {
|
|
74
|
+
if (value === void 0) return void 0;
|
|
75
|
+
const n = Number(value);
|
|
76
|
+
if (!Number.isFinite(n) || n < 0) throw new UsageError(`--${flag} needs a number of 0 or more`);
|
|
77
|
+
return n;
|
|
78
|
+
}
|
|
79
|
+
async function runCli(argv, io) {
|
|
80
|
+
try {
|
|
81
|
+
const { values, positionals } = parseArgs({ args: argv, options, allowPositionals: true, strict: true });
|
|
82
|
+
const [command, ...args] = positionals;
|
|
83
|
+
if (values.version) return io.out(VERSION), 0;
|
|
84
|
+
if (!command || values.help || command === "help") return io.out(HELP), 0;
|
|
85
|
+
const need = (n, usage) => {
|
|
86
|
+
if (args.length < n) throw new UsageError(`usage: agentic-drive ${usage}`);
|
|
87
|
+
};
|
|
88
|
+
const env = io.env;
|
|
89
|
+
const dir = resolve(values.dir ?? env.AGENTIC_DRIVE_DIR ?? ".agentic-drive");
|
|
90
|
+
const { drive, ctx } = openLocal({
|
|
91
|
+
dir,
|
|
92
|
+
owner: values.owner ?? env.AGENTIC_DRIVE_OWNER ?? "me",
|
|
93
|
+
actor: values.actor ?? env.AGENTIC_DRIVE_ACTOR,
|
|
94
|
+
kind: values["actor-kind"] ?? env.AGENTIC_DRIVE_KIND
|
|
95
|
+
});
|
|
96
|
+
const onConflict = values["on-conflict"];
|
|
97
|
+
if (onConflict && !["fail", "rename", "replace"].includes(onConflict)) throw new UsageError("--on-conflict needs fail, rename, or replace");
|
|
98
|
+
const print = (value) => io.out(JSON.stringify(value));
|
|
99
|
+
switch (command) {
|
|
100
|
+
case "ls":
|
|
101
|
+
print((await drive.list(ctx, args[0] ?? "/")).map((n) => publicNode(n)));
|
|
102
|
+
break;
|
|
103
|
+
case "stat":
|
|
104
|
+
need(1, "stat <path|id>");
|
|
105
|
+
print(publicNode(await drive.stat(ctx, args[0])));
|
|
106
|
+
break;
|
|
107
|
+
case "mkdir":
|
|
108
|
+
need(1, "mkdir <path> [-p]");
|
|
109
|
+
print(publicNode(await drive.mkdir(ctx, args[0], { parents: values.parents })));
|
|
110
|
+
break;
|
|
111
|
+
case "upload": {
|
|
112
|
+
need(2, "upload <local> <remote>");
|
|
113
|
+
const remote = args[1].endsWith("/") ? `${args[1]}${basename(args[0])}` : args[1];
|
|
114
|
+
const node = await upload(drive, ctx, remote, createReadStream(resolve(args[0])), {
|
|
115
|
+
mime: values.mime,
|
|
116
|
+
maxBytes: num(values["max-bytes"], "max-bytes"),
|
|
117
|
+
parents: values.parents,
|
|
118
|
+
onConflict
|
|
119
|
+
});
|
|
120
|
+
print(publicNode(node));
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "download": {
|
|
124
|
+
need(1, "download <remote> [out]");
|
|
125
|
+
const { node, data } = await drive.read(ctx, args[0]);
|
|
126
|
+
const out = args[1] ?? node.name;
|
|
127
|
+
if (out === "-") process.stdout.write(data);
|
|
128
|
+
else {
|
|
129
|
+
writeFileSync(resolve(out), data);
|
|
130
|
+
print({ saved: resolve(out), size: data.byteLength, hash: node.hash });
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case "mv":
|
|
135
|
+
need(2, "mv <src> <folder>");
|
|
136
|
+
print(publicNode(await drive.move(ctx, args[0], args[1], { name: values.name, onConflict })));
|
|
137
|
+
break;
|
|
138
|
+
case "rm":
|
|
139
|
+
need(1, "rm <path|id>");
|
|
140
|
+
print(publicNode(await drive.trash(ctx, args[0])));
|
|
141
|
+
break;
|
|
142
|
+
case "trash":
|
|
143
|
+
print((await drive.listTrash(ctx)).map((n) => publicNode(n)));
|
|
144
|
+
break;
|
|
145
|
+
case "restore":
|
|
146
|
+
need(1, "restore <id>");
|
|
147
|
+
print(publicNode(await drive.restore(ctx, args[0], { parentId: values.parent, onConflict })));
|
|
148
|
+
break;
|
|
149
|
+
case "purge":
|
|
150
|
+
need(1, "purge <id>");
|
|
151
|
+
print(await drive.purge(ctx, args[0]));
|
|
152
|
+
break;
|
|
153
|
+
case "search":
|
|
154
|
+
print((await drive.search(ctx, {
|
|
155
|
+
name: values.name,
|
|
156
|
+
mime: values.mime,
|
|
157
|
+
kind: values.kind,
|
|
158
|
+
minSize: num(values["min-size"], "min-size"),
|
|
159
|
+
maxSize: num(values["max-size"], "max-size"),
|
|
160
|
+
createdByKind: values["created-by-kind"],
|
|
161
|
+
limit: num(values.limit, "limit")
|
|
162
|
+
})).map((r) => publicNode(r.node, r.path)));
|
|
163
|
+
break;
|
|
164
|
+
case "export": {
|
|
165
|
+
need(1, "export <out.zip>");
|
|
166
|
+
const zip = await buildExport(drive, ctx);
|
|
167
|
+
writeFileSync(resolve(args[0]), zip);
|
|
168
|
+
print({ saved: resolve(args[0]), size: zip.byteLength });
|
|
169
|
+
break;
|
|
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;
|
|
184
|
+
case "delete-all":
|
|
185
|
+
if (!values.yes) throw new UsageError("delete-all erases you for good. Add --yes to confirm.");
|
|
186
|
+
print(await drive.erasePerson(ctx));
|
|
187
|
+
break;
|
|
188
|
+
case "mcp": {
|
|
189
|
+
const { startMcp } = await import("./mcp.js");
|
|
190
|
+
await startMcp({ drive, ctx, exportDir: join(dir, "exports") });
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
default:
|
|
194
|
+
throw new UsageError(`unknown command "${command}"`);
|
|
195
|
+
}
|
|
196
|
+
return 0;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (error instanceof UsageError || error instanceof TypeError && "code" in error && String(error.code).startsWith("ERR_PARSE_ARGS")) {
|
|
199
|
+
io.err(JSON.stringify({ error: { code: "USAGE", message: error.message } }));
|
|
200
|
+
return 2;
|
|
201
|
+
}
|
|
202
|
+
if (error instanceof DriveError) {
|
|
203
|
+
io.err(JSON.stringify({ error: { code: error.code, message: error.message } }));
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
207
|
+
io.err(JSON.stringify({ error: { code: "LOCAL_FILE_NOT_FOUND", message: error.message } }));
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
io.err(JSON.stringify({ error: { code: "INTERNAL", message: error instanceof Error ? error.message : String(error) } }));
|
|
211
|
+
return 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function isMainModule() {
|
|
215
|
+
const invoked = process.argv[1];
|
|
216
|
+
if (typeof invoked !== "string") return false;
|
|
217
|
+
try {
|
|
218
|
+
return import.meta.url === pathToFileURL(realpathSync(invoked)).href;
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (isMainModule()) {
|
|
224
|
+
runCli(process.argv.slice(2), {
|
|
225
|
+
out: (t) => console.log(t),
|
|
226
|
+
err: (t) => console.error(t),
|
|
227
|
+
env: process.env
|
|
228
|
+
}).then((code) => {
|
|
229
|
+
if (code !== 0) process.exit(code);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
export {
|
|
233
|
+
UsageError,
|
|
234
|
+
runCli
|
|
235
|
+
};
|
|
@@ -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
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
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';
|
|
5
|
+
|
|
6
|
+
declare const VERSION: string;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A Store kept in a journal file: one JSON line per committed write set, replayed on open.
|
|
10
|
+
* It is the shared memory store plus the journal, so it follows the same rules. One process at a time.
|
|
11
|
+
* A half-written last line (a crash during an append) is cut off on open. A bad line anywhere else
|
|
12
|
+
* throws, because the data cannot be trusted. If an append fails, the store refuses every later call.
|
|
13
|
+
* `shared` exists for the contract suites: stores that share a name share one live dataset, so do not
|
|
14
|
+
* open the same directory twice with it in production.
|
|
15
|
+
*/
|
|
16
|
+
declare function createFileStore(dir: string, options?: AdapterOptions & {
|
|
17
|
+
shared?: string;
|
|
18
|
+
}): Store;
|
|
19
|
+
/** Blobs kept as files under `dir`. One process at a time: a conditional put is atomic only inside one process. File names are key hashes, so no key escapes the folder. */
|
|
20
|
+
declare function createFileBlobs(dir: string, options?: AdapterOptions): Blobs;
|
|
21
|
+
|
|
22
|
+
declare const DEFAULT_MAX_BYTES: number;
|
|
23
|
+
/** The type named by the first bytes, or null when they match no known signature. */
|
|
24
|
+
declare function sniffMime(head: Uint8Array): string | null;
|
|
25
|
+
interface UploadOptions {
|
|
26
|
+
/** Refuse a stream longer than this. Defaults to 25 MiB. */
|
|
27
|
+
maxBytes?: number;
|
|
28
|
+
/** The type the caller claims. Known file signatures override it. */
|
|
29
|
+
mime?: string;
|
|
30
|
+
parents?: boolean;
|
|
31
|
+
onConflict?: OnConflict;
|
|
32
|
+
}
|
|
33
|
+
/** Reads the stream with a size cap, finds the type, hashes the bytes, then stores the file. */
|
|
34
|
+
declare function upload(drive: Drive, ctx: Ctx, path: string, source: AsyncIterable<Uint8Array> | ReadableStream<Uint8Array>, opts?: UploadOptions): Promise<Node>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
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.
|
|
42
|
+
* Entries are named by item id, never by file name. A file whose bytes are gone is marked `missing`.
|
|
43
|
+
*/
|
|
44
|
+
declare function buildExport(drive: Drive, ctx: Ctx, now?: () => number): Promise<Uint8Array>;
|
|
45
|
+
|
|
46
|
+
interface ZipEntry {
|
|
47
|
+
name: string;
|
|
48
|
+
data: Uint8Array;
|
|
49
|
+
}
|
|
50
|
+
declare function writeZip(entries: ZipEntry[], at: number): Uint8Array;
|
|
51
|
+
/** Reads a zip made by `writeZip`. It checks every CRC. */
|
|
52
|
+
declare function readZip(bytes: Uint8Array): Map<string, Uint8Array>;
|
|
53
|
+
|
|
54
|
+
interface LocalOptions {
|
|
55
|
+
/** Where the drive keeps its data. */
|
|
56
|
+
dir: string;
|
|
57
|
+
/** Whose drive this is. */
|
|
58
|
+
owner: string;
|
|
59
|
+
/** Who acts. Defaults to the owner. */
|
|
60
|
+
actor?: string;
|
|
61
|
+
/** What kind of actor. Defaults to `human`. Set an agent kind when an agent runs the command. */
|
|
62
|
+
kind?: ActorKind;
|
|
63
|
+
}
|
|
64
|
+
declare function openLocal(options: LocalOptions): {
|
|
65
|
+
drive: Drive;
|
|
66
|
+
ctx: Ctx;
|
|
67
|
+
};
|
|
68
|
+
/** A node as the CLI and MCP print it: no storage keys. */
|
|
69
|
+
declare function publicNode(node: Node, path?: string): {
|
|
70
|
+
id: string;
|
|
71
|
+
owner: string;
|
|
72
|
+
parentId: string;
|
|
73
|
+
kind: "folder" | "file" | "ref";
|
|
74
|
+
name: string;
|
|
75
|
+
size: number;
|
|
76
|
+
mime: string | null;
|
|
77
|
+
hash: string | null;
|
|
78
|
+
ref: string | null;
|
|
79
|
+
createdAt: number;
|
|
80
|
+
updatedAt: number;
|
|
81
|
+
createdBy: Actor;
|
|
82
|
+
updatedBy: Actor;
|
|
83
|
+
createdById: string;
|
|
84
|
+
updatedById: string;
|
|
85
|
+
trashed: boolean;
|
|
86
|
+
trashedAt: number | null;
|
|
87
|
+
} | {
|
|
88
|
+
id: string;
|
|
89
|
+
owner: string;
|
|
90
|
+
parentId: string;
|
|
91
|
+
kind: "folder" | "file" | "ref";
|
|
92
|
+
name: string;
|
|
93
|
+
size: number;
|
|
94
|
+
mime: string | null;
|
|
95
|
+
hash: string | null;
|
|
96
|
+
ref: string | null;
|
|
97
|
+
createdAt: number;
|
|
98
|
+
updatedAt: number;
|
|
99
|
+
createdBy: Actor;
|
|
100
|
+
updatedBy: Actor;
|
|
101
|
+
createdById: string;
|
|
102
|
+
updatedById: string;
|
|
103
|
+
trashed: boolean;
|
|
104
|
+
trashedAt: number | null;
|
|
105
|
+
path: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export { Actor, ActorKind, Ctx, DEFAULT_MAX_BYTES, Drive, Node, OnConflict, type UploadOptions, VERSION, buildExport, createFileBlobs, createFileStore, openLocal, publicNode, readZip, sniffMime, upload, writeZip };
|