@capybara-hq/testing 0.0.0-stage → 0.1.0-rc.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.
Files changed (89) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +34 -2
  4. package/dist/containers.d.ts +102 -0
  5. package/dist/containers.js +314 -0
  6. package/dist/dual-replica.d.ts +121 -0
  7. package/dist/dual-replica.js +247 -0
  8. package/dist/extension-config.d.ts +9 -0
  9. package/dist/extension-config.js +16 -0
  10. package/dist/fake-channel.d.ts +162 -0
  11. package/dist/fake-channel.js +325 -0
  12. package/dist/fake-e2b.d.ts +104 -0
  13. package/dist/fake-e2b.js +500 -0
  14. package/dist/fake-git-server.d.ts +57 -0
  15. package/dist/fake-git-server.js +323 -0
  16. package/dist/fake-lark.d.ts +234 -0
  17. package/dist/fake-lark.js +837 -0
  18. package/dist/fake-marketplace.d.ts +25 -0
  19. package/dist/fake-marketplace.js +58 -0
  20. package/dist/fake-mcp-server.d.ts +134 -0
  21. package/dist/fake-mcp-server.js +308 -0
  22. package/dist/fake-model-server.d.ts +28 -0
  23. package/dist/fake-model-server.js +67 -0
  24. package/dist/fake-npm-registry.d.ts +59 -0
  25. package/dist/fake-npm-registry.js +174 -0
  26. package/dist/fake-oauth-server.d.ts +63 -0
  27. package/dist/fake-oauth-server.js +284 -0
  28. package/dist/fake-openapi-server.d.ts +50 -0
  29. package/dist/fake-openapi-server.js +278 -0
  30. package/dist/fake-slack.d.ts +258 -0
  31. package/dist/fake-slack.js +654 -0
  32. package/dist/fixtures.d.ts +47 -0
  33. package/dist/fixtures.js +90 -0
  34. package/dist/global-setup.d.ts +15 -0
  35. package/dist/global-setup.js +58 -0
  36. package/dist/index.d.ts +25 -0
  37. package/dist/index.js +25 -0
  38. package/dist/kernel-helpers.d.ts +125 -0
  39. package/dist/kernel-helpers.js +336 -0
  40. package/dist/media-samples.d.ts +10 -0
  41. package/dist/media-samples.js +76 -0
  42. package/dist/model-conformance.d.ts +61 -0
  43. package/dist/model-conformance.js +287 -0
  44. package/dist/replica-runner.d.ts +1 -0
  45. package/dist/replica-runner.js +59 -0
  46. package/dist/sse.d.ts +20 -0
  47. package/dist/sse.js +87 -0
  48. package/fixtures/extensions/fake-bootstrap-a/.capybara-plugin/plugin.json +7 -0
  49. package/fixtures/extensions/fake-bootstrap-a/package.json +12 -0
  50. package/fixtures/extensions/fake-bootstrap-a/src/index.ts +62 -0
  51. package/fixtures/extensions/fake-bootstrap-b/.capybara-plugin/plugin.json +7 -0
  52. package/fixtures/extensions/fake-bootstrap-b/migrations/0001_init.sql +10 -0
  53. package/fixtures/extensions/fake-bootstrap-b/package.json +12 -0
  54. package/fixtures/extensions/fake-bootstrap-b/src/index.ts +123 -0
  55. package/fixtures/extensions/fake-connector/.capybara-plugin/plugin.json +7 -0
  56. package/fixtures/extensions/fake-connector/connectors/fake-demo.json +12 -0
  57. package/fixtures/extensions/fake-connector/package.json +13 -0
  58. package/fixtures/extensions/fake-connector/src/index.ts +477 -0
  59. package/fixtures/extensions/fake-dep-provider/.capybara-plugin/plugin.json +7 -0
  60. package/fixtures/extensions/fake-dep-provider/package.json +12 -0
  61. package/fixtures/extensions/fake-dep-provider/src/index.ts +19 -0
  62. package/fixtures/extensions/fake-dep-requirer/.capybara-plugin/plugin.json +8 -0
  63. package/fixtures/extensions/fake-dep-requirer/package.json +12 -0
  64. package/fixtures/extensions/fake-dep-requirer/src/index.ts +21 -0
  65. package/fixtures/extensions/fake-embed/.capybara-plugin/plugin.json +7 -0
  66. package/fixtures/extensions/fake-embed/package.json +12 -0
  67. package/fixtures/extensions/fake-embed/src/index.ts +207 -0
  68. package/fixtures/extensions/fake-im/.capybara-plugin/plugin.json +7 -0
  69. package/fixtures/extensions/fake-im/package.json +12 -0
  70. package/fixtures/extensions/fake-im/src/channel.ts +224 -0
  71. package/fixtures/extensions/fake-im/src/directory.ts +25 -0
  72. package/fixtures/extensions/fake-im/src/identity.ts +66 -0
  73. package/fixtures/extensions/fake-im/src/index.ts +91 -0
  74. package/fixtures/extensions/fake-object-store/.capybara-plugin/plugin.json +7 -0
  75. package/fixtures/extensions/fake-object-store/package.json +15 -0
  76. package/fixtures/extensions/fake-object-store/src/index.ts +210 -0
  77. package/fixtures/extensions/fake-openapi/.capybara-plugin/plugin.json +7 -0
  78. package/fixtures/extensions/fake-openapi/package.json +12 -0
  79. package/fixtures/extensions/fake-openapi/src/index.ts +258 -0
  80. package/fixtures/extensions/fake-storage-user/.capybara-plugin/plugin.json +7 -0
  81. package/fixtures/extensions/fake-storage-user/package.json +12 -0
  82. package/fixtures/extensions/fake-storage-user/src/index.ts +58 -0
  83. package/fixtures/extensions/fake-tools-ui/.capybara-plugin/plugin.json +7 -0
  84. package/fixtures/extensions/fake-tools-ui/connectors/fake-notes.json +11 -0
  85. package/fixtures/extensions/fake-tools-ui/migrations/0001_init.sql +7 -0
  86. package/fixtures/extensions/fake-tools-ui/package.json +12 -0
  87. package/fixtures/extensions/fake-tools-ui/skills/fake-notes/SKILL.md +7 -0
  88. package/fixtures/extensions/fake-tools-ui/src/index.ts +140 -0
  89. package/package.json +66 -3
@@ -0,0 +1,7 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "name": "fake-tools-ui",
4
+ "version": "0.1.0",
5
+ "publisher": "Capybara",
6
+ "description": "Test fake: an extension tool with a conversation view, a migration, a skill and a connector template"
7
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "displayName": { "zh": "笔记服务", "en": "Notes service" },
3
+ "description": "A connector template of the fake-tools-ui bundle (fake connector, kind fake)",
4
+ "template": {
5
+ "plugin": "fake-connector",
6
+ "connector": "fake",
7
+ "endpoint": { "url": "https://notes.example.com/mcp" },
8
+ "editableEndpoint": ["url"],
9
+ "auth": { "type": "bearer", "ownership": "org" }
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ -- Runs with search_path "plugin_fake_tools_ui, public": table names carry no schema prefix.
2
+ -- One item list per conversation (replaced as a whole on every write).
3
+ CREATE TABLE IF NOT EXISTS items (
4
+ conversation_id text PRIMARY KEY,
5
+ items jsonb NOT NULL,
6
+ updated_at timestamptz NOT NULL DEFAULT now()
7
+ );
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@capybara-hq/test-plugin-fake-tools-ui",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Test fake: an extension tool with a conversation view, a migration, a skill and a connector template",
6
+ "license": "Apache-2.0",
7
+ "type": "module",
8
+ "dependencies": {
9
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.0",
10
+ "zod": "^4.6.7"
11
+ }
12
+ }
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: fake-notes
3
+ description: Keep a short item list for the conversation with the fake-tools-ui write tool. Test fixture only.
4
+ ---
5
+ # fake-notes
6
+
7
+ Call `fake-tools-ui__write` with every item of the list.
@@ -0,0 +1,140 @@
1
+ import {
2
+ defineExtension,
3
+ type ExtensionContext,
4
+ ToolError,
5
+ type ToolSpec,
6
+ type UiView,
7
+ } from "@capybara-hq/plugin-sdk";
8
+ import { z } from "zod";
9
+
10
+ /**
11
+ * Test fake of a tool + UI extension: the kernel contracts an extension tool, a schema-rendered
12
+ * view, a migration and a full plugin bundle exercise, without the official todo plugin. Like todo
13
+ * it needs no object store (storage is `fake-storage-user`'s).
14
+ * - tool `write` (model-visible `fake-tools-ui__write`, default policy allow): replaces the
15
+ * conversation's item list in the extension's own table, then invalidates the view `tasks`;
16
+ * - view `tasks` (conversation tab): badge `<done>/<total>` and a checklist;
17
+ * - the plugin also ships the skill `fake-notes` and the connector template `fake-notes`
18
+ * (for the `fake-connector` extension).
19
+ */
20
+
21
+ type Db = ExtensionContext["db"];
22
+
23
+ const Item = z.object({
24
+ text: z.string().min(1),
25
+ status: z.enum(["pending", "in_progress", "completed"]),
26
+ });
27
+ const WriteInput = z.object({ items: z.array(Item).max(100) });
28
+
29
+ /** One item of the list. */
30
+ export type FakeItem = z.infer<typeof Item>;
31
+
32
+ const WRITE: ToolSpec = {
33
+ name: "write",
34
+ description:
35
+ "Replace the item list of the current conversation. Send every item each time; status is " +
36
+ "pending, in_progress or completed. Test fixture only.",
37
+ inputSchema: {
38
+ type: "object",
39
+ properties: {
40
+ items: {
41
+ type: "array",
42
+ maxItems: 100,
43
+ description: "The complete list, in order.",
44
+ items: {
45
+ type: "object",
46
+ properties: {
47
+ text: { type: "string", minLength: 1, description: "What the item is." },
48
+ status: {
49
+ type: "string",
50
+ enum: ["pending", "in_progress", "completed"],
51
+ description: "Progress of the item.",
52
+ },
53
+ },
54
+ required: ["text", "status"],
55
+ additionalProperties: false,
56
+ },
57
+ },
58
+ },
59
+ required: ["items"],
60
+ additionalProperties: false,
61
+ },
62
+ defaultPolicy: "allow",
63
+ };
64
+
65
+ const render = (items: FakeItem[]): string =>
66
+ items.length === 0
67
+ ? "The list is now empty."
68
+ : `List updated (${items.length} items):\n${items.map((i) => `[${i.status}] ${i.text}`).join("\n")}`;
69
+
70
+ async function writeItems(db: Db, conversationId: string, items: FakeItem[]): Promise<void> {
71
+ await db
72
+ .insertInto("items")
73
+ .values({ conversation_id: conversationId, items: JSON.stringify(items) })
74
+ .onConflict((oc) =>
75
+ oc.column("conversation_id").doUpdateSet((eb) => ({
76
+ items: eb.ref("excluded.items"),
77
+ updated_at: new Date(),
78
+ })),
79
+ )
80
+ .execute();
81
+ }
82
+
83
+ async function readItems(db: Db, conversationId: string): Promise<FakeItem[]> {
84
+ const row = await db
85
+ .selectFrom("items")
86
+ .select("items")
87
+ .where("conversation_id", "=", conversationId)
88
+ .executeTakeFirst();
89
+ return row === undefined ? [] : (row.items as FakeItem[]);
90
+ }
91
+
92
+ /** The `tasks` view of a list. */
93
+ export function tasksView(items: FakeItem[]): UiView {
94
+ const done = items.filter((i) => i.status === "completed").length;
95
+ return {
96
+ badge: `${done}/${items.length}`,
97
+ blocks: [{ type: "checklist", items: items.map((i) => ({ text: i.text, status: i.status })) }],
98
+ };
99
+ }
100
+
101
+ export default defineExtension({
102
+ name: "fake-tools-ui",
103
+ version: "0.1.0",
104
+ sdk: "^0.1.0-rc.0",
105
+ provides: [],
106
+ migrations: "../migrations",
107
+ ui: [{ id: "tasks", title: { zh: "条目", en: "Items" }, place: "conversation", kind: "view" }],
108
+ permissions: { tools: ["write"], ui: { views: ["tasks"] } },
109
+ setup(ctx) {
110
+ ctx.tools.register(WRITE, async (input, tctx) => {
111
+ const parsed = WriteInput.safeParse(input);
112
+ if (!parsed.success) {
113
+ throw new ToolError(
114
+ "Invalid items: pass an array of { text, status } where status is pending, in_progress or completed.",
115
+ );
116
+ }
117
+ const items = parsed.data.items;
118
+ await writeItems(ctx.db, tctx.conversationId, items);
119
+ await ctx.ui
120
+ .invalidate({
121
+ orgId: tctx.orgId,
122
+ conversationId: tctx.conversationId,
123
+ contribution: "tasks",
124
+ })
125
+ .catch(() => {});
126
+ return {
127
+ content: [{ type: "text", text: render(items) }],
128
+ display: {
129
+ summary: {
130
+ zh: `更新条目(${items.length} 项)`,
131
+ en: `Updated items (${items.length})`,
132
+ },
133
+ },
134
+ };
135
+ });
136
+ ctx.ui.view("tasks", async ({ conversationId }) =>
137
+ tasksView(conversationId === undefined ? [] : await readItems(ctx.db, conversationId)),
138
+ );
139
+ },
140
+ });
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@capybara-hq/testing",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0-rc.0",
4
+ "description": "Test helpers for Capybara: test containers, dual-replica harness and fake third-party servers",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/capybara-hq/core.git",
9
+ "directory": "packages/testing"
10
+ },
11
+ "homepage": "https://github.com/capybara-hq/core/tree/main/packages/testing#readme",
12
+ "type": "module",
13
+ "engines": {
14
+ "node": ">=24"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "./global-setup": {
25
+ "types": "./dist/global-setup.d.ts",
26
+ "import": "./dist/global-setup.js",
27
+ "default": "./dist/global-setup.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "fixtures",
33
+ "README.md",
34
+ "NOTICE"
35
+ ],
36
+ "dependencies": {
37
+ "@aws-sdk/client-s3": "^3.1154.0",
38
+ "@aws-sdk/s3-presigned-post": "^3.1154.0",
39
+ "@aws-sdk/s3-request-presigner": "^3.1154.0",
40
+ "@modelcontextprotocol/sdk": "^1.32.1",
41
+ "@testcontainers/postgresql": "^12.2.1",
42
+ "ioredis": "^6.0.0",
43
+ "kysely": "^0.29.6",
44
+ "pg": "^8.23.1",
45
+ "protobufjs": "^7.5.4",
46
+ "testcontainers": "^12.2.1",
47
+ "ws": "^8.22.0",
48
+ "zod": "^4.6.7",
49
+ "@capybara-hq/kernel": "^0.1.0-rc.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/pg": "^8.23.1",
53
+ "@types/ws": "^8.18.2",
54
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.0"
55
+ },
56
+ "peerDependencies": {
57
+ "vitest": "^5.0.5",
58
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.0",
59
+ "@capybara-hq/plugin-tools": "^0.1.0-rc.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "@capybara-hq/plugin-tools": {
63
+ "optional": true
64
+ }
65
+ },
66
+ "scripts": {
67
+ "build": "tsc -b"
68
+ }
6
69
  }