agentic-documents 0.1.0 → 0.3.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/dist/cli.js CHANGED
@@ -2,18 +2,24 @@
2
2
  import {
3
3
  createFileBlobs,
4
4
  createFileStore
5
- } from "./chunk-BSXUNQDR.js";
5
+ } from "./chunk-TFCRPEEM.js";
6
+ import {
7
+ LiveService,
8
+ whoAmI
9
+ } from "./chunk-R5XUTBC3.js";
6
10
  import {
7
11
  DocsService
8
- } from "./chunk-LRPLNXOF.js";
12
+ } from "./chunk-FZA5LQV3.js";
9
13
  import {
10
14
  VERSION
11
15
  } from "./chunk-5Y66UCTE.js";
16
+ import {
17
+ commands
18
+ } from "./chunk-GYQFTF2E.js";
12
19
  import {
13
20
  DocError,
14
- commands,
15
21
  parseAuthor
16
- } from "./chunk-4YVWD5PH.js";
22
+ } from "./chunk-YBBQNCMC.js";
17
23
 
18
24
  // src/cli.ts
19
25
  import { readFileSync, writeFileSync } from "fs";
@@ -26,6 +32,7 @@ var options = {
26
32
  as: { type: "string" },
27
33
  scopes: { type: "string" },
28
34
  dir: { type: "string" },
35
+ live: { type: "string" },
29
36
  file: { type: "string" },
30
37
  out: { type: "string" },
31
38
  help: { type: "boolean", short: "h" },
@@ -47,6 +54,8 @@ function help() {
47
54
  " agent_of_agent, agent, user_token.",
48
55
  " --scopes <a,b> Rights for the author, such as docs:accept. Or set AGENTIC_DOCS_SCOPES. An agent",
49
56
  " needs docs:accept to accept a suggestion.",
57
+ " --live <url> Join live rooms on this server, as the author of AGENTIC_DOCS_TOKEN. Or set",
58
+ " AGENTIC_DOCS_LIVE_URL. The server sets your author kind, so --as, --scopes, and --dir are refused.",
50
59
  " --dir <path> Where documents live. Or set AGENTIC_DOCS_DIR. Default: .agentic-documents",
51
60
  " --file <path> Read content (or markdown for new) from a file.",
52
61
  " --out <path> Write the export to a file.",
@@ -76,13 +85,40 @@ async function main(argv, env = process.env) {
76
85
  console.log(values.version ? VERSION : help());
77
86
  return 0;
78
87
  }
79
- const dir = values.dir ?? env.AGENTIC_DOCS_DIR ?? ".agentic-documents";
80
- const service = new DocsService({ blobs: createFileBlobs(join(dir, "blobs")), store: createFileStore(join(dir, "store")) });
81
- const scopes = (values.scopes ?? env.AGENTIC_DOCS_SCOPES)?.split(",").map((s) => s.trim()).filter(Boolean);
82
- const named = parseAuthor(values.as ?? env.AGENTIC_DOCS_AS);
83
- const author = named && scopes?.length ? { ...named, scopes } : named;
88
+ const liveUrl = values.live ?? (env.AGENTIC_DOCS_LIVE_URL || void 0);
89
+ let service;
90
+ let author;
91
+ if (liveUrl !== void 0) {
92
+ if (!/^https?:\/\/[^/\s]+/.test(liveUrl)) {
93
+ console.error(`--live needs an http:// or https:// address, not "${liveUrl}"`);
94
+ return 2;
95
+ }
96
+ if (values.as !== void 0 || values.scopes !== void 0 || values.dir !== void 0) {
97
+ console.error("--as, --scopes, and --dir do not apply with --live: the server sets the author kind and the scopes from the token, and keeps the documents.");
98
+ return 2;
99
+ }
100
+ const token = env.AGENTIC_DOCS_TOKEN;
101
+ if (!token) {
102
+ console.error("set AGENTIC_DOCS_TOKEN to a bearer token to join a live room");
103
+ return 2;
104
+ }
105
+ try {
106
+ author = (await whoAmI(liveUrl, token)).author;
107
+ } catch (error) {
108
+ if (!(error instanceof DocError)) throw error;
109
+ console.error(`${error.code}: ${error.message}`);
110
+ return 1;
111
+ }
112
+ service = new LiveService({ server: liveUrl, token });
113
+ } else {
114
+ const dir = values.dir ?? env.AGENTIC_DOCS_DIR ?? ".agentic-documents";
115
+ service = new DocsService({ blobs: createFileBlobs(join(dir, "blobs")), store: createFileStore(join(dir, "store")) });
116
+ const scopes = (values.scopes ?? env.AGENTIC_DOCS_SCOPES)?.split(",").map((s) => s.trim()).filter(Boolean);
117
+ const named = parseAuthor(values.as ?? env.AGENTIC_DOCS_AS);
118
+ author = named && scopes?.length ? { ...named, scopes } : named;
119
+ }
84
120
  if (name === "mcp") {
85
- const { startMcp } = await import("./mcp-2VZIGHIX.js");
121
+ const { startMcp } = await import("./mcp-P2C75IIG.js");
86
122
  await startMcp(service, author);
87
123
  return 0;
88
124
  }
@@ -91,7 +127,7 @@ async function main(argv, env = process.env) {
91
127
  console.error(`unknown command "${name}". Run "${NAME} --help".`);
92
128
  return 2;
93
129
  }
94
- const { as: _as, scopes: _scopes, dir: _dir, file, out, help: _help, version: _version, ...fields } = values;
130
+ const { as: _as, scopes: _scopes, dir: _dir, live: _live, file, out, help: _help, version: _version, ...fields } = values;
95
131
  const input = { ...command.shape.id ? { id } : {}, ...fields };
96
132
  try {
97
133
  if (file !== void 0) {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as Principal, A as Anchor, a as AgenticDoc, T as TextTarget, b as AnchorTarget, D as DocsService, c as PmNode } from './service-eYISo6-b.js';
2
- export { d as DocInfo, e as DocOptions, f as DocsPorts, E as EditEntry, g as PRINCIPAL_KINDS, h as PrincipalKind, S as Section, i as SectionTarget, j as Target, k as blocksToMarkdown, m as markdownToBlocks, p as parseAuthor } from './service-eYISo6-b.js';
1
+ import { P as Principal, A as Anchor, a as AgenticDoc, T as TextTarget, b as AnchorTarget, D as DocAccess, c as PmNode } from './service-BKkxEnaJ.js';
2
+ export { d as DocInfo, e as DocOptions, f as DocsPorts, g as DocsService, E as EditEntry, h as PRINCIPAL_KINDS, i as PrincipalKind, S as Section, j as SectionTarget, k as Target, l as blocksToMarkdown, m as markdownToBlocks, p as parseAuthor } from './service-BKkxEnaJ.js';
3
3
  import { Schema } from 'prosemirror-model';
4
4
  import { z } from 'zod';
5
5
  import { AdapterOptions, Blobs, Store } from 'agentic-identity';
@@ -95,7 +95,7 @@ interface Command {
95
95
  description: string;
96
96
  shape: z.ZodRawShape;
97
97
  readOnly: boolean;
98
- run(service: DocsService, input: Record<string, unknown>, author: Principal | undefined): Promise<unknown>;
98
+ run(service: DocAccess, input: Record<string, unknown>, author: Principal | undefined): Promise<unknown>;
99
99
  }
100
100
  declare const commands: Command[];
101
101
 
@@ -142,4 +142,4 @@ declare const ODT_MIME = "application/vnd.oasis.opendocument.text";
142
142
  /** Write blocks as an OpenDocument text file (.odt): content, styles, and images as embedded files. */
143
143
  declare function exportOdt(blocks: PmNode[]): Uint8Array;
144
144
 
145
- export { AgenticDoc, Anchor, AnchorTarget, type Command, DocError, type DocErrorCode, DocsService, EXPORT_FORMATS, type ExportFormat, type ExportOptions, type ExportResult, type FileBlobsOptions, type Message, ODT_MIME, PmNode, Principal, type Suggestion, Suggestions, TextTarget, type Thread, Threads, commands, createFileBlobs, createFileStore, exportDoc, exportOdt, schema };
145
+ export { AgenticDoc, Anchor, AnchorTarget, type Command, DocError, type DocErrorCode, EXPORT_FORMATS, type ExportFormat, type ExportOptions, type ExportResult, type FileBlobsOptions, type Message, ODT_MIME, PmNode, Principal, type Suggestion, Suggestions, TextTarget, type Thread, Threads, commands, createFileBlobs, createFileStore, exportDoc, exportOdt, schema };
package/dist/index.js CHANGED
@@ -1,26 +1,28 @@
1
1
  import {
2
2
  createFileBlobs,
3
3
  createFileStore
4
- } from "./chunk-BSXUNQDR.js";
4
+ } from "./chunk-TFCRPEEM.js";
5
5
  import {
6
6
  DocsService
7
- } from "./chunk-LRPLNXOF.js";
7
+ } from "./chunk-FZA5LQV3.js";
8
8
  import {
9
- AgenticDoc,
10
- DocError,
11
9
  EXPORT_FORMATS,
12
10
  ODT_MIME,
11
+ commands,
12
+ exportDoc,
13
+ exportOdt
14
+ } from "./chunk-GYQFTF2E.js";
15
+ import {
16
+ AgenticDoc,
17
+ DocError,
13
18
  PRINCIPAL_KINDS,
14
19
  Suggestions,
15
20
  Threads,
16
21
  blocksToMarkdown,
17
- commands,
18
- exportDoc,
19
- exportOdt,
20
22
  markdownToBlocks,
21
23
  parseAuthor,
22
24
  schema
23
- } from "./chunk-4YVWD5PH.js";
25
+ } from "./chunk-YBBQNCMC.js";
24
26
  export {
25
27
  AgenticDoc,
26
28
  DocError,
@@ -0,0 +1,20 @@
1
+ import { createDurableObjectRealtime } from 'agentic-realtime/worker';
2
+ export { SyncRoom } from 'agentic-realtime/worker';
3
+ import { WorkerEnv } from './worker.js';
4
+ import 'hono';
5
+ import 'agentic-identity';
6
+ import 'agentic-identity/cloudflare';
7
+ import 'agentic-realtime';
8
+ import './service-BKkxEnaJ.js';
9
+ import 'yjs';
10
+
11
+ type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
12
+ interface LiveEnv extends WorkerEnv {
13
+ /** Durable Object namespace of the live rooms. Bind `SyncRoom` to it. */
14
+ SYNC_ROOM?: Namespace;
15
+ }
16
+ declare const _default: {
17
+ fetch: (request: Request, env: LiveEnv, ctx: unknown) => Response | Promise<Response>;
18
+ };
19
+
20
+ export { type LiveEnv, _default as default };
@@ -0,0 +1,27 @@
1
+ import {
2
+ createApp,
3
+ regionOf,
4
+ serviceFor
5
+ } from "./chunk-IU3YIQ4Y.js";
6
+ import "./chunk-FZA5LQV3.js";
7
+ import "./chunk-GYQFTF2E.js";
8
+ import {
9
+ DocError
10
+ } from "./chunk-YBBQNCMC.js";
11
+
12
+ // src/live-worker.ts
13
+ import { createDurableObjectRealtime } from "agentic-realtime/worker";
14
+ import { SyncRoom } from "agentic-realtime/worker";
15
+ var app = createApp(serviceFor, {
16
+ live: (env) => {
17
+ const region = regionOf(env);
18
+ const rooms = env.SYNC_ROOM;
19
+ if (!rooms) throw new DocError("MISCONFIGURED", "bind a Durable Object namespace as SYNC_ROOM");
20
+ return createDurableObjectRealtime(rooms, region === "eu" ? "eu" : void 0);
21
+ }
22
+ });
23
+ var live_worker_default = { fetch: (request, env, ctx) => app.fetch(request, env, ctx) };
24
+ export {
25
+ SyncRoom,
26
+ live_worker_default as default
27
+ };
package/dist/live.d.ts ADDED
@@ -0,0 +1,73 @@
1
+ import { Peer } from 'agentic-realtime/client';
2
+ import { a as AgenticDoc, P as Principal, e as DocOptions, D as DocAccess } from './service-BKkxEnaJ.js';
3
+ import 'yjs';
4
+ import 'agentic-identity';
5
+
6
+ interface LiveOptions {
7
+ /** The Worker address, such as `https://docs.example.com`. */
8
+ server: string;
9
+ /** Document id. */
10
+ id: string;
11
+ /** A bearer token. The server maps it to the author kind and the role. */
12
+ token: string;
13
+ /** The name other people see in presence. Defaults to the author name. */
14
+ name?: string;
15
+ /** Reconnect after a drop. Edits made offline merge when the socket returns. */
16
+ reconnect?: {
17
+ initialMs?: number;
18
+ maxMs?: number;
19
+ };
20
+ doc?: DocOptions;
21
+ /** Start an empty room from the stored document. Default true. A merge is safe when the room already holds it. */
22
+ seed?: boolean;
23
+ }
24
+ /** Who the server says the token is. */
25
+ interface Me {
26
+ author: Principal;
27
+ role: "editor" | "viewer";
28
+ }
29
+ declare function whoAmI(server: string, token: string): Promise<Me>;
30
+ /**
31
+ * One document, open in a live room. The author of every edit is the one the server reads from the
32
+ * token, so an agent token always writes agent edits. The room keeps the edits, the stored author
33
+ * label of each update, and the state for the next join.
34
+ */
35
+ declare class LiveDoc {
36
+ readonly doc: AgenticDoc;
37
+ readonly me: Me;
38
+ private readonly client;
39
+ private readonly api;
40
+ private readonly refusedAtStart;
41
+ private constructor();
42
+ static join(options: LiveOptions): Promise<LiveDoc>;
43
+ /** Saves the room's state to the stored document, through the Blobs port. A viewer cannot save. */
44
+ save(): Promise<void>;
45
+ /** The other people and agents in the room. `machine` is true for an agent. */
46
+ get peers(): Peer[];
47
+ /** Why the room dropped an update, in order. */
48
+ get denied(): string[];
49
+ /**
50
+ * Runs one change as the token's author and waits until the room has it. A viewer fails before the change.
51
+ * After a refusal from the room the local copy still holds the change. Leave and join again.
52
+ */
53
+ edit<T>(run: (doc: AgenticDoc, author: Principal) => T): Promise<T>;
54
+ /** Waits until the room has every change made so far. */
55
+ flush(): Promise<void>;
56
+ leave(): Promise<void>;
57
+ }
58
+ /**
59
+ * The commands of the CLI and the MCP server, run in a live room. Each call joins the room, runs one
60
+ * change as the token's author, saves to the stored document, and leaves. A read joins and leaves.
61
+ */
62
+ declare class LiveService implements DocAccess {
63
+ private readonly options;
64
+ constructor(options: Pick<LiveOptions, "server" | "token" | "name" | "doc">);
65
+ private join;
66
+ /** The room is only a copy, so a new document is made in the store. A room seeds from it on the next join. */
67
+ create(id: string, markdown: string): Promise<void>;
68
+ read<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
69
+ write<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
70
+ private exists;
71
+ }
72
+
73
+ export { LiveDoc, type LiveOptions, LiveService, type Me, whoAmI };
package/dist/live.js ADDED
@@ -0,0 +1,12 @@
1
+ import {
2
+ LiveDoc,
3
+ LiveService,
4
+ whoAmI
5
+ } from "./chunk-R5XUTBC3.js";
6
+ import "./chunk-FZA5LQV3.js";
7
+ import "./chunk-YBBQNCMC.js";
8
+ export {
9
+ LiveDoc,
10
+ LiveService,
11
+ whoAmI
12
+ };
@@ -2,9 +2,11 @@ import {
2
2
  VERSION
3
3
  } from "./chunk-5Y66UCTE.js";
4
4
  import {
5
- DocError,
6
5
  commands
7
- } from "./chunk-4YVWD5PH.js";
6
+ } from "./chunk-GYQFTF2E.js";
7
+ import {
8
+ DocError
9
+ } from "./chunk-YBBQNCMC.js";
8
10
 
9
11
  // src/mcp.ts
10
12
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -138,14 +138,36 @@ interface DocsPorts {
138
138
  /** Holds the catalog: who created each document, and when. */
139
139
  store: Store;
140
140
  }
141
+ /** What every command needs: make a document, read it, and change it. A live room can stand behind it as well as the ports. */
142
+ interface DocAccess {
143
+ create(id: string, markdown: string, author: Principal): Promise<void>;
144
+ read<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
145
+ write<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
146
+ }
141
147
  /** Loads and saves documents as Yjs updates in a Blobs port. The CLI, MCP server, and API share it. */
142
- declare class DocsService {
148
+ declare class DocsService implements DocAccess {
143
149
  private readonly options;
144
150
  private readonly blobs;
145
151
  private readonly store;
146
152
  constructor(ports: DocsPorts, options?: DocOptions);
147
153
  private key;
148
154
  create(id: string, markdown: string, author: Principal): Promise<void>;
155
+ /** The stored document as a Yjs update. A live client seeds its room from it. */
156
+ state(id: string): Promise<Uint8Array>;
157
+ /**
158
+ * Merge a Yjs update from a live room into the stored document, and create the document when it
159
+ * has none. A merge is safe to repeat and to run in any order. The save is conditional, so two
160
+ * checkpoints at once both land.
161
+ */
162
+ checkpoint(id: string, update: Uint8Array, author: Principal): Promise<void>;
163
+ /**
164
+ * The encoded document, once it reads back and every stored entry has the shape the commands rely on. Yjs does not
165
+ * check types, so an update can put text where the editor expects blocks, or a thread with no messages, and then a
166
+ * command fails on every later call.
167
+ */
168
+ private checked;
169
+ private fits;
170
+ private catalog;
149
171
  /** Who created the document and when, or null when it has no catalog entry (made before the catalog, or a failed catalog write). */
150
172
  info(id: string): Promise<DocInfo | null>;
151
173
  read<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
@@ -156,4 +178,4 @@ declare class DocsService {
156
178
  write<T>(id: string, run: (doc: AgenticDoc) => T): Promise<T>;
157
179
  }
158
180
 
159
- export { type Anchor as A, DocsService as D, type EditEntry as E, type Principal as P, type Section as S, type TextTarget as T, AgenticDoc as a, type AnchorTarget as b, type PmNode as c, type DocInfo as d, type DocOptions as e, type DocsPorts as f, PRINCIPAL_KINDS as g, type PrincipalKind as h, type SectionTarget as i, type Target as j, blocksToMarkdown as k, markdownToBlocks as m, parseAuthor as p };
181
+ export { type Anchor as A, type DocAccess as D, type EditEntry as E, type Principal as P, type Section as S, type TextTarget as T, AgenticDoc as a, type AnchorTarget as b, type PmNode as c, type DocInfo as d, type DocOptions as e, type DocsPorts as f, DocsService as g, PRINCIPAL_KINDS as h, type PrincipalKind as i, type SectionTarget as j, type Target as k, blocksToMarkdown as l, markdownToBlocks as m, parseAuthor as p };
package/dist/worker.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { Hono } from 'hono';
2
+ import { Region } from 'agentic-identity';
2
3
  import { D1Like, R2Like } from 'agentic-identity/cloudflare';
3
- import { D as DocsService, P as Principal } from './service-eYISo6-b.js';
4
+ import { Realtime, Role } from 'agentic-realtime';
5
+ import { g as DocsService, P as Principal } from './service-BKkxEnaJ.js';
4
6
  import 'yjs';
5
- import 'agentic-identity';
6
7
 
7
8
  interface WorkerEnv {
8
9
  /**
@@ -17,21 +18,33 @@ interface WorkerEnv {
17
18
  /** R2 bucket: each document as a Yjs update. Create it with `--jurisdiction eu`. */
18
19
  DOCS?: R2Like;
19
20
  /**
20
- * JSON object: bearer token (32 bytes or more) to `kind:name`, or to `{ "as": "kind:name", "scopes": ["docs:accept"] }`.
21
+ * JSON object: bearer token (32 bytes or more) to `kind:name`, or to `{ "as": "kind:name", "scopes": ["docs:accept"], "role": "viewer" }`.
22
+ * A role is `editor` (the default) or `viewer`. A viewer reads and cannot write, over HTTP or in a live room.
21
23
  * Set it as a secret. A short token or a bad entry refuses the whole config.
22
24
  */
23
25
  AGENTIC_DOCS_TOKENS?: string;
24
26
  }
27
+ type DocRole = Extract<Role, "editor" | "viewer">;
25
28
  /** A service, or a function that makes one from the Worker bindings. */
26
29
  type ServiceSource = DocsService | ((env: WorkerEnv) => Promise<DocsService>);
27
- declare function createApp(source: ServiceSource): Hono<{
30
+ type AppEnv = {
28
31
  Bindings: WorkerEnv;
29
32
  Variables: {
30
33
  author: Principal;
34
+ role: DocRole;
31
35
  };
32
- }>;
36
+ };
37
+ interface AppOptions {
38
+ /** Makes the live-room server from the Worker bindings. Without it, the ws route answers 404. */
39
+ live?: (env: WorkerEnv) => Realtime;
40
+ }
41
+ declare function createApp(source: ServiceSource, options?: AppOptions): Hono<AppEnv>;
42
+ /** The region claim, or a MISCONFIGURED error. The HTTP API and the live rooms both use it, so neither runs without it. */
43
+ declare function regionOf(env: WorkerEnv): Region;
44
+ /** The service over D1 and R2. One region claim covers D1 and R2. The bucket is declared with the "eu" jurisdiction in wrangler.jsonc: keep the two in step. */
45
+ declare function serviceFor(env: WorkerEnv): Promise<DocsService>;
33
46
  declare const _default: {
34
47
  fetch: (request: Request, env: WorkerEnv, ctx: unknown) => Response | Promise<Response>;
35
48
  };
36
49
 
37
- export { type ServiceSource, type WorkerEnv, createApp, _default as default };
50
+ export { type AppOptions, type DocRole, type ServiceSource, type WorkerEnv, createApp, _default as default, regionOf, serviceFor };
package/dist/worker.js CHANGED
@@ -1,162 +1,15 @@
1
1
  import {
2
- DocsService
3
- } from "./chunk-LRPLNXOF.js";
4
- import {
5
- DocError,
6
- PRINCIPAL_KINDS,
7
- commands,
8
- parseAuthor
9
- } from "./chunk-4YVWD5PH.js";
10
-
11
- // src/worker.ts
12
- import { Hono } from "hono";
13
- import { assertRegion, parseRegion } from "agentic-identity";
14
- import { D1_SCHEMA, createD1Store, createR2Blobs } from "agentic-identity/cloudflare";
15
- var STATUS = {
16
- NO_MATCH: 404,
17
- NO_SECTION: 404,
18
- NO_THREAD: 404,
19
- NO_SUGGESTION: 404,
20
- NO_DOCUMENT: 404,
21
- AMBIGUOUS_MATCH: 409,
22
- STALE_ANCHOR: 409,
23
- ALREADY_DECIDED: 409,
24
- DOCUMENT_EXISTS: 409,
25
- CONFLICT: 409,
26
- FORBIDDEN: 403,
27
- LOCKED: 503,
28
- MISCONFIGURED: 500
29
- };
30
- function same(a, b) {
31
- let diff = a.length ^ b.length;
32
- for (let i = 0; i < Math.max(a.length, b.length); i++) diff |= (a.charCodeAt(i) || 0) ^ (b.charCodeAt(i) || 0);
33
- return diff === 0;
34
- }
35
- var MIN_TOKEN_BYTES = 32;
36
- var warned = /* @__PURE__ */ new Set();
37
- function refuse(reason) {
38
- if (!warned.has(reason)) {
39
- warned.add(reason);
40
- console.error(`AGENTIC_DOCS_TOKENS is refused: ${reason}`);
41
- }
42
- return void 0;
43
- }
44
- function principalOf(entry) {
45
- const object = typeof entry === "object" && entry !== null && !Array.isArray(entry) ? entry : void 0;
46
- const who = typeof entry === "string" ? entry : object?.as;
47
- const author = typeof who === "string" ? parseAuthor(who) : void 0;
48
- if (!author || !PRINCIPAL_KINDS.includes(author.kind) || !author.name) return void 0;
49
- if (object?.scopes === void 0) return author;
50
- const scopes = object.scopes;
51
- return Array.isArray(scopes) && scopes.every((s) => typeof s === "string" && s !== "") ? { ...author, scopes } : void 0;
52
- }
53
- function readTokens(raw) {
54
- let table;
55
- try {
56
- table = JSON.parse(raw ?? "{}");
57
- } catch {
58
- return refuse("not valid JSON");
59
- }
60
- if (typeof table !== "object" || table === null || Array.isArray(table)) return refuse("not a JSON object");
61
- const found = /* @__PURE__ */ new Map();
62
- for (const [token, entry] of Object.entries(table)) {
63
- if (new TextEncoder().encode(token).length < MIN_TOKEN_BYTES) return refuse(`a token is shorter than ${MIN_TOKEN_BYTES} bytes`);
64
- const principal = principalOf(entry);
65
- if (!principal) return refuse("an entry is not a valid kind:name or { as, scopes }");
66
- found.set(token, principal);
67
- }
68
- return found;
69
- }
70
- function principalFor(tokens, bearer) {
71
- let found;
72
- for (const [token, principal] of readTokens(tokens) ?? []) if (same(token, bearer)) found = principal;
73
- return found;
74
- }
75
- var fail = (code, message, status, matches = []) => Response.json({ error: { code, message, matches } }, { status });
76
- function createApp(source) {
77
- const app2 = new Hono();
78
- app2.use("*", async (c, next) => {
79
- await next();
80
- c.header("x-content-type-options", "nosniff");
81
- });
82
- app2.get("/health", (c) => c.json({ ok: true, pack: "agentic-documents" }));
83
- app2.use("/api/v1/*", async (c, next) => {
84
- const bearer = /^Bearer (.+)$/.exec(c.req.header("authorization") ?? "")?.[1];
85
- const author = bearer === void 0 ? void 0 : principalFor(c.env?.AGENTIC_DOCS_TOKENS, bearer);
86
- if (!author) return fail("UNAUTHENTICATED", "send a valid bearer token", 401);
87
- c.set("author", author);
88
- await next();
89
- });
90
- async function run(c, command, input) {
91
- try {
92
- const result = await command.run(await (typeof source === "function" ? source(c.env ?? {}) : source), input, c.get("author"));
93
- if (command.name === "export") {
94
- const { data, contentType, encoding, format } = result;
95
- const images = input.remoteImages === true || input.remoteImages === "true" ? "data: https:" : "data:";
96
- const disposition = format === "odt" ? { "content-disposition": `attachment; filename="${input.id}.odt"` } : {};
97
- const headers = { ...disposition, "content-type": contentType, "content-security-policy": `default-src 'none'; style-src 'unsafe-inline'; img-src ${images}; sandbox` };
98
- return encoding === "base64" ? c.body(Uint8Array.from(atob(data), (ch) => ch.charCodeAt(0)), 200, headers) : c.body(data, 200, headers);
99
- }
100
- if (typeof result === "string") return c.body(result, 200, { "content-type": "text/markdown; charset=utf-8" });
101
- return c.json(result);
102
- } catch (error) {
103
- if (!(error instanceof DocError)) throw error;
104
- return fail(error.code, error.message, STATUS[error.code] ?? 400, error.matches);
105
- }
106
- }
107
- async function body(c) {
108
- const parsed = await c.req.json().catch(() => void 0);
109
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
110
- throw new DocError("INVALID_INPUT", "the body must be a JSON object");
111
- }
112
- return parsed;
113
- }
114
- const create = commands.find((c) => c.name === "new");
115
- app2.post("/api/v1/docs", async (c) => run(c, create, await body(c)));
116
- for (const command of commands.filter((c) => c !== create)) {
117
- const path = `/api/v1/docs/:id/${command.name}`;
118
- if (command.readOnly) app2.get(path, (c) => run(c, command, { ...c.req.query(), id: c.req.param("id") }));
119
- else app2.post(path, async (c) => run(c, command, { ...await body(c), id: c.req.param("id") }));
120
- }
121
- app2.notFound(() => fail("NOT_FOUND", "no such route", 404));
122
- app2.onError((error) => {
123
- if (error instanceof DocError) return fail(error.code, error.message, STATUS[error.code] ?? 400, error.matches);
124
- console.error(error instanceof Error ? error.name : "error");
125
- return fail("INTERNAL", "the server could not finish the request", 500);
126
- });
127
- return app2;
128
- }
129
- var schemas = /* @__PURE__ */ new WeakMap();
130
- var urlSecret;
131
- async function serviceFor(env) {
132
- if (!env.DB || !env.DOCS) throw new DocError("MISCONFIGURED", "bind a D1 database as DB and an R2 bucket as DOCS");
133
- let region;
134
- try {
135
- region = parseRegion(env.AGENTIC_DOCS_REGION);
136
- } catch {
137
- throw new DocError("MISCONFIGURED", 'set AGENTIC_DOCS_REGION to "eu" or "us" ("unknown" only for local development)');
138
- }
139
- if (region === "unknown" && env.AGENTIC_DOCS_UNSAFE_REGION !== "1") {
140
- throw new DocError("MISCONFIGURED", 'the region "unknown" turns the residency check off; set AGENTIC_DOCS_UNSAFE_REGION=1 to accept that for local development');
141
- }
142
- const db = env.DB;
143
- let ready = schemas.get(db);
144
- if (!ready) {
145
- ready = db.prepare(D1_SCHEMA).run().catch((error) => {
146
- schemas.delete(db);
147
- throw error;
148
- });
149
- schemas.set(db, ready);
150
- }
151
- await ready;
152
- const store = createD1Store(db, { region });
153
- const blobs = createR2Blobs(env.DOCS, { region, urlBase: "https://invalid.example/unused", urlSecret: urlSecret ??= `${crypto.randomUUID()}${crypto.randomUUID()}` });
154
- assertRegion(region, store, blobs);
155
- return new DocsService({ store, blobs });
156
- }
157
- var app = createApp(serviceFor);
158
- var worker_default = { fetch: (request, env, ctx) => app.fetch(request, env, ctx) };
2
+ createApp,
3
+ regionOf,
4
+ serviceFor,
5
+ worker_default
6
+ } from "./chunk-IU3YIQ4Y.js";
7
+ import "./chunk-FZA5LQV3.js";
8
+ import "./chunk-GYQFTF2E.js";
9
+ import "./chunk-YBBQNCMC.js";
159
10
  export {
160
11
  createApp,
161
- worker_default as default
12
+ worker_default as default,
13
+ regionOf,
14
+ serviceFor
162
15
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-documents",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Write documents with people and agents in real time.",
5
5
  "keywords": [
6
6
  "documents",
@@ -32,6 +32,7 @@
32
32
  "dist",
33
33
  "SECURITY.md",
34
34
  "SKILL.md",
35
+ "CHANGELOG.md",
35
36
  "llms.txt"
36
37
  ],
37
38
  "publishConfig": {
@@ -48,10 +49,19 @@
48
49
  "./worker": {
49
50
  "types": "./dist/worker.d.ts",
50
51
  "import": "./dist/worker.js"
52
+ },
53
+ "./live": {
54
+ "types": "./dist/live.d.ts",
55
+ "import": "./dist/live.js"
56
+ },
57
+ "./live-worker": {
58
+ "types": "./dist/live-worker.d.ts",
59
+ "import": "./dist/live-worker.js"
51
60
  }
52
61
  },
53
62
  "scripts": {
54
63
  "build": "tsup",
64
+ "prepack": "npm run build",
55
65
  "typecheck": "tsc --noEmit",
56
66
  "test": "vitest run",
57
67
  "clean": "rm -rf dist",
@@ -59,7 +69,8 @@
59
69
  },
60
70
  "dependencies": {
61
71
  "@modelcontextprotocol/sdk": "^1.12.0",
62
- "agentic-identity": "^0.2.0",
72
+ "agentic-identity": "^0.4.0",
73
+ "agentic-realtime": "^0.1.0",
63
74
  "fflate": "^0.8.3",
64
75
  "hono": "^4.13.14",
65
76
  "lib0": "^0.2.119",