agentic-comments 0.5.0 → 0.5.1

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
@@ -18,8 +18,6 @@ copy of `yjs`, because the package checks the Yjs types of your document. Instal
18
18
  npm install agentic-comments yjs
19
19
  ```
20
20
 
21
- To build from source, see Develop.
22
-
23
21
  ## Quick start
24
22
 
25
23
  ```ts
@@ -165,10 +163,6 @@ machine. It cannot check a lock held in another container or on another machine.
165
163
  share a volume can have the same process id, and each then clears the other's lock, so they can lose writes. A save that
166
164
  would pass 50 MB is refused.
167
165
 
168
- If the version you install has no `agentic-comments` command, build from source (see [Develop](#develop)).
169
- In the cloned repo, run `npm run build` and use `node dist/cli.js` where these examples say
170
- `npx agentic-comments`.
171
-
172
166
  ```sh
173
167
  export AGENTIC_COMMENTS_FILE=./comments.ydoc
174
168
  export AGENTIC_COMMENTS_AS=human:ada # kind:name. The operator sets this.
@@ -180,10 +174,10 @@ npx agentic-comments accept <id>
180
174
  ```
181
175
 
182
176
  - Commands: `init`, `list`, `show`, `comment`, `reply`, `resolve`, `reopen`, `relocate`, `suggestions`,
183
- `suggest`, `accept`, `reject`, `mcp`. Run `agentic-comments --help` for flags.
177
+ `suggest`, `accept`, `reject`, `mcp`. Run `npx agentic-comments --help` for flags.
184
178
  - Every result is JSON. An error is JSON on stderr with a `code`, and the exit code is 1. A usage error
185
179
  exits 2.
186
- - The principal comes from `--as` or `AGENTIC_COMMENTS_AS`. Scopes come only from
180
+ - The principal comes from `--as` or `AGENTIC_COMMENTS_AS`, as `kind:name`. The name starts with A-Z a-z 0-9 `_` `-`, then uses A-Z a-z 0-9 `.` `_` `-`, and is at most 128 characters. Scopes come only from
187
181
  `AGENTIC_COMMENTS_SCOPES`, a comma list such as `comments:accept`. No flag takes a scope or an author,
188
182
  and a flag that does not apply to a command is refused. `AGENTIC_COMMENTS_LOCK_TIMEOUT_MS` sets the wait
189
183
  for a lock, in whole milliseconds.
@@ -203,9 +197,13 @@ npx agentic-comments accept <id>
203
197
  document in a D1 database through the `Store` port of `agentic-identity`. It checks every request with the
204
198
  `Auth` port, so the host picks who the caller is and no request carries an author.
205
199
 
206
- Set the principals as a secret. It is JSON from a bearer token (32 bytes or more) to `kind:name`, or to
207
- `{ "as": "kind:name", "scopes": [...] }`. One bad entry refuses the whole table. A request with no valid
208
- token gets `401`.
200
+ Set the principals as a secret. It is JSON from a bearer token to `kind:name`, or to
201
+ `{ "as": "kind:name", "scopes": [...] }`. A token is 32 to 512 bytes, from A-Z a-z 0-9 `_` `-`, so a base64 token with `+` or `/` is refused.
202
+ A name starts with A-Z a-z 0-9 `_` `-`, then uses A-Z a-z 0-9 `.` `_` `-`, and is at most 128 characters.
203
+ One bad entry refuses the whole table. A request with no valid token gets `401`.
204
+ A request whose `Authorization` header is not `Bearer` followed by 1 to 512 bytes of A-Z a-z 0-9 `_` `-` gets `401`, before any custom `Auth` runs.
205
+ A write with no `Authorization` header (a session cookie, for example) gets `403` unless its `Origin` is in the `origins`
206
+ option of `createApp`. The default lists none.
209
207
 
210
208
  ```sh
211
209
  wrangler secret put AGENTIC_COMMENTS_TOKENS
@@ -244,6 +244,7 @@ var FileStore = class {
244
244
  };
245
245
 
246
246
  // src/operator.ts
247
+ import { PrincipalParseError, parsePrincipal } from "agentic-identity";
247
248
  function operatorActor(as, scopes) {
248
249
  const value = as?.trim();
249
250
  const cut = value === void 0 ? -1 : value.indexOf(":");
@@ -251,7 +252,13 @@ function operatorActor(as, scopes) {
251
252
  throw new CommentsError("INVALID_AUTHOR", `set the principal as kind:name, for example human:ada. Kinds: ${PRINCIPAL_KINDS.join(", ")}`);
252
253
  }
253
254
  const list = scopes === void 0 || scopes.trim() === "" ? [] : scopes.split(",").map((s) => s.trim());
254
- return stamp({ kind: value.slice(0, cut), name: value.slice(cut + 1), scopes: list });
255
+ try {
256
+ const { kind, id } = parsePrincipal({ kind: value.slice(0, cut), id: value.slice(cut + 1) });
257
+ return stamp({ kind, name: id, scopes: list });
258
+ } catch (error) {
259
+ if (!(error instanceof PrincipalParseError)) throw error;
260
+ throw new CommentsError("INVALID_AUTHOR", `the principal needs a known kind (${PRINCIPAL_KINDS.join(", ")}) and a name of A-Z a-z 0-9 . _ -`);
261
+ }
255
262
  }
256
263
 
257
264
  export {
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import { createRequire } from "module";
3
- var VERSION = true ? "0.5.0" : readPackageVersion();
3
+ var VERSION = true ? "0.5.1" : readPackageVersion();
4
4
 
5
5
  export {
6
6
  VERSION
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-WQHJNHAR.js";
3
+ } from "./chunk-REFQNIEP.js";
4
4
  import {
5
5
  CommentsService
6
6
  } from "./chunk-NQWRFKJ6.js";
package/dist/cli.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  FileStore,
4
4
  lockTimeoutFrom,
5
5
  operatorActor
6
- } from "./chunk-HXXJNFEO.js";
6
+ } from "./chunk-QJXI3P5D.js";
7
7
  import {
8
8
  VERSION
9
- } from "./chunk-WQHJNHAR.js";
9
+ } from "./chunk-REFQNIEP.js";
10
10
  import {
11
11
  CommentsService,
12
12
  TEXT_ROOT
@@ -155,7 +155,7 @@ async function runCli(argv, io) {
155
155
  return 0;
156
156
  }
157
157
  if (p.command === "mcp") {
158
- const { startMcp } = await import("./mcp-JMEXA2RI.js");
158
+ const { startMcp } = await import("./mcp-XUV3RPOO.js");
159
159
  await startMcp(store, operatorActor(p.flags["--as"] ?? io.env.AGENTIC_COMMENTS_AS, io.env.AGENTIC_COMMENTS_SCOPES));
160
160
  return 0;
161
161
  }
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-WQHJNHAR.js";
3
+ } from "./chunk-REFQNIEP.js";
4
4
  import {
5
5
  ACCEPT_SCOPE,
6
6
  Actor,
package/dist/local.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  MAX_FILE,
4
4
  lockTimeoutFrom,
5
5
  operatorActor
6
- } from "./chunk-HXXJNFEO.js";
6
+ } from "./chunk-QJXI3P5D.js";
7
7
  import {
8
8
  CommentsService,
9
9
  TEXT_ROOT,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createMcpServer,
3
3
  startMcp
4
- } from "./chunk-ABXAPWXD.js";
5
- import "./chunk-WQHJNHAR.js";
4
+ } from "./chunk-TFRLOBJW.js";
5
+ import "./chunk-REFQNIEP.js";
6
6
  import "./chunk-NQWRFKJ6.js";
7
7
  import "./chunk-BSNBDKR2.js";
8
8
  export {
package/dist/mcp-bin.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  FileStore,
4
4
  lockTimeoutFrom,
5
5
  operatorActor
6
- } from "./chunk-HXXJNFEO.js";
6
+ } from "./chunk-QJXI3P5D.js";
7
7
  import {
8
8
  startMcp
9
- } from "./chunk-ABXAPWXD.js";
9
+ } from "./chunk-TFRLOBJW.js";
10
10
  import {
11
11
  VERSION
12
- } from "./chunk-WQHJNHAR.js";
12
+ } from "./chunk-REFQNIEP.js";
13
13
  import "./chunk-NQWRFKJ6.js";
14
14
  import "./chunk-BSNBDKR2.js";
15
15
 
package/dist/worker.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Hono } from 'hono';
2
2
  import { Auth, Store, PrincipalContext, Region } from 'agentic-identity';
3
+ import { RequirePrincipalOptions } from 'agentic-identity/http';
3
4
  import { D1Like } from 'agentic-identity/cloudflare';
4
5
  import { A as Actor } from './suggestions-CvJwo6r4.js';
5
6
  import { C as CommentsService } from './service-hpWbnSmc.js';
@@ -13,7 +14,7 @@ interface WorkerEnv {
13
14
  /** D1 database: each document, with its threads, as a Yjs update. */
14
15
  DB?: D1Like;
15
16
  /**
16
- * JSON object: bearer token (32 bytes or more) to `kind:name`, or to `{ "as": "kind:name", "scopes": ["comments:write"] }`.
17
+ * JSON object: bearer token (32 to 512 bytes of A-Z a-z 0-9 _ -) to `kind:name` (the name uses A-Z a-z 0-9 . _ -), or to `{ "as": "kind:name", "scopes": ["comments:write"] }`.
17
18
  * Set it as a secret. A short token or a bad entry refuses the whole config.
18
19
  */
19
20
  AGENTIC_COMMENTS_TOKENS?: string;
@@ -39,6 +40,8 @@ type DocsSource = CommentsDocs | ((env: WorkerEnv) => Promise<CommentsDocs>);
39
40
  interface AppOptions {
40
41
  /** The credential check. The default reads `AGENTIC_COMMENTS_TOKENS`. Pass any `Auth` from agentic-identity to replace it. */
41
42
  auth?: Auth | ((env: WorkerEnv) => Auth);
43
+ /** Origins that may send a write with a session cookie. A bearer token needs none. Passed to `requirePrincipal`. */
44
+ origins?: RequirePrincipalOptions["origins"];
42
45
  }
43
46
  type AppEnv = {
44
47
  Bindings: WorkerEnv;
package/dist/worker.js CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  import {
6
6
  ACCEPT_SCOPE,
7
7
  CommentsError,
8
- PRINCIPAL_KINDS,
9
8
  exact,
10
9
  invalid,
11
10
  stamp
@@ -13,7 +12,8 @@ import {
13
12
 
14
13
  // src/worker.ts
15
14
  import { Hono } from "hono";
16
- import { StoreConflictError, assertRegion, isScope, parseRegion, hasScope } from "agentic-identity";
15
+ import { StoreConflictError, assertRegion, isScope, parseRegion, hasScope, parsePrincipal } from "agentic-identity";
16
+ import { requirePrincipal } from "agentic-identity/http";
17
17
  import { D1_SCHEMA, createD1Store } from "agentic-identity/cloudflare";
18
18
  import * as Y from "yjs";
19
19
  var READ = "comments:read";
@@ -22,6 +22,7 @@ var WRITE = "comments:write";
22
22
  var ADMIN = "comments:admin";
23
23
  var DEFAULT_SCOPES = [COMMENT];
24
24
  var MIN_TOKEN_BYTES = 32;
25
+ var TOKEN_SHAPE = /^[A-Za-z0-9_-]{1,512}$/;
25
26
  var warned = /* @__PURE__ */ new Set();
26
27
  function refuse(reason) {
27
28
  if (!warned.has(reason)) {
@@ -40,9 +41,13 @@ function contextOf(entry) {
40
41
  const who = typeof entry === "string" ? entry : object?.as;
41
42
  const cut = typeof who === "string" ? who.indexOf(":") : -1;
42
43
  if (typeof who !== "string" || cut < 1 || cut === who.length - 1) return void 0;
43
- const kind = who.slice(0, cut);
44
- const name = who.slice(cut + 1);
45
- if (!PRINCIPAL_KINDS.includes(kind)) return void 0;
44
+ let kind;
45
+ let name;
46
+ try {
47
+ ({ kind, id: name } = parsePrincipal({ kind: who.slice(0, cut), id: who.slice(cut + 1) }));
48
+ } catch {
49
+ return void 0;
50
+ }
46
51
  const given = object?.scopes;
47
52
  if (given !== void 0 && !(Array.isArray(given) && given.every(isScope))) return void 0;
48
53
  try {
@@ -61,6 +66,7 @@ function createTokenAuth(raw) {
61
66
  for (const [token, entry] of Object.entries(parsed)) {
62
67
  const context = contextOf(entry);
63
68
  if (new TextEncoder().encode(token).length < MIN_TOKEN_BYTES) throw new Error(`a token is shorter than ${MIN_TOKEN_BYTES} bytes`);
69
+ if (!TOKEN_SHAPE.test(token)) throw new Error("a token holds a character outside A-Z a-z 0-9 _ - or is over 512 bytes");
64
70
  if (!context) throw new Error("an entry is not a valid kind:name or { as, scopes }");
65
71
  table.set(token, context);
66
72
  }
@@ -186,8 +192,12 @@ function createApp(source, options = {}) {
186
192
  app2.get("/health", (c) => c.json({ ok: true, pack: "agentic-comments" }));
187
193
  app2.use("/api/v1/*", async (c, next) => {
188
194
  const auth = typeof options.auth === "function" ? options.auth(c.env ?? {}) : options.auth ?? createTokenAuth(c.env?.AGENTIC_COMMENTS_TOKENS);
189
- const context = await auth.authenticate(c.req.raw);
190
- if (!context) return fail("UNAUTHENTICATED", "send a valid bearer token", 401);
195
+ const context = await requirePrincipal(c.req.raw, auth, { origins: options.origins });
196
+ if (context instanceof Response) {
197
+ if (context.status === 401) return Response.json({ error: { code: "UNAUTHENTICATED", message: "send a valid bearer token" } }, { status: 401, headers: { "www-authenticate": "Bearer" } });
198
+ if (context.status === 403) return fail("FORBIDDEN", "this credential may not do this", 403);
199
+ return fail("INTERNAL", "the credential check is unavailable", context.status);
200
+ }
191
201
  c.set("context", context);
192
202
  await next();
193
203
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-comments",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Comment threads and suggestions anchored to any content, from people and agents.",
5
5
  "keywords": [
6
6
  "comments",
@@ -76,7 +76,7 @@
76
76
  },
77
77
  "dependencies": {
78
78
  "@modelcontextprotocol/sdk": "^1.32.2",
79
- "agentic-identity": "^0.4.0",
79
+ "agentic-identity": "^0.6.0",
80
80
  "hono": "^4.13.14",
81
81
  "zod": "^3.25.76"
82
82
  },