@tineira/apc-conformance 0.0.0-stage → 0.7.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/LICENSE +21 -0
- package/README.md +35 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +129 -0
- package/dist/http.d.ts +85 -0
- package/dist/http.js +458 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +434 -0
- package/dist/shared.d.ts +56 -0
- package/dist/shared.js +144 -0
- package/dist/workers.d.ts +27 -0
- package/dist/workers.js +95 -0
- package/package.json +52 -3
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
/** Lets "cloudflare:workers" resolve on Node, to a DurableObject base class that keeps ctx and env. */
|
|
3
|
+
export declare function installWorkersStandIn(): void;
|
|
4
|
+
/** One page of the person's, connected to the object: what it was sent, and whether it is still open. */
|
|
5
|
+
export interface FakePage {
|
|
6
|
+
readyState: number;
|
|
7
|
+
sent: string[];
|
|
8
|
+
send(text: string): void;
|
|
9
|
+
close(): void;
|
|
10
|
+
serializeAttachment(value: unknown): void;
|
|
11
|
+
deserializeAttachment(): unknown;
|
|
12
|
+
}
|
|
13
|
+
/** What a Durable Object gets from Cloudflare, on a fresh in-memory database, plus the pages around it. */
|
|
14
|
+
export interface DurableObjectRoom {
|
|
15
|
+
ctx: unknown;
|
|
16
|
+
db: DatabaseSync;
|
|
17
|
+
/** The alarm the object set last, in ms. */
|
|
18
|
+
alarm: number | null;
|
|
19
|
+
pages: FakePage[];
|
|
20
|
+
/** The person opens a page: a socket that answered its heartbeat just now. */
|
|
21
|
+
openPage(): FakePage;
|
|
22
|
+
/** The person closes every page. */
|
|
23
|
+
closePages(): void;
|
|
24
|
+
/** Every message sent to any page since the last call, parsed, oldest first. */
|
|
25
|
+
takeMessages(): Array<Record<string, unknown>>;
|
|
26
|
+
}
|
|
27
|
+
export declare function durableObjectRoom(): DurableObjectRoom;
|
package/dist/workers.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Cloudflare Durable Object on Node, for the conformance suite and other local tests (no network,
|
|
3
|
+
* no workerd): the object's own code on Node's SQLite, with a stand-in for what Cloudflare gives it.
|
|
4
|
+
* The person's pages are fake WebSockets that record what the object sends them.
|
|
5
|
+
*
|
|
6
|
+
* Call `installWorkersStandIn()` before importing a module that imports "cloudflare:workers".
|
|
7
|
+
*/
|
|
8
|
+
import { registerHooks } from "node:module";
|
|
9
|
+
import { DatabaseSync } from "node:sqlite";
|
|
10
|
+
let installed = false;
|
|
11
|
+
/** Lets "cloudflare:workers" resolve on Node, to a DurableObject base class that keeps ctx and env. */
|
|
12
|
+
export function installWorkersStandIn() {
|
|
13
|
+
if (installed)
|
|
14
|
+
return;
|
|
15
|
+
installed = true;
|
|
16
|
+
registerHooks({
|
|
17
|
+
resolve(specifier, context, next) {
|
|
18
|
+
if (specifier === "cloudflare:workers")
|
|
19
|
+
return { url: "data:text/javascript,export class DurableObject { constructor(ctx, env) { this.ctx = ctx; this.env = env; } }", shortCircuit: true };
|
|
20
|
+
return next(specifier, context);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const g = globalThis;
|
|
24
|
+
g.WebSocketRequestResponsePair ??= class {
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function durableObjectRoom() {
|
|
28
|
+
const db = new DatabaseSync(":memory:");
|
|
29
|
+
const sql = {
|
|
30
|
+
exec(query, ...params) {
|
|
31
|
+
if (query.trim().replace(/;\s*$/, "").includes(";")) {
|
|
32
|
+
db.exec(query);
|
|
33
|
+
return { toArray: () => [] };
|
|
34
|
+
}
|
|
35
|
+
const statement = db.prepare(query);
|
|
36
|
+
const rows = statement.columns().length ? statement.all(...params) : (statement.run(...params), []);
|
|
37
|
+
return { toArray: () => rows };
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const tables = () => db.prepare("select name from sqlite_master where type = 'table' and name not like 'sqlite_%'").all().map((t) => String(t.name));
|
|
41
|
+
const OPEN = 1;
|
|
42
|
+
const room = {
|
|
43
|
+
db,
|
|
44
|
+
alarm: null,
|
|
45
|
+
pages: [],
|
|
46
|
+
ctx: {
|
|
47
|
+
storage: {
|
|
48
|
+
sql,
|
|
49
|
+
setAlarm: async (at) => void (room.alarm = at),
|
|
50
|
+
deleteAll: async () => {
|
|
51
|
+
for (const table of tables())
|
|
52
|
+
db.exec(`drop table ${table}`);
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
setWebSocketAutoResponse: () => { },
|
|
56
|
+
blockConcurrencyWhile: (work) => work(),
|
|
57
|
+
acceptWebSocket: () => { },
|
|
58
|
+
getWebSockets: () => room.pages.filter((p) => p.readyState === OPEN),
|
|
59
|
+
// Every open page answered its heartbeat just now.
|
|
60
|
+
getWebSocketAutoResponseTimestamp: () => new Date(),
|
|
61
|
+
},
|
|
62
|
+
openPage() {
|
|
63
|
+
let attachment = { accepted_at: Date.now() };
|
|
64
|
+
const page = {
|
|
65
|
+
readyState: OPEN,
|
|
66
|
+
sent: [],
|
|
67
|
+
send: (text) => void page.sent.push(text),
|
|
68
|
+
close: () => void (page.readyState = 3),
|
|
69
|
+
serializeAttachment: (value) => void (attachment = value),
|
|
70
|
+
deserializeAttachment: () => attachment,
|
|
71
|
+
};
|
|
72
|
+
room.pages.push(page);
|
|
73
|
+
return page;
|
|
74
|
+
},
|
|
75
|
+
closePages() {
|
|
76
|
+
for (const page of room.pages)
|
|
77
|
+
page.close();
|
|
78
|
+
},
|
|
79
|
+
takeMessages() {
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const page of room.pages) {
|
|
82
|
+
for (const text of page.sent.splice(0)) {
|
|
83
|
+
try {
|
|
84
|
+
out.push(JSON.parse(text));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// not JSON
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
return room;
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tineira/apc-conformance",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"description": "Agent Pointed Context (APC) conformance: checks an app against the standard, in process with an adapter, or live over HTTP with the apc-conformance command.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"apc",
|
|
7
|
+
"agent-pointed-context",
|
|
8
|
+
"mcp",
|
|
9
|
+
"model-context-protocol",
|
|
10
|
+
"ai-agents",
|
|
11
|
+
"conformance",
|
|
12
|
+
"testing"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://apc.tineira.com/docs",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/tineira/agent-pointed-context/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/tineira/agent-pointed-context",
|
|
21
|
+
"directory": "packages/conformance"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./workers": {
|
|
31
|
+
"types": "./dist/workers.d.ts",
|
|
32
|
+
"import": "./dist/workers.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22.15"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@tineira/apc-protocol": "^0.7.1"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {},
|
|
51
|
+
"types": "./dist/index.d.ts",
|
|
52
|
+
"bin": {
|
|
53
|
+
"apc-conformance": "./dist/cli.js"
|
|
54
|
+
}
|
|
6
55
|
}
|