@polderlabs/bizar 4.8.0 → 5.0.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/bizar-dash/dist/assets/icons-CFqu2M-c.js +656 -0
- package/bizar-dash/dist/assets/icons-CFqu2M-c.js.map +1 -0
- package/bizar-dash/dist/assets/index-DmpSFPJY.js +9 -0
- package/bizar-dash/dist/assets/index-DmpSFPJY.js.map +1 -0
- package/bizar-dash/dist/assets/main-Dl8yY5_H.js +16 -0
- package/bizar-dash/dist/assets/main-Dl8yY5_H.js.map +1 -0
- package/bizar-dash/dist/assets/{main-DX_Jh8Wc.css → main-ZAfGKENE.css} +1 -1
- package/bizar-dash/dist/assets/markdown-DIquRulQ.js +29 -0
- package/bizar-dash/dist/assets/markdown-DIquRulQ.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-C2gysFOZ.js +2 -0
- package/bizar-dash/dist/assets/mobile-C2gysFOZ.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-DHXXbn1A.js +1 -0
- package/bizar-dash/dist/assets/{mobile-Chvf9u_B.js.map → mobile-DHXXbn1A.js.map} +1 -1
- package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js +40 -0
- package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js.map +1 -0
- package/bizar-dash/dist/index.html +6 -3
- package/bizar-dash/dist/mobile.html +5 -2
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/skills/eval/SKILL.md +237 -0
- package/bizar-dash/src/server/api.mjs +28 -0
- package/bizar-dash/src/server/auth.mjs +155 -1
- package/bizar-dash/src/server/eval-store.mjs +226 -0
- package/bizar-dash/src/server/eval.mjs +347 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +109 -0
- package/bizar-dash/src/server/memory-store.mjs +121 -0
- package/bizar-dash/src/server/ocr.mjs +55 -0
- package/bizar-dash/src/server/otel.mjs +133 -0
- package/bizar-dash/src/server/plugins/registry.mjs +363 -0
- package/bizar-dash/src/server/plugins/sandbox.mjs +655 -0
- package/bizar-dash/src/server/plugins/store.mjs +659 -0
- package/bizar-dash/src/server/routes/chat.mjs +246 -170
- package/bizar-dash/src/server/routes/clipboard.mjs +173 -0
- package/bizar-dash/src/server/routes/eval.mjs +147 -0
- package/bizar-dash/src/server/routes/memory.mjs +46 -0
- package/bizar-dash/src/server/routes/ocr.mjs +182 -0
- package/bizar-dash/src/server/routes/opencode-sessions.mjs +82 -48
- package/bizar-dash/src/server/routes/plugins.mjs +220 -0
- package/bizar-dash/src/server/routes/users.mjs +84 -0
- package/bizar-dash/src/server/routes/voice.mjs +131 -0
- package/bizar-dash/src/server/routes/workspaces.mjs +204 -0
- package/bizar-dash/src/server/server.mjs +40 -0
- package/bizar-dash/src/server/voice-store.mjs +202 -0
- package/bizar-dash/src/server/voice-transcribe.mjs +72 -0
- package/bizar-dash/src/server/workspaces.mjs +626 -0
- package/bizar-dash/src/web/components/InviteDialog.tsx +205 -0
- package/bizar-dash/src/web/components/ScreenshotCapture.tsx +138 -0
- package/bizar-dash/src/web/components/ScreenshotOCR.tsx +42 -0
- package/bizar-dash/src/web/components/SettingsSearch.tsx +204 -89
- package/bizar-dash/src/web/components/VoiceNotesPanel.tsx +182 -0
- package/bizar-dash/src/web/components/VoiceRecorder.tsx +104 -0
- package/bizar-dash/src/web/components/WorkspaceSelector.tsx +158 -0
- package/bizar-dash/src/web/lib/search.ts +115 -0
- package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +10 -35
- package/bizar-dash/src/web/mobile/views/QrCodePanel.tsx +69 -0
- package/bizar-dash/src/web/styles/memory.css +166 -1
- package/bizar-dash/src/web/styles/settings.css +80 -0
- package/bizar-dash/src/web/views/Memory.tsx +22 -2
- package/bizar-dash/src/web/views/Settings.tsx +99 -0
- package/bizar-dash/src/web/views/Workspace.tsx +294 -0
- package/bizar-dash/src/web/views/memory/FromScreenshotPanel.tsx +23 -0
- package/bizar-dash/src/web/views/memory/MemoryGraphLegend.tsx +29 -0
- package/bizar-dash/src/web/views/memory/MemoryGraphPanel.tsx +192 -0
- package/bizar-dash/src/web/views/memory/MemoryGraphView.tsx +336 -0
- package/bizar-dash/src/web/views/memory/VaultFromClipboardPanel.tsx +101 -0
- package/bizar-dash/src/web/views/settings/WorkspacesSection.tsx +189 -0
- package/bizar-dash/tests/bundle-analysis.test.mjs +73 -0
- package/bizar-dash/tests/clipboard.test.mjs +147 -0
- package/bizar-dash/tests/components/screenshot-ocr.test.tsx +75 -0
- package/bizar-dash/tests/components/settings-search.test.tsx +180 -0
- package/bizar-dash/tests/components/workspace-selector.test.tsx +73 -0
- package/bizar-dash/tests/deploy-templates.test.mjs +100 -0
- package/bizar-dash/tests/docker-build.test.mjs +96 -0
- package/bizar-dash/tests/eval/fixtures.test.mjs +141 -0
- package/bizar-dash/tests/eval/report.test.mjs +284 -0
- package/bizar-dash/tests/eval/runner.test.mjs +471 -0
- package/bizar-dash/tests/lib/search-fuzzy.test.ts +149 -0
- package/bizar-dash/tests/memory-graph-view.test.tsx +69 -0
- package/bizar-dash/tests/memory-graph.test.mjs +95 -0
- package/bizar-dash/tests/ocr.test.mjs +87 -0
- package/bizar-dash/tests/otel.test.mjs +188 -0
- package/bizar-dash/tests/plugins-registry.test.mjs +387 -0
- package/bizar-dash/tests/plugins-sandbox.test.mjs +374 -0
- package/bizar-dash/tests/plugins-store.test.mjs +455 -0
- package/bizar-dash/tests/users.test.mjs +108 -0
- package/bizar-dash/tests/voice-recorder.test.tsx +95 -0
- package/bizar-dash/tests/voice-store.test.mjs +148 -0
- package/bizar-dash/tests/voice-transcribe.test.mjs +87 -0
- package/bizar-dash/tests/workspaces.test.mjs +527 -0
- package/cli/bin.mjs +72 -2
- package/cli/commands/clip.mjs +146 -0
- package/cli/commands/dash.mjs +6 -0
- package/cli/commands/deploy/cloudflare.mjs +250 -0
- package/cli/commands/deploy/docker.mjs +221 -0
- package/cli/commands/deploy/fly.mjs +161 -0
- package/cli/commands/deploy/vercel.mjs +225 -0
- package/cli/commands/deploy.mjs +240 -0
- package/cli/commands/eval.mjs +378 -0
- package/cli/commands/marketplace.mjs +64 -0
- package/cli/commands/ocr.mjs +165 -0
- package/cli/commands/plugin.mjs +358 -0
- package/cli/commands/voice.mjs +211 -0
- package/cli/commands/workspace.mjs +247 -0
- package/package.json +12 -2
- package/templates/deploy/cloudflare/README.md +32 -0
- package/templates/deploy/cloudflare/functions-index.template.js +15 -0
- package/templates/deploy/cloudflare/wrangler.toml.template +9 -0
- package/templates/deploy/docker/.env.template +16 -0
- package/templates/deploy/docker/README.md +58 -0
- package/templates/deploy/docker/docker-compose.template.yml +23 -0
- package/templates/deploy/fly/README.md +35 -0
- package/templates/deploy/fly/fly.toml.template +28 -0
- package/templates/deploy/vercel/README.md +29 -0
- package/templates/deploy/vercel/api-index.template.js +18 -0
- package/templates/deploy/vercel/vercel.json.template +16 -0
- package/templates/eval-fixtures/README.md +58 -0
- package/templates/eval-fixtures/code-search-basic.json +28 -0
- package/templates/eval-fixtures/latency-bounds.json +16 -0
- package/templates/eval-fixtures/regression-suite.json +79 -0
- package/templates/eval-fixtures/response-format.json +30 -0
- package/templates/eval-fixtures/tool-call-correctness.json +24 -0
- package/templates/plugin-template/README.md +121 -0
- package/templates/plugin-template/index.js +66 -0
- package/templates/plugin-template/plugin.json +42 -0
- package/templates/plugin-template/tests/plugin.test.js +83 -0
- package/bizar-dash/dist/assets/main-DHZmbnxQ.js +0 -361
- package/bizar-dash/dist/assets/main-DHZmbnxQ.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-BK8-ythT.js +0 -351
- package/bizar-dash/dist/assets/mobile-BK8-ythT.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-Chvf9u_B.js +0 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "regression-suite",
|
|
3
|
+
"name": "Full regression suite",
|
|
4
|
+
"description": "Runs all smoke tests to catch regressions",
|
|
5
|
+
"fixtures": [
|
|
6
|
+
{
|
|
7
|
+
"id": "code-search-basic",
|
|
8
|
+
"name": "Basic code search",
|
|
9
|
+
"description": "Verifies the agent can find a function definition",
|
|
10
|
+
"agent": "thor",
|
|
11
|
+
"prompt": "Find the function named 'add' in this repository. Show its signature and parameter names.",
|
|
12
|
+
"expected": {
|
|
13
|
+
"contains": ["function add", "a", "b"],
|
|
14
|
+
"notContains": ["TODO", "FIXME"],
|
|
15
|
+
"regex": ["function\\s+add\\s*\\("],
|
|
16
|
+
"jsonSchema": null,
|
|
17
|
+
"maxTokens": 500,
|
|
18
|
+
"maxLatencyMs": 30000
|
|
19
|
+
},
|
|
20
|
+
"tags": ["code-search", "smoke"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "tool-call-correctness",
|
|
24
|
+
"name": "Tool call correctness",
|
|
25
|
+
"description": "Verifies correct tool usage",
|
|
26
|
+
"agent": "thor",
|
|
27
|
+
"prompt": "Create a new file called /tmp/eval-test.txt with the content 'eval test output'.",
|
|
28
|
+
"expected": {
|
|
29
|
+
"contains": ["write", "/tmp/eval-test.txt"],
|
|
30
|
+
"notContains": ["error", "failed"],
|
|
31
|
+
"regex": [],
|
|
32
|
+
"jsonSchema": null,
|
|
33
|
+
"maxTokens": 500,
|
|
34
|
+
"maxLatencyMs": 30000
|
|
35
|
+
},
|
|
36
|
+
"tags": ["tools", "smoke"]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "response-format",
|
|
40
|
+
"name": "Response format validation",
|
|
41
|
+
"description": "Verifies JSON output with schema",
|
|
42
|
+
"agent": "thor",
|
|
43
|
+
"prompt": "Return JSON: { \"status\": \"ok\", \"version\": \"1.0.0\", \"data\": { \"count\": 42 } }. No markdown.",
|
|
44
|
+
"expected": {
|
|
45
|
+
"contains": [],
|
|
46
|
+
"notContains": [],
|
|
47
|
+
"regex": [],
|
|
48
|
+
"jsonSchema": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["status", "version", "data"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"status": { "type": "string" },
|
|
53
|
+
"version": { "type": "string" },
|
|
54
|
+
"data": { "type": "object", "required": ["count"], "properties": { "count": { "type": "number" } } }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"maxTokens": 300,
|
|
58
|
+
"maxLatencyMs": 20000
|
|
59
|
+
},
|
|
60
|
+
"tags": ["format", "json", "smoke"]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "latency-bounds",
|
|
64
|
+
"name": "Latency bounds",
|
|
65
|
+
"description": "Verifies response within latency budget",
|
|
66
|
+
"agent": "thor",
|
|
67
|
+
"prompt": "Respond with exactly the word 'ok' and nothing else.",
|
|
68
|
+
"expected": {
|
|
69
|
+
"contains": ["ok"],
|
|
70
|
+
"notContains": [],
|
|
71
|
+
"regex": [],
|
|
72
|
+
"jsonSchema": null,
|
|
73
|
+
"maxTokens": 50,
|
|
74
|
+
"maxLatencyMs": 10000
|
|
75
|
+
},
|
|
76
|
+
"tags": ["latency", "performance"]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "response-format",
|
|
3
|
+
"name": "Response format validation",
|
|
4
|
+
"description": "Verifies the agent returns valid JSON with a known schema",
|
|
5
|
+
"agent": "thor",
|
|
6
|
+
"prompt": "Return a JSON object with exactly these fields: { \"status\": \"ok\", \"version\": \"1.0.0\", \"data\": { \"count\": 42 } }. No extra fields. Valid JSON only, no markdown fences.",
|
|
7
|
+
"expected": {
|
|
8
|
+
"contains": [],
|
|
9
|
+
"notContains": [],
|
|
10
|
+
"regex": [],
|
|
11
|
+
"jsonSchema": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"required": ["status", "version", "data"],
|
|
14
|
+
"properties": {
|
|
15
|
+
"status": { "type": "string" },
|
|
16
|
+
"version": { "type": "string" },
|
|
17
|
+
"data": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["count"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"count": { "type": "number" }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"maxTokens": 300,
|
|
27
|
+
"maxLatencyMs": 20000
|
|
28
|
+
},
|
|
29
|
+
"tags": ["format", "json", "smoke"]
|
|
30
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "tool-call-correctness",
|
|
3
|
+
"name": "Tool call correctness",
|
|
4
|
+
"description": "Verifies the agent uses the correct tool for a file system operation",
|
|
5
|
+
"agent": "thor",
|
|
6
|
+
"prompt": "Create a new file called /tmp/eval-test.txt with the content 'eval test output' using the write tool.",
|
|
7
|
+
"expected": {
|
|
8
|
+
"contains": [
|
|
9
|
+
"write",
|
|
10
|
+
"/tmp/eval-test.txt",
|
|
11
|
+
"eval test output"
|
|
12
|
+
],
|
|
13
|
+
"notContains": [
|
|
14
|
+
"error",
|
|
15
|
+
"failed",
|
|
16
|
+
"could not"
|
|
17
|
+
],
|
|
18
|
+
"regex": [],
|
|
19
|
+
"jsonSchema": null,
|
|
20
|
+
"maxTokens": 500,
|
|
21
|
+
"maxLatencyMs": 30000
|
|
22
|
+
},
|
|
23
|
+
"tags": ["tools", "smoke", "file-ops"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Echo Plugin Template
|
|
2
|
+
|
|
3
|
+
The smallest possible Bizar marketplace plugin. Use it as a starting
|
|
4
|
+
point for your own — copy this folder, rename the fields in
|
|
5
|
+
`plugin.json`, replace the body of `index.js`, and ship it as a
|
|
6
|
+
`.tar.gz`.
|
|
7
|
+
|
|
8
|
+
## What you get
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
plugin-template/
|
|
12
|
+
├── plugin.json # manifest (id, name, version, main, exports, permissions)
|
|
13
|
+
├── index.js # the plugin's main entry — exports { init, echo, shout }
|
|
14
|
+
├── README.md # you are here
|
|
15
|
+
└── tests/
|
|
16
|
+
└── plugin.test.js # node:test smoke tests
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Trying it locally
|
|
20
|
+
|
|
21
|
+
You can sanity-check the template without publishing it: install it
|
|
22
|
+
from a local path (or just point the host at this folder directly):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# From inside your dashboard's project:
|
|
26
|
+
bizar plugin install <path-to-plugin-template>
|
|
27
|
+
bizar plugin info echo
|
|
28
|
+
bizar plugin invoke echo echo "hello world"
|
|
29
|
+
# → echo: hello world
|
|
30
|
+
|
|
31
|
+
bizar plugin config echo greeting "yo"
|
|
32
|
+
bizar plugin invoke echo shout "hello world"
|
|
33
|
+
# → SHOUT: HELLO WORLD
|
|
34
|
+
|
|
35
|
+
bizar plugin uninstall echo
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Publishing
|
|
39
|
+
|
|
40
|
+
1. **Pick an id.** It must match `/^[a-z0-9][a-z0-9-]*$/`. Lowercase
|
|
41
|
+
kebab-case is the convention (`vercel-deploy`, `github-pr-watcher`).
|
|
42
|
+
|
|
43
|
+
2. **Edit `plugin.json`** — at minimum:
|
|
44
|
+
- `id`, `name`, `version`, `description`
|
|
45
|
+
- `main` (relative to the tarball root)
|
|
46
|
+
- `exports` (array of method descriptors; drives the dashboard UI)
|
|
47
|
+
- `permissions` (every `api.*` method you'll use, except `console`,
|
|
48
|
+
`api.config.*`, and `api.log` which are always available)
|
|
49
|
+
|
|
50
|
+
3. **Write the plugin code** in `index.js`. Use `module.exports = { ... }`
|
|
51
|
+
and export plain async functions. Every async method is awaited by
|
|
52
|
+
the host; a thrown error becomes `{ ok: false, error, code }` on the
|
|
53
|
+
wire.
|
|
54
|
+
|
|
55
|
+
4. **Package the tarball.** Standard convention:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
tar czf echo-0.1.0.tar.gz plugin.json index.js tests
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The tarball root must contain `plugin.json` (either at the very
|
|
62
|
+
top of the archive or inside a single top-level directory — both
|
|
63
|
+
work).
|
|
64
|
+
|
|
65
|
+
5. **Compute the SHA-256** of the tarball:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
sha256sum echo-0.1.0.tar.gz
|
|
69
|
+
# → 8a3f... (64 hex chars)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
6. **Submit to the registry** by opening a PR against
|
|
73
|
+
[DrB0rk/bizar-plugins](https://github.com/DrB0rk/bizar-plugins).
|
|
74
|
+
Add your entry under `plugins:` in `registry.json`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"id": "echo",
|
|
79
|
+
"name": "Echo",
|
|
80
|
+
"version": "0.1.0",
|
|
81
|
+
"description": "Echo plugin",
|
|
82
|
+
"author": "you",
|
|
83
|
+
"category": "utility",
|
|
84
|
+
"tags": ["example", "template"],
|
|
85
|
+
"tarball": "https://github.com/.../echo-0.1.0.tar.gz",
|
|
86
|
+
"checksum": "sha256:8a3f...",
|
|
87
|
+
"permissions": [],
|
|
88
|
+
"minBizarVersion": "4.9.0"
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The host verifies the checksum before extracting — a tampered
|
|
93
|
+
tarball is rejected outright.
|
|
94
|
+
|
|
95
|
+
7. **Install.** Once merged:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
bizar plugin install echo
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Sandbox permissions
|
|
102
|
+
|
|
103
|
+
| Permission | What it unlocks |
|
|
104
|
+
|--------------|------------------------------------------------------|
|
|
105
|
+
| `net` | `api.http.get(url)`, `api.http.post(url, body)` |
|
|
106
|
+
| `fs:read` | `api.fs.read(path)` — sandboxed to the plugin root |
|
|
107
|
+
| *(none)* | `api.config.*`, `api.log`, `console.*` are free |
|
|
108
|
+
|
|
109
|
+
Anything else (`process`, `child_process`, raw `fs`, `net`) is
|
|
110
|
+
stripped from the plugin's global scope by `node:vm`. A plugin that
|
|
111
|
+
tries `require('node:fs')` will throw `require is not defined`.
|
|
112
|
+
|
|
113
|
+
## Versioning
|
|
114
|
+
|
|
115
|
+
Bizar plugins use [semver](https://semver.org/). Bump the major
|
|
116
|
+
version for any breaking change to `plugin.json`'s `exports[]`; minor
|
|
117
|
+
for new methods; patch for bugfixes.
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT, same as the rest of BizarHarness.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Echo plugin — the minimal marketplace plugin.
|
|
3
|
+
*
|
|
4
|
+
* Plugins export their public methods via `module.exports`. Each
|
|
5
|
+
* method receives `(args..., { config, api, plugin })`. The host
|
|
6
|
+
* (bizar-dash's plugin store) handles sandboxing, timeouts, and
|
|
7
|
+
* permission enforcement — your code just needs to focus on the
|
|
8
|
+
* domain logic.
|
|
9
|
+
*
|
|
10
|
+
* What you CAN do from inside a plugin:
|
|
11
|
+
* - console.log / console.error → routed through the dashboard's logger
|
|
12
|
+
* - api.http.get(url) → only if you declare "net" permission
|
|
13
|
+
* - api.fs.read(path) → only if you declare "fs:read" permission
|
|
14
|
+
* - api.config.get(key) / set(key, value) → in-memory + persisted to disk
|
|
15
|
+
* - api.log(level, msg) → structured logger with your plugin id
|
|
16
|
+
*
|
|
17
|
+
* What you CANNOT do (the sandbox strips these globals):
|
|
18
|
+
* - require('node:fs') directly → use api.fs.read instead
|
|
19
|
+
* - require('node:child_process') → not allowed in v1
|
|
20
|
+
* - process.exit / process.kill → use api.log instead
|
|
21
|
+
* - require('node:net') → use api.http instead
|
|
22
|
+
*/
|
|
23
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The plugin's `init` hook. Called once after install and on every
|
|
27
|
+
* dashboard start (the host caches the loaded plugin). Use it to
|
|
28
|
+
* warm caches, validate config, or subscribe to events.
|
|
29
|
+
*
|
|
30
|
+
* Returning a Promise is fine — the host awaits it.
|
|
31
|
+
*/
|
|
32
|
+
async function init() {
|
|
33
|
+
// Pull a config value the user may have set.
|
|
34
|
+
const greeting = api.config.get('greeting') || 'echo';
|
|
35
|
+
api.log('info', `${plugin.id} v${plugin.version} initialized (greeting="${greeting}")`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The simplest possible plugin method: return the input verbatim,
|
|
40
|
+
* prefixed with the configured greeting.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} message
|
|
43
|
+
*/
|
|
44
|
+
async function echo(message) {
|
|
45
|
+
if (typeof message !== 'string') {
|
|
46
|
+
throw new Error(`echo: expected string, got ${typeof message}`);
|
|
47
|
+
}
|
|
48
|
+
const greeting = api.config.get('greeting') || 'echo';
|
|
49
|
+
return `${greeting}: ${message}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Upper-cased variant — demonstrates that async / await / Promises
|
|
54
|
+
* all work transparently inside the sandbox.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} message
|
|
57
|
+
*/
|
|
58
|
+
async function shout(message) {
|
|
59
|
+
return `SHOUT: ${String(message).toUpperCase()}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
init,
|
|
64
|
+
echo,
|
|
65
|
+
shout,
|
|
66
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "echo",
|
|
3
|
+
"name": "Echo",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "A minimal example plugin — echoes whatever you send it. Use this as a starting template for your own marketplace plugin.",
|
|
6
|
+
"author": "DrB0rk",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"exports": [
|
|
9
|
+
{
|
|
10
|
+
"name": "echo",
|
|
11
|
+
"args": [
|
|
12
|
+
{ "name": "message", "type": "string" }
|
|
13
|
+
],
|
|
14
|
+
"returns": "string",
|
|
15
|
+
"description": "Return the input message prefixed with 'echo:'."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "shout",
|
|
19
|
+
"args": [
|
|
20
|
+
{ "name": "message", "type": "string" }
|
|
21
|
+
],
|
|
22
|
+
"returns": "string",
|
|
23
|
+
"description": "Return the input message upper-cased and prefixed with 'SHOUT:'."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "init",
|
|
27
|
+
"args": [],
|
|
28
|
+
"returns": "void",
|
|
29
|
+
"description": "Optional setup hook — called once after install / dashboard start."
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"permissions": [],
|
|
33
|
+
"configSchema": {
|
|
34
|
+
"greeting": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"required": false,
|
|
37
|
+
"default": "echo"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/DrB0rk/bizar-plugins",
|
|
41
|
+
"minBizarVersion": "4.9.0"
|
|
42
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tests/plugin.test.js
|
|
3
|
+
*
|
|
4
|
+
* Smoke tests for the Echo plugin template.
|
|
5
|
+
*
|
|
6
|
+
* These run with `node --test tests/plugin.test.js` from the
|
|
7
|
+
* template folder. They exercise the plugin by loading it directly
|
|
8
|
+
* through the host's `sandbox.mjs` (so the same code path that runs
|
|
9
|
+
* in production is exercised here), not by re-implementing the
|
|
10
|
+
* loading logic.
|
|
11
|
+
*
|
|
12
|
+
* If you copy this template for your own plugin, replace the
|
|
13
|
+
* expectations with real ones for your plugin's behaviour.
|
|
14
|
+
*/
|
|
15
|
+
import { test } from 'node:test';
|
|
16
|
+
import assert from 'node:assert/strict';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { dirname, resolve } from 'node:path';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const TEMPLATE_ROOT = resolve(HERE, '..');
|
|
23
|
+
|
|
24
|
+
test('plugin.json is valid JSON with required fields', () => {
|
|
25
|
+
const raw = readFileSync(resolve(TEMPLATE_ROOT, 'plugin.json'), 'utf8');
|
|
26
|
+
const m = JSON.parse(raw);
|
|
27
|
+
assert.equal(typeof m.id, 'string');
|
|
28
|
+
assert.ok(m.id, 'id must be non-empty');
|
|
29
|
+
assert.equal(typeof m.version, 'string');
|
|
30
|
+
assert.ok(m.version, 'version must be non-empty');
|
|
31
|
+
assert.equal(typeof m.main, 'string');
|
|
32
|
+
assert.ok(m.main, 'main must be non-empty');
|
|
33
|
+
assert.ok(Array.isArray(m.exports), 'exports must be an array');
|
|
34
|
+
assert.ok(Array.isArray(m.permissions), 'permissions must be an array');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('index.js exists and is non-empty', () => {
|
|
38
|
+
const src = readFileSync(resolve(TEMPLATE_ROOT, 'index.js'), 'utf8');
|
|
39
|
+
assert.ok(src.length > 100, 'index.js should be a real file');
|
|
40
|
+
assert.match(src, /module\.exports\s*=/, 'must use module.exports');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('plugin exports echo, shout, and init', async () => {
|
|
44
|
+
// We exercise the actual sandbox so this test fails if the plugin
|
|
45
|
+
// ever drifts from the host's API surface.
|
|
46
|
+
const sandboxUrl = new URL('../../../bizar-dash/src/server/plugins/sandbox.mjs',
|
|
47
|
+
import.meta.url);
|
|
48
|
+
const { loadPlugin, safeInvoke } = await import(sandboxUrl);
|
|
49
|
+
const loaded = await loadPlugin({
|
|
50
|
+
mainFile: resolve(TEMPLATE_ROOT, 'index.js'),
|
|
51
|
+
config: { greeting: 'test' },
|
|
52
|
+
permissions: [],
|
|
53
|
+
pluginId: 'echo',
|
|
54
|
+
pluginRoot: TEMPLATE_ROOT,
|
|
55
|
+
});
|
|
56
|
+
assert.equal(typeof loaded.exports.echo, 'function');
|
|
57
|
+
assert.equal(typeof loaded.exports.shout, 'function');
|
|
58
|
+
assert.equal(typeof loaded.exports.init, 'function');
|
|
59
|
+
|
|
60
|
+
const r1 = await safeInvoke(loaded, 'echo', ['hello']);
|
|
61
|
+
assert.equal(r1.ok, true);
|
|
62
|
+
assert.equal(r1.result, 'test: hello');
|
|
63
|
+
|
|
64
|
+
const r2 = await safeInvoke(loaded, 'shout', ['hi']);
|
|
65
|
+
assert.equal(r2.ok, true);
|
|
66
|
+
assert.equal(r2.result, 'SHOUT: HI');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('echo validates its argument', async () => {
|
|
70
|
+
const sandboxUrl = new URL('../../../bizar-dash/src/server/plugins/sandbox.mjs',
|
|
71
|
+
import.meta.url);
|
|
72
|
+
const { loadPlugin, safeInvoke } = await import(sandboxUrl);
|
|
73
|
+
const loaded = await loadPlugin({
|
|
74
|
+
mainFile: resolve(TEMPLATE_ROOT, 'index.js'),
|
|
75
|
+
config: {},
|
|
76
|
+
permissions: [],
|
|
77
|
+
pluginId: 'echo',
|
|
78
|
+
pluginRoot: TEMPLATE_ROOT,
|
|
79
|
+
});
|
|
80
|
+
const r = await safeInvoke(loaded, 'echo', [42]);
|
|
81
|
+
assert.equal(r.ok, false);
|
|
82
|
+
assert.match(r.error, /expected string/);
|
|
83
|
+
});
|