@bendyline/gezel-service 0.1.0 → 1.0.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/dist/NOTICE.md +13 -5
- package/dist/bin/gezeld.js +56269 -50686
- package/dist/handboek-content/README.md +24 -2
- package/dist/handboek-content/conceptual/connected-apps.md +2 -0
- package/dist/handboek-content/technical/building-connected-apps-with-gezel-app-sdk.md +144 -0
- package/dist/handboek-content/technical/cli-reference.md +11 -0
- package/dist/handboek-content/technical/how-we-test-models.md +57 -9
- package/dist/handboek-content/technical/model-scorecard.md +27 -9
- package/dist/handboek-content/technical/npm-packages.md +67 -0
- package/dist/handboek-content/technical/security-model.md +18 -1
- package/dist/handboek-content/technical/where-files-live.md +3 -0
- package/dist/handboek-content/technical/writing-scripts-with-gezel-sdk.md +106 -0
- package/dist/handboek-content/whats-new/1.26224.md +82 -0
- package/dist/handboek-content/whats-new/whats-new-index.md +20 -0
- package/dist/handboek.d.ts +53 -26
- package/dist/handboek.js +109 -27
- package/dist/index-store/static-index-worker.js +518 -98
- package/dist/index.d.ts +377 -22
- package/dist/index.js +56204 -50670
- package/dist/ui/assets/{TerminalCodeEditor-CB-AXtKI.js → TerminalCodeEditor-BpkIEKrW.js} +2 -2
- package/dist/ui/assets/{_.contribution-C6KlZ1yR.js → _.contribution-DESswd3T.js} +1 -1
- package/dist/ui/assets/{chunk-6N2J7C2B-DIF7DJtk.js → chunk-6N2J7C2B-Du7clJkV.js} +1 -1
- package/dist/ui/assets/chunk-ILCJ3WFD-fHFoZ9wm.js +4 -0
- package/dist/ui/assets/{chunk-S5PCVMKU-CmFF6ff9.js → chunk-S5PCVMKU-DopU3Q9j.js} +2 -2
- package/dist/ui/assets/chunk-USU6HTKB-B-ElCUQS.js +1 -0
- package/dist/ui/assets/{chunk-WATAOG4Y-perfmDXF.js → chunk-WATAOG4Y-BnCgSncI.js} +1 -1
- package/dist/ui/assets/{cpp.contribution-BBBWN3Dm.js → cpp.contribution-DazoHQwn.js} +1 -1
- package/dist/ui/assets/{csharp.contribution-jBA6TWhG.js → csharp.contribution-B64MdTmq.js} +1 -1
- package/dist/ui/assets/{css.contribution-DQSNU6vC.js → css.contribution-u617lXYH.js} +1 -1
- package/dist/ui/assets/{cssMode-68UP_lrS.js → cssMode-Cf_QqRjJ.js} +1 -1
- package/dist/ui/assets/{csv-Ctux8fO7.js → csv-RAgECoUg.js} +1 -1
- package/dist/ui/assets/dist-DB6xxrlD.js +2 -0
- package/dist/ui/assets/dist-DucbO86I.js +222 -0
- package/dist/ui/assets/{doc-BdMAKGDm.js → doc-B7NXSLxH.js} +33 -23
- package/dist/ui/assets/{dockerfile.contribution-CSpSPKgK.js → dockerfile.contribution-P8T1FxHz.js} +1 -1
- package/dist/ui/assets/docx-IZY6hX8A.js +2 -0
- package/dist/ui/assets/{epub-oLed1tAU.js → epub-00L2M424.js} +1 -1
- package/dist/ui/assets/extract-UTAOG7LV-BZ_L7lLL.js +1 -0
- package/dist/ui/assets/{go.contribution-BjibqVfF.js → go.contribution-BqqmPpYF.js} +1 -1
- package/dist/ui/assets/{handlebars-Dq5dJ5WJ.js → handlebars-B9qcPqjD.js} +1 -1
- package/dist/ui/assets/handlebars.contribution-vZslMuRE.js +2 -0
- package/dist/ui/assets/{html-fQ6X2xaa.js → html-Cgejr9YM.js} +24 -24
- package/dist/ui/assets/{html-Bgyk9kiZ.js → html-Fp2jEZ7j.js} +1 -1
- package/dist/ui/assets/html.contribution-B1GzaEV-.js +2 -0
- package/dist/ui/assets/{htmlMode-D5UKInqK.js → htmlMode-B5cPhGim.js} +1 -1
- package/dist/ui/assets/{index-CPXp6BEZ.js → index-CQSh1NUQ.js} +193 -205
- package/dist/ui/assets/index-Ch0qGtVJ.css +1 -0
- package/dist/ui/assets/infer-v-bCXeT9.js +2 -0
- package/dist/ui/assets/{ini.contribution-BiJ4T5K-.js → ini.contribution-XkZYQphY.js} +1 -1
- package/dist/ui/assets/{java.contribution-B8o1lBqP.js → java.contribution-CUQitE0a.js} +1 -1
- package/dist/ui/assets/{javascript-COWkS1tr.js → javascript-CcBUpTjJ.js} +1 -1
- package/dist/ui/assets/javascript.contribution-D6DhUpPP.js +2 -0
- package/dist/ui/assets/{jsonMode-CWWZvfzr.js → jsonMode-D9SbmDW6.js} +1 -1
- package/dist/ui/assets/{kotlin.contribution-CxSOPQ6P.js → kotlin.contribution-B9AYd3Yp.js} +1 -1
- package/dist/ui/assets/layouts-2A7QNF6Y-D94iKLLQ.js +1 -0
- package/dist/ui/assets/{less.contribution-Cthyflev.js → less.contribution-DtKQK3HF.js} +1 -1
- package/dist/ui/assets/{lua.contribution-894DRgPR.js → lua.contribution-DxFFDcBW.js} +1 -1
- package/dist/ui/assets/{markdown.contribution-d_TBTsNA.js → markdown.contribution-B7aCxle-.js} +1 -1
- package/dist/ui/assets/{monaco-PebRhLpo.js → monaco-BBFILBn6.js} +3 -3
- package/dist/ui/assets/{monaco-base-4S0M5BiY.js → monaco-base-CvsKN-Z-.js} +1 -1
- package/dist/ui/assets/{monaco-setup-7hp0Z9jn.js → monaco-setup-BeHgXlpG.js} +1 -1
- package/dist/ui/assets/{monaco.contribution-CKDvyGVE.js → monaco.contribution-B-c9k_-7.js} +2 -2
- package/dist/ui/assets/{monaco.contribution-BL_zgiu2.js → monaco.contribution-BBrbIBfd.js} +2 -2
- package/dist/ui/assets/{monaco.contribution-D61txvUB.js → monaco.contribution-Baa3Cv83.js} +2 -2
- package/dist/ui/assets/{monaco.contribution-DQjPk7Rx.js → monaco.contribution-DRZnYNPl.js} +2 -2
- package/dist/ui/assets/{pdf-CwJ6ZJmB.js → pdf-66KslaXf.js} +1 -1
- package/dist/ui/assets/{php.contribution-B8kn8QoR.js → php.contribution-vC08ovbS.js} +1 -1
- package/dist/ui/assets/pptx-CsdVR0xn.js +6 -0
- package/dist/ui/assets/{python-CHygH1eM.js → python-CevkXHVb.js} +1 -1
- package/dist/ui/assets/python.contribution-C6TUOYNZ.js +2 -0
- package/dist/ui/assets/{ruby.contribution-BQdPKFM2.js → ruby.contribution-BMZvE4z1.js} +1 -1
- package/dist/ui/assets/{rust.contribution-D7wKqShc.js → rust.contribution-DOWKjtrn.js} +1 -1
- package/dist/ui/assets/{scss.contribution-kSTLMoty.js → scss.contribution-BXLJWEv0.js} +1 -1
- package/dist/ui/assets/{shell.contribution-BQnT3BBJ.js → shell.contribution-gbqcscnj.js} +1 -1
- package/dist/ui/assets/{sql.contribution-C9z55nWb.js → sql.contribution-DO39C_-u.js} +1 -1
- package/dist/ui/assets/{swift.contribution-BZoW5ZZj.js → swift.contribution-D4Vjb2VA.js} +1 -1
- package/dist/ui/assets/{terminal-monaco-setup-DbSXji0d.js → terminal-monaco-setup-Bb4lnoOq.js} +1 -1
- package/dist/ui/assets/{tsMode-jPpKNC6E.js → tsMode-BAHQHAnm.js} +1 -1
- package/dist/ui/assets/{typescript-Djn_8BHF.js → typescript-15FhCrR2.js} +1 -1
- package/dist/ui/assets/typescript.contribution-D30L44Mw.js +2 -0
- package/dist/ui/assets/xlsx-CdLmEC42.js +4 -0
- package/dist/ui/assets/{xml-DmE4Aqa-.js → xml-BA1mrkDo.js} +1 -1
- package/dist/ui/assets/xml.contribution-CN9NFcUA.js +2 -0
- package/dist/ui/assets/{yaml-BjmK3C-c.js → yaml-CIqCv-ro.js} +1 -1
- package/dist/ui/assets/yaml.contribution-BsLB8vrs.js +2 -0
- package/dist/ui/index.html +15 -15
- package/package.json +16 -15
- package/dist/postinstall.d.ts +0 -2
- package/dist/postinstall.js +0 -40
- package/dist/ui/assets/chunk-ILCJ3WFD-uwKSpQd8.js +0 -4
- package/dist/ui/assets/chunk-USU6HTKB-hbaA8KyS.js +0 -1
- package/dist/ui/assets/dist-i9jV8CX8.js +0 -2
- package/dist/ui/assets/dist-jh33rACm.js +0 -195
- package/dist/ui/assets/docx-CNW-xqHT.js +0 -2
- package/dist/ui/assets/extract-UTAOG7LV-WxmEnrrc.js +0 -1
- package/dist/ui/assets/handlebars.contribution-DDSQBNPu.js +0 -2
- package/dist/ui/assets/html.contribution-DrClfj2L.js +0 -2
- package/dist/ui/assets/index-Caz-FRQy.css +0 -1
- package/dist/ui/assets/infer-Cc7ks6NX.js +0 -2
- package/dist/ui/assets/javascript.contribution-DMEzq0y4.js +0 -2
- package/dist/ui/assets/layouts-2A7QNF6Y-DR7-KboS.js +0 -1
- package/dist/ui/assets/pptx-Bmj2v9CT.js +0 -6
- package/dist/ui/assets/python.contribution-CmwCxp-v.js +0 -2
- package/dist/ui/assets/typescript.contribution-D4JmB9tz.js +0 -2
- package/dist/ui/assets/xlsx-BCnO44v8.js +0 -4
- package/dist/ui/assets/xml.contribution-BepqhMeK.js +0 -2
- package/dist/ui/assets/yaml.contribution-BwT8qEVt.js +0 -2
- package/postinstall.mjs +0 -8
|
@@ -16,9 +16,12 @@ gezel-roles/ Role articles (curated leads; generated bodies fill gaps)
|
|
|
16
16
|
craftbooks/ Usually generated; curated overrides welcome
|
|
17
17
|
project-types/ Usually generated; curated overrides welcome
|
|
18
18
|
technical/ Architecture, files on disk, security, CLI
|
|
19
|
+
whats-new/ Release notes — one article per release, newest first
|
|
19
20
|
assets/ Images referenced by articles (incl. assets/poppetje/*.svg)
|
|
20
21
|
```
|
|
21
22
|
|
|
23
|
+
`whats-new/` is written by the `release-note` skill (`.claude/skills/release-note/`), which reads the commit range since the previous tag and drafts the article. Read that skill before hand-writing one — the voice is deliberately different from the rest of the Handboek, and the ordering convention below is easy to get wrong.
|
|
24
|
+
|
|
22
25
|
## Article format
|
|
23
26
|
|
|
24
27
|
Squisq-flavored markdown with YAML frontmatter:
|
|
@@ -43,8 +46,26 @@ Body prose…
|
|
|
43
46
|
(curated ids always win).
|
|
44
47
|
|
|
45
48
|
- `title` — TOC + tab title. Falls back to the first `#` heading.
|
|
46
|
-
- `order` — sort key within the area (generated articles sit at 10).
|
|
47
|
-
|
|
49
|
+
- `order` — sort key within the area, ascending (generated articles sit at 10).
|
|
50
|
+
|
|
51
|
+
`whats-new/` inverts this to get newest-first: a release article's order is
|
|
52
|
+
|
|
53
|
+
the negated calendar line, so `1.26224` carries `order: -26224`, and the
|
|
54
|
+
|
|
55
|
+
section index sits at `-999999`. Nothing else needs touching when a release
|
|
56
|
+
|
|
57
|
+
lands — the next article simply sorts above the last one, and the index
|
|
58
|
+
|
|
59
|
+
picks it up through `::handboek-whats-new-list`.
|
|
60
|
+
|
|
61
|
+
- `summary` — one line for the TOC. In `whats-new/` it is also the whole of
|
|
62
|
+
|
|
63
|
+
the release in the section's own list, so it is required there and capped
|
|
64
|
+
|
|
65
|
+
at 200 characters (enforced by the content lint in
|
|
66
|
+
|
|
67
|
+
`packages/service/src/handboek/engine.test.ts`).
|
|
68
|
+
|
|
48
69
|
- `defaultDuration` — optional seconds-per-block override for the video
|
|
49
70
|
|
|
50
71
|
playback mode's timing.
|
|
@@ -69,6 +90,7 @@ A macro is a leaf directive on its own line. The engine expands it into plain ma
|
|
|
69
90
|
| `::handboek-craftbook-list{role=…}` | Table of craftbooks (optionally the role's defaults). |
|
|
70
91
|
| `::handboek-installed-models` | Models installed on this device with engine and tier. |
|
|
71
92
|
| `::handboek-project-type-composition{id=…}` | What a project type sets up: crew, craftbooks, toolsets, schedules. |
|
|
93
|
+
| `::handboek-whats-new-list{limit=12}` | Every release in `whats-new/`, newest first, each with its one-line summary. Identical in all three modes. |
|
|
72
94
|
|
|
73
95
|
## Conventions
|
|
74
96
|
|
|
@@ -55,3 +55,5 @@ One honest caveat, spelled out next to the switch: Ollama's convention is no pas
|
|
|
55
55
|
## Keeping an eye on things
|
|
56
56
|
|
|
57
57
|
Every chat an app completes through these endpoints is recorded in the **History** tab (look for "App chat" entries), and the tokens they consume count in the **Usage** view — so you can always see who has been using your models, and how much.
|
|
58
|
+
|
|
59
|
+
If you are developing one of these applications, continue with the technical guide [Building connected apps with gezel-app-sdk](../technical/building-connected-apps-with-gezel-app-sdk.md).
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: building-connected-apps-with-gezel-app-sdk
|
|
3
|
+
title: Building connected apps with gezel-app-sdk
|
|
4
|
+
order: 9
|
|
5
|
+
summary: Discover Gezel, ask for user consent, and use local models or product APIs from another app.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Building connected apps with gezel-app-sdk
|
|
9
|
+
|
|
10
|
+
`@bendyline/gezel-app-sdk` is for software that runs *beside* Gezel. It lets a desktop or Node application discover the logged-in user's daemon, trust its loopback TLS certificate, ask the user for a scoped connection, remember the issued token securely, and use OpenAI-shaped model APIs.
|
|
11
|
+
|
|
12
|
+
This differs from `@bendyline/gezel-sdk`, whose scripts run *inside* Gezel's sandbox. A connected app owns its own interface and process. Gezel owns model discovery and downloads, consent, revocation, and the local inference service.
|
|
13
|
+
|
|
14
|
+
The [Connected apps](../conceptual/connected-apps.md) article explains what the user sees. This article covers the developer side.
|
|
15
|
+
|
|
16
|
+
## Install and connect
|
|
17
|
+
|
|
18
|
+
The Node entry requires Node.js 24 or newer:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @bendyline/gezel-app-sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The normal third-party flow is: detect Gezel, request the narrowest scope, let the user approve in the desktop app, and store the resulting token in the operating system's credential store.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { connect, detectGezel } from '@bendyline/gezel-app-sdk';
|
|
28
|
+
|
|
29
|
+
// Demo only: this lasts for one process. Replace it with Keychain,
|
|
30
|
+
// Credential Manager, libsecret, or your existing secure credential vault.
|
|
31
|
+
const demoTokens = new Map<string, string>();
|
|
32
|
+
const tokenStorage = {
|
|
33
|
+
load: (appId: string) => demoTokens.get(appId) ?? null,
|
|
34
|
+
save: (appId: string, token: string) => demoTokens.set(appId, token),
|
|
35
|
+
delete: (appId: string) => demoTokens.delete(appId),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const status = await detectGezel();
|
|
39
|
+
if (!status.running) {
|
|
40
|
+
throw new Error('Start Gezel, then connect this app again.');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const app = await connect({
|
|
44
|
+
appId: 'acme.notes',
|
|
45
|
+
appName: 'Acme Notes',
|
|
46
|
+
scopes: ['openai'],
|
|
47
|
+
tokenStorage,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const available = await app.models();
|
|
51
|
+
const model = available.data[0];
|
|
52
|
+
if (!model) throw new Error('No Gezel models are available.');
|
|
53
|
+
|
|
54
|
+
const stream = await app.chat({
|
|
55
|
+
model: model.id,
|
|
56
|
+
messages: [{ role: 'user', content: 'Give this note a short title.' }],
|
|
57
|
+
stream: true,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
for await (const chunk of stream) {
|
|
61
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The in-memory map keeps the quickstart self-contained, but a real application must replace it with secure operating-system storage; do not save the token in source code, a project file, or plain-text preferences. `connect()` verifies a stored token still has the requested scopes. If it was revoked or is too narrow, the SDK discards it and starts a fresh visible consent flow.
|
|
66
|
+
|
|
67
|
+
Call `app.models()` rather than hardcoding a model id. The response is the authoritative list available to this connection and can include both raw models and eligible gezels. Selecting a gezel gives the caller that gezel's character and tuned model, while your app keeps ownership of its interface and conversation loop.
|
|
68
|
+
|
|
69
|
+
## Consent and scopes
|
|
70
|
+
|
|
71
|
+
Every connection has a stable `appId`, a user-visible name, and one or more scopes. The user can review and revoke it under **Settings → Connected Apps**.
|
|
72
|
+
|
|
73
|
+
| Scope | Use it for | Approval |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| `openai` | Model listing, chat, embeddings, and ensuring a local model is present | Click approval by default |
|
|
76
|
+
| `product` | The ordinary Gezel product API through `@bendyline/gezel-client` | Approval plus a requester-visible verification code |
|
|
77
|
+
| `remote-inference` | Gezel's paired-device inference surface | Click approval by default |
|
|
78
|
+
|
|
79
|
+
Third-party applications should not request the first-party `cli` scope or call `authorizeLocalOwner()`. Those surfaces are reserved for Gezel's own command line and same-user clients. Ask for `product` only when the application truly needs projects, tasks, gezels, or other product state; add `openai` only if that same application also performs inference.
|
|
80
|
+
|
|
81
|
+
Stateful scopes require `onVerificationCode`. Show the code in the requesting application so the user can type it into Gezel's approval dialog:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { authorizeLocal } from '@bendyline/gezel-app-sdk';
|
|
85
|
+
|
|
86
|
+
const authorized = await authorizeLocal({
|
|
87
|
+
appId: 'acme.editor',
|
|
88
|
+
appName: 'Acme Editor',
|
|
89
|
+
scopes: ['product'],
|
|
90
|
+
tokenStorage,
|
|
91
|
+
onVerificationCode(code) {
|
|
92
|
+
showConnectionCode(code);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The daemon generates the code and never sends it to the Gezel desktop approval surface. This proves that the person approving the grant can also see the application that requested it.
|
|
98
|
+
|
|
99
|
+
## Use the full product API
|
|
100
|
+
|
|
101
|
+
The app SDK's `GezelApp` class deliberately stays focused on the OpenAI-compatible surface. When a connected app also needs projects, tasks, chats, scripts, or other Gezel state, use `authorizeLocal()` to obtain the approved transport and pass it to the typed client:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { authorizeLocal } from '@bendyline/gezel-app-sdk';
|
|
105
|
+
import { GezelClient } from '@bendyline/gezel-client/node';
|
|
106
|
+
|
|
107
|
+
const authorized = await authorizeLocal({
|
|
108
|
+
appId: 'acme.editor',
|
|
109
|
+
appName: 'Acme Editor',
|
|
110
|
+
scopes: ['product'],
|
|
111
|
+
tokenStorage,
|
|
112
|
+
onVerificationCode: showConnectionCode,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const client = new GezelClient(authorized);
|
|
116
|
+
const projects = await client.listProjects();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This pairing is the supported route for a rich integration: the app SDK owns discovery, pinned TLS, consent, and scoped credentials; `@bendyline/gezel-client` owns the typed product API. Avoid hand-written `/api` requests when the client already covers the endpoint.
|
|
120
|
+
|
|
121
|
+
## Model and inference methods
|
|
122
|
+
|
|
123
|
+
| Method | Purpose |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `app.models()` | List the models and gezels exposed to this app |
|
|
126
|
+
| `app.chat()` | OpenAI-compatible chat completion, streaming or complete |
|
|
127
|
+
| `app.embeddings()` | OpenAI-compatible embeddings when the selected provider supports them |
|
|
128
|
+
| `app.ensureModel()` | Make sure a catalog model is installed and warm |
|
|
129
|
+
| `app.streamEnsureEvents()` | Follow model download progress and completion over SSE |
|
|
130
|
+
| `app.revokeMyToken()` | Let the app revoke its own connection |
|
|
131
|
+
|
|
132
|
+
`detectGezel()` is a useful preflight, but it does not authorize anything. `connect()` returns a ready `GezelApp`; `authorize()` returns the generic base URL, token, and trusted `fetch`; `connectLocal()` combines the full Node-native local flow with non-sensitive daemon diagnostics.
|
|
133
|
+
|
|
134
|
+
Ordinary third-party apps should ask the user to start Gezel when discovery reports `daemon_not_running`. The optional start-if-missing path is only for native integrations that deliberately bundle the matching `gezeld` package.
|
|
135
|
+
|
|
136
|
+
## TLS, browsers, and errors
|
|
137
|
+
|
|
138
|
+
The local daemon uses a per-launch self-signed certificate. The Node SDK reads the public certificate from Gezel's runtime directory and constructs a pinned `fetch`, so you should not disable TLS verification globally or replace the transport with an unpinned client.
|
|
139
|
+
|
|
140
|
+
A browser cannot read `~/.gezel/runtime/` and cannot perform this local trust setup itself. Use a desktop helper to complete discovery and consent, then pass the resolved address and scoped token to a renderer that imports `GezelApp` from `@bendyline/gezel-app-sdk/browser`. Pure websites should not attempt to bypass the browser's loopback TLS protections.
|
|
141
|
+
|
|
142
|
+
Catch `GezelSdkError` and branch on its `code`. Common cases include `daemon_not_running`, `user_denied`, `approval_timeout`, `verification_code_handler_required`, `model_not_found`, `embeddings_not_supported`, `missing_scope:<scope>`, and `provider_error`. Treat denial as a normal user choice, and give timeouts and missing-daemon errors an obvious retry path.
|
|
143
|
+
|
|
144
|
+
The daemon publishes the current public OpenAPI document at unauthenticated `GET /v1/openapi.json`. Use it for route inspection or code generation; use the SDK types for ordinary application code.
|
|
@@ -9,6 +9,15 @@ summary: Headless gezel — start the service, run one-shot work, export docs.
|
|
|
9
9
|
|
|
10
10
|
The `gezel` command drives the same service the desktop app uses — handy on servers, in scripts, or when you just live in a terminal.
|
|
11
11
|
|
|
12
|
+
Install the command-line package with Node.js 24 or newer:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @bendyline/gezel-cli
|
|
16
|
+
gezel
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The CLI is one of Gezel's public JavaScript packages. See [Gezel on npm](npm-packages.md) for the complete package map and the SDKs to use when a shell command is not the right integration boundary.
|
|
20
|
+
|
|
12
21
|
## Everyday commands
|
|
13
22
|
|
|
14
23
|
```
|
|
@@ -37,3 +46,5 @@ gezel handboek export --out ./site
|
|
|
37
46
|
renders the Handboek — the same articles you're reading now — as a static website, for publishing or offline reading.
|
|
38
47
|
|
|
39
48
|
Run `gezel --help` (or `--help` on any subcommand) for the full surface.
|
|
49
|
+
|
|
50
|
+
To automate work *inside* a project, continue with [Writing scripts with gezel-sdk](writing-scripts-with-gezel-sdk.md). To let another application use Gezel, see [Building connected apps with gezel-app-sdk](building-connected-apps-with-gezel-app-sdk.md).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: how-we-test-models
|
|
3
3
|
title: "How we test models"
|
|
4
|
-
order:
|
|
4
|
+
order: 10
|
|
5
5
|
summary: What the model scores mean, how they are measured, and what they don't tell you.
|
|
6
6
|
---
|
|
7
7
|
# How we test models
|
|
@@ -36,13 +36,49 @@ fixing a bug from a description of the symptoms, following a checklist and
|
|
|
36
36
|
stopping when something looks wrong, turning several documents into one
|
|
37
37
|
reconciled summary.
|
|
38
38
|
|
|
39
|
+
### Core (11 tests)
|
|
40
|
+
|
|
41
|
+
| Test | What the task entails |
|
|
42
|
+
|---|---|
|
|
43
|
+
| Tic-tac-toe (`tictactoe`) | Create a new project and build a working two-player tic-tac-toe game in a single HTML file, including a clear win state. |
|
|
44
|
+
| Pet shop (`petshop`) | Build a single-page pet-shop website, generate a custom logo with the image tool, and make sure the page uses that logo. |
|
|
45
|
+
| Tank combat (`tankcombat`) | Build a playable top-down tank game in one HTML file, with keyboard controls, shooting, an enemy tank, and a visible score. |
|
|
46
|
+
| Schema migration (`schema-migration`) | Refactor a small multi-file TypeScript app from one name field to first and last names, preserve every record, add tests and notes, and pass the type check. |
|
|
47
|
+
| Tests as the specification (`failing-tests-spec`) | Work out an order-lifecycle state machine from its tests alone, implement it, and make every test pass without changing the tests. |
|
|
48
|
+
| Debug from symptoms (`symptom-debug`) | Diagnose an undocumented pagination error from the failing output, then fix the implementation without changing the acceptance check. |
|
|
49
|
+
| Data wrangling (`data-wrangle`) | Clean three messy CSV exports, standardise dates and email addresses, remove duplicates, sort the records, and produce the exact required JSON. |
|
|
50
|
+
| Incident postmortem (`incident-postmortem`) | Read five evidence files and write a structured, blame-free postmortem with accurate facts and citations, without inventing unmeasured impact. |
|
|
51
|
+
| Runbook anomaly (`ops-runbook-anomaly`) | Follow a maintenance checklist step by step, verify and record each action, and stop with a grounded report when a planted backup check fails. |
|
|
52
|
+
| Plan and estimate (`plan-and-estimate`) | Produce an office-relocation plan with valid owners, correctly ordered dependencies, risks, and a checkable definition of done for every task. |
|
|
53
|
+
| Conflict synthesis (`conflict-synthesis`) | Reconcile five documents that disagree about a launch date, budget, and owner; show each conflict and use the authoritative answer consistently. |
|
|
54
|
+
|
|
39
55
|
**The productivity set** is office work — a customer notice written to a hard
|
|
40
56
|
word limit, a meeting turned into an action register, a research brief with
|
|
41
57
|
its sources cited, an A/B test read-out, a spreadsheet model, a slide deck,
|
|
42
58
|
a Word document.
|
|
43
59
|
|
|
60
|
+
### Productivity (13 tests)
|
|
61
|
+
|
|
62
|
+
| Test | What the task entails |
|
|
63
|
+
|---|---|
|
|
64
|
+
| Constrained communications (`constrained-comms`) | Write a 140–220 word customer outage notice containing the required facts and disclosures while avoiding banned or unsupported claims. |
|
|
65
|
+
| Plan the week (`craftbook-week-plan`) | Turn eight calendar events into a five-day plan, flag meetings that need preparation, resolve a Tuesday conflict, and protect focus time. |
|
|
66
|
+
| A/B test read-out (`craftbook-ab-test-readout`) | Calculate the experiment results correctly and apply a pre-set decision rule, including a safety measure that overrides an otherwise positive result. |
|
|
67
|
+
| Annotated bibliography (`craftbook-annotated-bibliography`) | Produce six consistently formatted source entries, each with a summary, an evaluation of the source, and its relevance to the question. |
|
|
68
|
+
| Records intake (`records-intake`) | Combine registrations from emails, phone notes, and an old CSV into one correctly shaped, deduplicated record set with standardised dates. |
|
|
69
|
+
| Plan and estimate (`plan-and-estimate`) | Produce an office-relocation plan with valid owners, correctly ordered dependencies, risks, and a checkable definition of done for every task. |
|
|
70
|
+
| Meeting follow-up (`meeting-followup`) | Reconcile a noisy transcript, stale agenda, and current staff list into a decision brief and an exact action register with owners, dates, dependencies, questions, and risks. |
|
|
71
|
+
| Spreadsheet model (`craftbook-spreadsheet-model`) | Calculate the correct roll-ups from seeded records and turn them into a useful spreadsheet-style model and read-out that highlights the main risk. |
|
|
72
|
+
| Conflict synthesis (`conflict-synthesis`) | Reconcile five documents that disagree about a launch date, budget, and owner; show each conflict and use the authoritative answer consistently. |
|
|
73
|
+
| Theme round-trip (`docblocks-theme-roundtrip`) | Read the theme from a brand document, apply it to a presentation, inspect the result, and report only fonts, colours, page counts, and unresolved styles returned by the document tools. |
|
|
74
|
+
| Research to Word (`craftbook-research-to-document`) | Turn reviewed, source-grounded Markdown into a real editable Word document, preview it, and save the finished `.docx` as an artifact. |
|
|
75
|
+
| PowerPoint deck (`craftbook-powerpoint-deck`) | Acquire and cite source material, create an outline, make every slide match it, and save a real editable `.pptx` presentation. |
|
|
76
|
+
| Wikipedia research brief (`wikipedia-research-brief`) | Use a closed local copy of Wikipedia to select the relevant sources and write a cited 700–1,500 word brief with correct chronology and careful treatment of a disputed claim. |
|
|
77
|
+
|
|
44
78
|
A model gets a score on each set. They measure different things, and it is
|
|
45
|
-
normal for a model to be strong on one and weak on the other.
|
|
79
|
+
normal for a model to be strong on one and weak on the other. Plan and
|
|
80
|
+
estimate and conflict synthesis deliberately appear in both sets because
|
|
81
|
+
they test capabilities that matter to each.
|
|
46
82
|
|
|
47
83
|
## What "passed" means
|
|
48
84
|
|
|
@@ -75,8 +111,12 @@ a coin toss that happened to land well.
|
|
|
75
111
|
Sometimes a run fails for reasons that have nothing to do with the model: the
|
|
76
112
|
graphics driver falls over, the machine runs out of memory, someone stops the
|
|
77
113
|
run. Those attempts are recorded and set aside rather than counted as
|
|
78
|
-
failures.
|
|
79
|
-
|
|
114
|
+
failures.
|
|
115
|
+
|
|
116
|
+
If enough of a model's attempts are lost that its results no longer stand up,
|
|
117
|
+
we leave it out of the table rather than publish a score with a hole in it.
|
|
118
|
+
A missing model means "not measured properly yet", never "measured and found
|
|
119
|
+
wanting".
|
|
80
120
|
|
|
81
121
|
## What we deliberately don't claim
|
|
82
122
|
|
|
@@ -86,11 +126,19 @@ task set. Change any of those and you have a different experiment. When a
|
|
|
86
126
|
model was measured in an earlier round we list it separately and say why —
|
|
87
127
|
we never quietly merge it into a newer table to make the list look fuller.
|
|
88
128
|
|
|
89
|
-
**
|
|
90
|
-
large AI model to rate qualities like clarity
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
129
|
+
**Quality scores are an opinion, not a measurement.** Alongside the pass/fail
|
|
130
|
+
checks we ask a large AI model to rate qualities like clarity, grounding and
|
|
131
|
+
candour, and we publish that as a Quality column. Read it with two caveats.
|
|
132
|
+
|
|
133
|
+
First, it only covers work that got *produced*. A model that gives up early
|
|
134
|
+
is graded on the few pieces it did finish, so a high quality score over a
|
|
135
|
+
small number of pieces can mean "good when it manages it" rather than
|
|
136
|
+
"good". That is why the count always travels with the score — `6.5/10 (18
|
|
137
|
+
pieces)` next to `6.5/10 (26 pieces)` are not the same claim.
|
|
138
|
+
|
|
139
|
+
Second, the rating model changes over time, so quality scores are comparable
|
|
140
|
+
*within* a round and not across rounds. The pass/fail results are the ones
|
|
141
|
+
that track.
|
|
94
142
|
|
|
95
143
|
**A score is not a recommendation for your machine.** Results come from one
|
|
96
144
|
particular computer. A model that scores well on a large desktop may not fit
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: model-scorecard
|
|
3
3
|
title: "Model scorecard"
|
|
4
|
-
order:
|
|
4
|
+
order: 11
|
|
5
5
|
summary: Measured results for every model we have tested, on both task sets.
|
|
6
6
|
---
|
|
7
7
|
# Model scorecard
|
|
@@ -33,14 +33,32 @@ model, a slide deck, a Word document.
|
|
|
33
33
|
## Reading the table
|
|
34
34
|
|
|
35
35
|
**Tasks passed** counts every attempt across every job in the set. A model
|
|
36
|
-
with `24/33 (73%)` finished 24 of 33 attempts correctly.
|
|
37
|
-
you'll see a raw count instead of a
|
|
38
|
-
as a rate.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
with `24/33 (73%)` finished 24 of 33 attempts correctly. If any job in the
|
|
37
|
+
set ran fewer than three times you'll see a raw count instead of a
|
|
38
|
+
percentage — too small a sample to quote as a rate. A model whose results
|
|
39
|
+
were incomplete is left out of the table entirely rather than shown with a
|
|
40
|
+
gap.
|
|
41
|
+
|
|
42
|
+
**Quality** is an AI reviewer's opinion of the finished work, and the count
|
|
43
|
+
beside it is how many pieces that opinion covers. It only grades work that
|
|
44
|
+
was actually produced, so a model that fails often is judged on its
|
|
45
|
+
successes alone — `6.5/10 (18 pieces)` is a weaker claim than `6.5/10 (26
|
|
46
|
+
pieces)`. Treat it as colour next to the pass rate, never as a substitute
|
|
47
|
+
for it.
|
|
48
|
+
|
|
49
|
+
**Reads at / Writes at** are measured speeds on the machine named above:
|
|
50
|
+
how fast the model takes in your documents, and how fast it writes its
|
|
51
|
+
answer. Both matter for how a gezel *feels* — reading speed governs the
|
|
52
|
+
pause before it starts, writing speed governs how fast text appears.
|
|
53
|
+
|
|
54
|
+
**Context** is the working memory the model was given for these runs — how
|
|
55
|
+
much it can hold at once. **Memory used** is the peak RAM the model and its
|
|
56
|
+
engine actually occupied, which is the number to check against your own
|
|
57
|
+
machine.
|
|
58
|
+
|
|
59
|
+
**Earlier rounds** appear as separate tables below each set, with their own
|
|
60
|
+
stamps. They are kept apart rather than merged because a change to gezel or
|
|
61
|
+
to the task set can move a score without any model changing.
|
|
44
62
|
|
|
45
63
|
**Size** is the model's parameter count where we know it. Bigger is often but
|
|
46
64
|
not always better: on office work in particular, some smaller models beat
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: npm-packages
|
|
3
|
+
title: "Gezel on npm: the package map"
|
|
4
|
+
order: 7
|
|
5
|
+
summary: Choose the command line, SDK, daemon, client, or lower-level package for your integration.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Gezel on npm
|
|
9
|
+
|
|
10
|
+
The desktop app is the simplest way to use gezel, but the same system is also available as a family of npm packages. You can install the `gezel` command, write a repeatable script, connect another app to local models, embed the daemon, or reuse the schemas and clients that Gezel itself uses.
|
|
11
|
+
|
|
12
|
+
All eleven packages published from the Gezel repository require Node.js 24 or newer and expose public, semver-versioned contracts. You normally install only the package at the top of your use case; npm brings in its required Gezel dependencies for you.
|
|
13
|
+
|
|
14
|
+
## Choose your starting point
|
|
15
|
+
|
|
16
|
+
| You want to… | Start with |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Use gezel from a terminal or server | [`@bendyline/gezel-cli`](cli-reference.md) |
|
|
19
|
+
| Write an automation that runs inside Gezel | [`@bendyline/gezel-sdk`](writing-scripts-with-gezel-sdk.md) |
|
|
20
|
+
| Let a desktop or Node app use the user's Gezel models | [`@bendyline/gezel-app-sdk`](building-connected-apps-with-gezel-app-sdk.md) |
|
|
21
|
+
| Call the full daemon API from TypeScript | `@bendyline/gezel-client` |
|
|
22
|
+
| Run or embed a daemon yourself | `@bendyline/gezel-service` together with `@bendyline/gezel-client` |
|
|
23
|
+
| Give an MCP client Gezel's tools | `@bendyline/gezel-mcp` |
|
|
24
|
+
| Share Gezel's schemas or parse its files | `@bendyline/gezel` |
|
|
25
|
+
|
|
26
|
+
For example, the command-line install is:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g @bendyline/gezel-cli
|
|
30
|
+
gezel
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
A library belongs in the application that uses it:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @bendyline/gezel-app-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Every published package
|
|
40
|
+
|
|
41
|
+
| Package | What it does | Who normally installs it directly |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `@bendyline/gezel` | Core TypeScript types, Zod schemas, path helpers, the `gezel.md` parser, native-platform helpers, and reusable gate checks. It is the shared wire-contract source of truth. | Library authors who need Gezel's data contracts or on-disk formats. |
|
|
44
|
+
| `@bendyline/gezel-client` | Typed HTTP and event-stream client for the complete `gezeld` product API: projects, gezels, chats, tasks, scripts, models, memories, usage, and more. Its `/node` entry also contains local daemon discovery helpers. | Integrations that already have an authorized connection and need more than the OpenAI-compatible app surface. |
|
|
45
|
+
| `@bendyline/gezel-sdk` | The small, typed API available to TypeScript scripts inside Gezel's sandbox: `defineScript`, `gezel.fs`, `gezel.task`, `gezel.llm`, and the other capability-gated namespaces. | Script and craftbook authors. See [Writing scripts with gezel-sdk](writing-scripts-with-gezel-sdk.md). |
|
|
46
|
+
| `@bendyline/gezel-app-sdk` | Local discovery, pinned TLS, user consent, scoped token storage, model setup, chat, embeddings, and model listing for third-party applications. | Desktop and Node application developers. See [Building connected apps with gezel-app-sdk](building-connected-apps-with-gezel-app-sdk.md). |
|
|
47
|
+
| `@bendyline/gezel-plugin-sdk` | The historical plugin helper surface. It remains supported for compatibility, but new extensions should use `@bendyline/gezel-sdk`. | Maintainers of existing Gezel plugins. |
|
|
48
|
+
| `@bendyline/gezel-catalog` | Loads model definitions, toolsets, connector types, project types, gezel roles, and craftbooks from the separately released Gilde content. | Catalog tooling, tests, and embedders that need to resolve catalog items outside the daemon. |
|
|
49
|
+
| `@bendyline/gezel-connectors-spectral` | An isolated subprocess host for compatible Prismatic components. Keeping it out of the daemon process isolates its SDK and vendored connectors. | Usually nobody directly; `@bendyline/gezel-service` resolves and spawns it. |
|
|
50
|
+
| `@bendyline/gezel-script-stdlib` | The trusted, read-only standard library of gate scripts that ships with Gezel. Its plain TypeScript sources run in place under the `standard` script scope. | Usually the daemon; craftbook authors may inspect it for reusable standard checks. |
|
|
51
|
+
| `@bendyline/gezel-mcp` | The stdio Model Context Protocol server that exposes workspace, memory, artifact, document, task, team, execution, history, and media tools. It calls back into a running daemon. | MCP hosts and custom agent harnesses that need Gezel's tool surface. |
|
|
52
|
+
| `@bendyline/gezel-service` | `gezeld`, the local daemon. It owns state, provider routing, chat sessions, tools, tasks, engines, and the HTTP API, and includes the browser UI and Handboek. | Headless operators and applications deliberately embedding or managing a Gezel daemon. |
|
|
53
|
+
| `@bendyline/gezel-cli` | The `gezel` terminal interface, one-shot runner, service manager, model and engine manager, media commands, and Handboek exporter. It does not expose a supported JavaScript API. | People using [the gezel command line](cli-reference.md). |
|
|
54
|
+
|
|
55
|
+
## Packages that work together
|
|
56
|
+
|
|
57
|
+
`@bendyline/gezel-app-sdk` and `@bendyline/gezel-client` solve different halves of a connected application. The app SDK discovers the logged-in user's daemon, pins its loopback certificate, asks for consent, and returns a scoped connection. Pass that connection to `GezelClient` when the application also needs the full product API.
|
|
58
|
+
|
|
59
|
+
`@bendyline/gezel-service` is the runtime, while `@bendyline/gezel-client` is its supported control surface. Prefer the client over hand-written `/api` calls, and do not assume the service runs inside your process: the desktop app, CLI, and remote deployments all use the same HTTP boundary.
|
|
60
|
+
|
|
61
|
+
`@bendyline/gezel-sdk` is different from both. Its code runs *inside* Gezel's script sandbox and receives only the capabilities declared by the script. It is for automations attached to projects and craftbooks, not for connecting a separate application.
|
|
62
|
+
|
|
63
|
+
## The companion Gilde package
|
|
64
|
+
|
|
65
|
+
`@bendyline/gilde` contains the catalog data: model entries, toolsets, roles, craftbooks, connector types, and project types. It is released from the separate Gilde repository and consumed at an exact version by `@bendyline/gezel-catalog`. Install it directly only when you are working with the catalog content itself; ordinary Gezel consumers receive it through the catalog package.
|
|
66
|
+
|
|
67
|
+
The Electron app, React UI, VS Code extension, evaluation viewer, and deployment-only ML runtime are private workspaces rather than public npm packages. The desktop app and extension have their own distribution paths, the React UI is bundled into `@bendyline/gezel-service`, and the evaluation and ML workspaces support development and complete application builds.
|
|
@@ -59,7 +59,15 @@ Some tools deliberately run with more authority. After showing the exact
|
|
|
59
59
|
command and receiving your approval, **package scripts and installed package
|
|
60
60
|
binaries** (`run_package_script` and `run_npx`) run as your operating-system
|
|
61
61
|
account. They may start other programs, use the network, and read or change
|
|
62
|
-
files outside the project.
|
|
62
|
+
files outside the project. A persistent approval is tied to the exact command,
|
|
63
|
+
its arguments, and the content hashes of local inputs Gezel can identify — such
|
|
64
|
+
as `package.json`, a referenced script, relative static imports, or an installed
|
|
65
|
+
binary entry. Changing one of those files asks for approval again. Commands can
|
|
66
|
+
still discover files dynamically, load implicit configuration, resolve other
|
|
67
|
+
programs through `PATH`, use outside-project or directory/glob inputs, or fetch
|
|
68
|
+
code from the network, so this binding is not a complete dependency graph and
|
|
69
|
+
does not turn package commands into a sandbox.
|
|
70
|
+
Third-party MCP servers are also not inside the
|
|
63
71
|
standalone-script sandbox; because they are unconfined local or remote code,
|
|
64
72
|
Gezel enables them only under the corresponding Security & Compliance setting.
|
|
65
73
|
|
|
@@ -94,6 +102,15 @@ At your direction, Gezel can download additional scripts and tools from NPM
|
|
|
94
102
|
has experienced compromises. Gezel installs packages with their install-time
|
|
95
103
|
scripts disabled, preventing that common supply-chain execution path.
|
|
96
104
|
|
|
105
|
+
Gezel's ordinary install flow accepts packages from the NPM registry only: a
|
|
106
|
+
package name plus a version, semver range, or dist-tag. It does not accept a
|
|
107
|
+
URL, Git repository, local file or directory, workspace reference, package
|
|
108
|
+
alias, or command-line option in that field. Installs can target only the
|
|
109
|
+
private package directory of an existing project; project identifiers and the
|
|
110
|
+
resolved destination are checked before the package manager starts. Alternate
|
|
111
|
+
package sources would need a separate, explicitly approved feature with their
|
|
112
|
+
own network and path protections.
|
|
113
|
+
|
|
97
114
|
What happens later depends on the tool type. A standalone script uses the
|
|
98
115
|
sandbox described above. A package command runs with your account's authority
|
|
99
116
|
after explicit approval. A third-party MCP server is unconfined and requires
|
|
@@ -24,6 +24,9 @@ Everything gezel knows lives in one folder — the **gezel home** — as plain f
|
|
|
24
24
|
project.json name, working folder, crew settings
|
|
25
25
|
documents/ About + Mission Objectives
|
|
26
26
|
artifacts/ everything the crew produces
|
|
27
|
+
shadow/ machine-made markdown twins of workspace documents,
|
|
28
|
+
pictures, and recordings (rebuilt automatically —
|
|
29
|
+
safe to delete, not a place to put your own files)
|
|
27
30
|
documents/ the shared library
|
|
28
31
|
history.jsonl the audit log
|
|
29
32
|
logs/ service logs (rolling)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: writing-scripts-with-gezel-sdk
|
|
3
|
+
title: Writing scripts with gezel-sdk
|
|
4
|
+
order: 8
|
|
5
|
+
summary: Build typed, capability-limited automations that run inside a Gezel project.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Writing scripts with gezel-sdk
|
|
9
|
+
|
|
10
|
+
A Gezel script is a small TypeScript automation that runs in a sandbox. It can inspect a project, create an artifact, update a task, ask a model a focused question, call an approved tool, or decide whether a craftbook step may advance. The `@bendyline/gezel-sdk` package is the typed bridge between that script and Gezel.
|
|
11
|
+
|
|
12
|
+
Scripts are project-scoped by default and live as readable files under `~/.gezel/projects/{projectId}/scripts/`. They can be run manually, attached to the start or end of a task step, used as a completion gate, or called by another script.
|
|
13
|
+
|
|
14
|
+
## Start in the app
|
|
15
|
+
|
|
16
|
+
Turn on **Settings → About → Advanced → Show advanced features**, then open **Scripts** from the sidebar. Choose a project and select **New script**. You can describe the automation for an AI draft, start from a working template, or begin with the blank skeleton.
|
|
17
|
+
|
|
18
|
+
The built-in editor supplies autocomplete for the exact SDK version used by the running daemon. Saving checks the metadata, TypeScript syntax, and Node-compatible type syntax; **Run** supplies a form for the inputs declared by the script and shows its output, logs, calls, and errors.
|
|
19
|
+
|
|
20
|
+
You do not need to install the SDK into each Gezel project. The daemon places its matching SDK in the sandbox at run time. Install `@bendyline/gezel-sdk` in a separate source repository only when that repository authors reusable scripts and should type-check them itself:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install --save-dev @bendyline/gezel-sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## The shape of a script
|
|
27
|
+
|
|
28
|
+
Every script exports a static `meta` block, reads validated input from `gezel.input`, and stamps one final result with `gezel.output()`. This gate checks that a workspace contains a README before a task step can finish:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import {
|
|
32
|
+
defineScript,
|
|
33
|
+
gezel,
|
|
34
|
+
type GateScriptResult,
|
|
35
|
+
type InferredInput,
|
|
36
|
+
} from '@bendyline/gezel-sdk';
|
|
37
|
+
|
|
38
|
+
export const meta = defineScript({
|
|
39
|
+
name: 'require-readme',
|
|
40
|
+
description: 'Require a README before this step can finish.',
|
|
41
|
+
kind: 'gate',
|
|
42
|
+
inputs: {
|
|
43
|
+
path: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Workspace-relative README path.',
|
|
46
|
+
default: 'README.md',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
requires: ['workspace.read'],
|
|
50
|
+
} as const);
|
|
51
|
+
|
|
52
|
+
const input = gezel.input as InferredInput<typeof meta>;
|
|
53
|
+
const files = await gezel.fs.listAll();
|
|
54
|
+
|
|
55
|
+
const result: GateScriptResult = files.includes(input.path)
|
|
56
|
+
? { decision: 'approve', message: `${input.path} is present.` }
|
|
57
|
+
: {
|
|
58
|
+
decision: 'reject',
|
|
59
|
+
message: `Add ${input.path} with setup and usage instructions, then try again.`,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
gezel.output(result);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The file name is the script's invocation name, so keep it aligned with `meta.name`. Input descriptors drive the manual-run form and `InferredInput`; output descriptors document an action script's result. A gate instead returns `approve` or `reject`. A rejection must include a useful `message` because Gezel gives it directly to the working gezel as the repair instruction.
|
|
66
|
+
|
|
67
|
+
## Declare capabilities before using them
|
|
68
|
+
|
|
69
|
+
The `requires` list is both documentation and an enforced permission boundary. A call fails with `CAPABILITY_DENIED` when the matching capability was not declared, and project or installation policy can still deny a declared capability.
|
|
70
|
+
|
|
71
|
+
| Capability | SDK surface |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `workspace.read`, `workspace.write` | `gezel.fs` for project workspace files |
|
|
74
|
+
| `artifacts.read`, `artifacts.write` | `gezel.artifacts` for generated project outputs |
|
|
75
|
+
| `documents.read`, `documents.write` | `gezel.documents` for the shared document library |
|
|
76
|
+
| `tasks.read`, `tasks.write` | `gezel.task` for task records, steps, and notes |
|
|
77
|
+
| `memory.read`, `memory.write` | `gezel.memory` for project memories |
|
|
78
|
+
| `llm` | `gezel.llm.oneShot()` |
|
|
79
|
+
| `network` | `gezel.mcp.call()` and `gezel.http` |
|
|
80
|
+
| `credential:<name>` plus `network` | `gezel.http.authed()` with a project-approved named credential |
|
|
81
|
+
|
|
82
|
+
`gezel.input`, `gezel.output()`, `gezel.log()`, and `gezel.script.run()` need no capability. A nested script runs under its own metadata and permission set, and nesting is limited to four levels.
|
|
83
|
+
|
|
84
|
+
Ask only for what the script needs. In particular, use `gezel.fs` for workspace I/O rather than importing Node's `fs`: raw filesystem code runs in an isolated scratch directory and cannot see the project workspace. For authenticated HTTP, the service attaches the named credential and scrubs it from the response; the secret value never enters the script.
|
|
85
|
+
|
|
86
|
+
## Actions, gates, and hooks
|
|
87
|
+
|
|
88
|
+
An action script produces ordinary structured output. Declare `outputs` in `meta`, then call `gezel.output()` exactly once with that shape. Actions are useful for reports, task updates, model-assisted transforms, and small integrations.
|
|
89
|
+
|
|
90
|
+
A gate script sets `kind: 'gate'` and returns a `GateScriptResult`. `approve` allows the step to complete. `reject` holds the step and explains what must change. Optional `goto` routing can send work back to an earlier step, while an approved `handoff` can pass a message and parameters to the next step.
|
|
91
|
+
|
|
92
|
+
After a script works manually, attach it from a task's step automation controls. Craftbooks can also ship scripts and connect them to step entry, exit, and gate moments. Gezel preserves a trace for each run: input, stamped output, logs, host calls, duration, trigger, and any error.
|
|
93
|
+
|
|
94
|
+
## Reusable helpers
|
|
95
|
+
|
|
96
|
+
The package has three public entry points:
|
|
97
|
+
|
|
98
|
+
| Import | Purpose |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `@bendyline/gezel-sdk` | `defineScript`, `gezel`, metadata types, inferred input/output types, and gate result types |
|
|
101
|
+
| `@bendyline/gezel-sdk/checks` | Reusable gate-check predicates, `gateResult()`, and `workspaceFromGezel()` for adapting `gezel.fs` to check helpers |
|
|
102
|
+
| `@bendyline/gezel-sdk/stores` | File-backed `logStore`, `rosterStore`, and integer-cents `ledgerStore` helpers for scripts that maintain structured project state |
|
|
103
|
+
|
|
104
|
+
The separate `@bendyline/gezel-script-stdlib` package contains Gezel's trusted standard gate scripts. Use those by their `standard` scope when they already express the check you need; write a project script when the behavior belongs to one project, and a reusable craftbook script when it belongs to a workflow others will install.
|
|
105
|
+
|
|
106
|
+
If the code lives in another application rather than inside Gezel's sandbox, use [gezel-app-sdk](building-connected-apps-with-gezel-app-sdk.md) instead.
|