clankerbend 0.1.0 → 0.1.2
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/DEVELOPERS.md +6 -6
- package/README.md +31 -14
- package/apps/README.md +3 -3
- package/apps/sticky-notes/README.md +2 -2
- package/apps/sticky-notes/{onewhack.manifest.json → clankerbend.manifest.json} +1 -1
- package/apps/sticky-notes/package.json +3 -3
- package/apps/sticky-notes/public/app.js +42 -9
- package/apps/sticky-notes/src/sticky-notes-app.js +17 -21
- package/apps/vim-nav/README.md +22 -22
- package/apps/vim-nav/{onewhack.manifest.json → clankerbend.manifest.json} +4 -4
- package/apps/vim-nav/package.json +3 -3
- package/apps/vim-nav/public/app.js +44 -11
- package/apps/vim-nav/public/index.html +2 -2
- package/apps/vim-nav/server.mjs +2 -2
- package/apps/vim-nav/src/vim-nav-app.js +5 -5
- package/assets/clankerbend-banner.webp +0 -0
- package/assets/clankerbend-demo-thumbnail.webp +0 -0
- package/assets/clankerbend.svg +26 -0
- package/cli.mjs +22 -11
- package/docs/app-lifecycle.md +9 -9
- package/docs/app-manifest.md +4 -4
- package/docs/author-guide.md +5 -5
- package/docs/launcher-profiles.md +45 -10
- package/docs/protocol.md +248 -241
- package/host/README.md +4 -4
- package/host/src/app-registry.js +8 -6
- package/host/src/codex-desktop-cdp-adapter.js +85 -65
- package/host/src/codex-desktop-renderer-bridge.js +839 -270
- package/host/src/index.js +186 -34
- package/launch/accounts.mjs +360 -0
- package/launch/codex-mux-adapter.mjs +175 -0
- package/launch/profiles.mjs +40 -16
- package/launch/runtime-paths.mjs +9 -6
- package/launch/test-accounts.mjs +71 -0
- package/package.json +12 -10
- package/scripts/release-npm.mjs +1 -2
- package/server.mjs +21 -14
- package/assets/onewhack.jpg +0 -0
package/docs/protocol.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ClankerBend Protocol
|
|
2
2
|
|
|
3
3
|
Status: draft
|
|
4
4
|
|
|
5
|
-
Disclaimer:
|
|
5
|
+
Disclaimer: ClankerBend is an independent OneWill project compatible with OpenAI
|
|
6
6
|
Codex Desktop. It is not affiliated with or endorsed by OpenAI.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
ClankerBend Protocol, short for the OneWill ClankerBend protocol, is a
|
|
9
9
|
standalone local protocol for building companion apps around Codex Desktop
|
|
10
|
-
without requiring changes to Codex Desktop itself.
|
|
10
|
+
without requiring changes to Codex Desktop itself. ClankerBend separates a shared
|
|
11
11
|
local host from individual apps: the host owns the risky integration work, and
|
|
12
12
|
apps consume stable transcript, annotation, navigation, and state primitives.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
ClankerBend codifies the hack paths available today:
|
|
15
15
|
|
|
16
16
|
- a local host process bound to loopback
|
|
17
17
|
- a browser side panel loaded from that local host
|
|
@@ -20,8 +20,8 @@ OneWhack codifies the hack paths available today:
|
|
|
20
20
|
- `codex app-server` awareness for thread, turn, item, approval, and rollback
|
|
21
21
|
data
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
adds such an API, it should be able to replace the adapters underneath
|
|
23
|
+
ClankerBend is not a proposal for a privileged built-in Codex plugin API. If Codex later
|
|
24
|
+
adds such an API, it should be able to replace the adapters underneath ClankerBend, but
|
|
25
25
|
the protocol here is designed to work now with external tools.
|
|
26
26
|
|
|
27
27
|
## Normative Language
|
|
@@ -32,7 +32,7 @@ non-normative unless explicitly marked otherwise.
|
|
|
32
32
|
|
|
33
33
|
## Goals
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
ClankerBend should give any local app a small set of reusable primitives:
|
|
36
36
|
|
|
37
37
|
1. **View**: start a local browser UI and place it in Codex Desktop's existing
|
|
38
38
|
side panel.
|
|
@@ -55,32 +55,32 @@ OneWhack should give any local app a small set of reusable primitives:
|
|
|
55
55
|
|
|
56
56
|
## Scope
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
ClankerBend `0.1` is transcript-adjacent. It covers side panels that observe, annotate,
|
|
59
59
|
navigate, and correlate Codex Desktop transcript content. Other Desktop
|
|
60
60
|
surfaces such as terminal panes, file trees, browser previews, command palette
|
|
61
61
|
commands, notifications, and settings are out of scope for this version.
|
|
62
62
|
|
|
63
63
|
## Non-Goals
|
|
64
64
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
65
|
+
- ClankerBend does not require Codex Desktop changes.
|
|
66
|
+
- ClankerBend does not replace `codex app-server`.
|
|
67
|
+
- ClankerBend does not define a security boundary.
|
|
68
|
+
- ClankerBend does not grant host filesystem, shell, network, or approval authority.
|
|
69
|
+
- ClankerBend does not assume exact item-to-DOM mapping is always possible.
|
|
70
70
|
|
|
71
71
|
## Architecture
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
ClankerBend describes a shared local host that can mount multiple apps.
|
|
74
74
|
|
|
75
75
|
```text
|
|
76
|
-
|
|
76
|
+
ClankerBend app panel(s) <-> ClankerBend host <-> Codex Desktop adapter <-> renderer bridge
|
|
77
77
|
\
|
|
78
78
|
-> app-server adapter
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
###
|
|
81
|
+
### ClankerBend Host
|
|
82
82
|
|
|
83
|
-
The
|
|
83
|
+
The ClankerBend host is the trusted local process for a session. It owns the
|
|
84
84
|
loopback HTTP/SSE server, app registry, app state fanout, Desktop CDP adapter,
|
|
85
85
|
renderer bridge injection, app-server adapter, lifecycle cleanup, and policy
|
|
86
86
|
checks. Apps do not talk directly to CDP or app-server unless the host grants a
|
|
@@ -91,9 +91,9 @@ Vim-style transcript navigation, diagnostics, bookmarks, or review tools. Apps
|
|
|
91
91
|
share transcript anchors, selection, app-server correlation, and side-panel
|
|
92
92
|
placement through the host.
|
|
93
93
|
|
|
94
|
-
###
|
|
94
|
+
### ClankerBend App
|
|
95
95
|
|
|
96
|
-
A
|
|
96
|
+
A ClankerBend app is a UI and state contributor mounted by the host. It may provide
|
|
97
97
|
a side-panel route, annotations, commands, app state entries, and action
|
|
98
98
|
handlers. An app is identified by a stable `appId` and described by a manifest.
|
|
99
99
|
|
|
@@ -106,7 +106,7 @@ a future built-in plugin API, or a mock adapter.
|
|
|
106
106
|
The Codex Desktop adapter is the implementation-specific component that launches
|
|
107
107
|
or attaches to Codex Desktop with a loopback CDP port and evaluates the injected
|
|
108
108
|
renderer bridge. This adapter is not the protocol. It is the current way the
|
|
109
|
-
|
|
109
|
+
ClankerBend host realizes the protocol without Codex Desktop changes.
|
|
110
110
|
|
|
111
111
|
### Injected Renderer Bridge
|
|
112
112
|
|
|
@@ -119,12 +119,12 @@ single app.
|
|
|
119
119
|
|
|
120
120
|
Connection to `codex app-server --stdio` or another app-server transport when
|
|
121
121
|
available. It is used for task identity and lifecycle data, not for Desktop UI
|
|
122
|
-
control. A
|
|
122
|
+
control. A ClankerBend host may still run when app-server is unavailable, but it
|
|
123
123
|
must report that state explicitly.
|
|
124
124
|
|
|
125
125
|
## Transport
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
ClankerBend uses ordinary local web primitives:
|
|
128
128
|
|
|
129
129
|
- HTTP JSON endpoints for commands
|
|
130
130
|
- Server-Sent Events for state updates
|
|
@@ -147,7 +147,7 @@ explicitly defined as SSE. JSON endpoints must use `application/json`.
|
|
|
147
147
|
Successful JSON responses use this envelope:
|
|
148
148
|
|
|
149
149
|
```ts
|
|
150
|
-
type
|
|
150
|
+
type ClankerBendResponse<T> = {
|
|
151
151
|
ok: true;
|
|
152
152
|
data: T;
|
|
153
153
|
};
|
|
@@ -156,7 +156,7 @@ type OneWhackResponse<T> = {
|
|
|
156
156
|
Failed JSON responses use this envelope:
|
|
157
157
|
|
|
158
158
|
```ts
|
|
159
|
-
type
|
|
159
|
+
type ClankerBendError = {
|
|
160
160
|
ok: false;
|
|
161
161
|
error: {
|
|
162
162
|
code:
|
|
@@ -199,9 +199,9 @@ app or adapter:
|
|
|
199
199
|
|
|
200
200
|
1. Request bodies must parse as JSON objects. Arrays, strings, numbers, and
|
|
201
201
|
invalid JSON return `400` with code `bad_request`.
|
|
202
|
-
2. Hosts should reject bodies larger than 1 MiB for
|
|
202
|
+
2. Hosts should reject bodies larger than 1 MiB for ClankerBend `0.1` JSON
|
|
203
203
|
endpoints.
|
|
204
|
-
3. `POST /
|
|
204
|
+
3. `POST /clankerbend/apps/:appId/actions` requires an `action` object with
|
|
205
205
|
non-empty string `appId`, `actionId`, and `type` fields.
|
|
206
206
|
4. `action.appId` must match the route `:appId`. A mismatch returns `400` with
|
|
207
207
|
code `bad_request`.
|
|
@@ -215,7 +215,7 @@ additional validation for app-specific payloads.
|
|
|
215
215
|
|
|
216
216
|
### Session Token
|
|
217
217
|
|
|
218
|
-
A production
|
|
218
|
+
A production ClankerBend host must protect JSON and SSE endpoints with an
|
|
219
219
|
unguessable session token. The token is passed as:
|
|
220
220
|
|
|
221
221
|
```text
|
|
@@ -236,43 +236,46 @@ An app must not bypass the host token by calling CDP or app-server directly.
|
|
|
236
236
|
|
|
237
237
|
### Bootstrap
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
ClankerBend `0.1` defines two token bootstrap paths:
|
|
240
240
|
|
|
241
241
|
1. **Hosted panel apps**: the host serves the app entry URL and may append the
|
|
242
|
-
token in the URL fragment as `#
|
|
243
|
-
the
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
token in the URL fragment as `#clankerbend_token=<token>`. For served HTML,
|
|
243
|
+
the host may also inject a token bootstrap such as
|
|
244
|
+
`window.__CLANKERBEND_TOKEN` or `<meta name="clankerbend-token">` so URL
|
|
245
|
+
rewriting does not strand the app without a token. App JavaScript stores the
|
|
246
|
+
token in memory, may keep it in `sessionStorage` for hash-less reloads,
|
|
247
|
+
immediately removes any fragment with `history.replaceState`, and sends the
|
|
248
|
+
token in the `Authorization` header for JSON and SSE requests.
|
|
246
249
|
2. **External clients**: the token is provided out of band by the launcher, such
|
|
247
|
-
as an environment variable, config file, local IPC
|
|
250
|
+
as the `Token:` startup line, an environment variable, config file, local IPC
|
|
251
|
+
channel, or command output.
|
|
248
252
|
|
|
249
253
|
Hosts must not require a token in the query string. Query strings are more
|
|
250
|
-
likely to be logged or copied than URL fragments.
|
|
251
|
-
|
|
252
|
-
support the bearer-token header.
|
|
254
|
+
likely to be logged or copied than URL fragments. Clients must support the
|
|
255
|
+
bearer-token header.
|
|
253
256
|
|
|
254
257
|
## State Model
|
|
255
258
|
|
|
256
259
|
The host maintains one public state object and streams snapshots to the panel.
|
|
257
260
|
|
|
258
261
|
```ts
|
|
259
|
-
type
|
|
260
|
-
protocolName: "
|
|
262
|
+
type ClankerBendPublicState = {
|
|
263
|
+
protocolName: "clankerbend";
|
|
261
264
|
protocolVersion: "0.1";
|
|
262
265
|
sequence: number;
|
|
263
266
|
generatedAt: string;
|
|
264
|
-
capabilities:
|
|
267
|
+
capabilities: ClankerBendCapabilities;
|
|
265
268
|
host: HostStatus;
|
|
266
269
|
desktop: DesktopStatus;
|
|
267
270
|
panel: PanelStatus;
|
|
268
271
|
transcript: TranscriptState;
|
|
269
|
-
selection:
|
|
270
|
-
selectionActions?:
|
|
271
|
-
overlay?:
|
|
272
|
-
composer?:
|
|
273
|
-
apps:
|
|
272
|
+
selection: ClankerBendSelection | null;
|
|
273
|
+
selectionActions?: ClankerBendSelectionAction[];
|
|
274
|
+
overlay?: ClankerBendOverlay | null;
|
|
275
|
+
composer?: ClankerBendComposerState;
|
|
276
|
+
apps: ClankerBendAppState[];
|
|
274
277
|
appServer: AppServerStatus;
|
|
275
|
-
lastAction?:
|
|
278
|
+
lastAction?: ClankerBendActionResult;
|
|
276
279
|
};
|
|
277
280
|
```
|
|
278
281
|
|
|
@@ -312,15 +315,15 @@ type TranscriptScrollState = {
|
|
|
312
315
|
The state object is intentionally denormalized so the panel can render from a
|
|
313
316
|
single event payload.
|
|
314
317
|
|
|
315
|
-
### `GET /
|
|
318
|
+
### `GET /clankerbend/state`
|
|
316
319
|
|
|
317
320
|
Returns the latest public state.
|
|
318
321
|
|
|
319
322
|
```ts
|
|
320
|
-
type GetStateResponse =
|
|
323
|
+
type GetStateResponse = ClankerBendResponse<ClankerBendPublicState>;
|
|
321
324
|
```
|
|
322
325
|
|
|
323
|
-
### `GET /
|
|
326
|
+
### `GET /clankerbend/events`
|
|
324
327
|
|
|
325
328
|
Streams host events as SSE. The host must send a full state snapshot
|
|
326
329
|
immediately after a client connects.
|
|
@@ -328,19 +331,19 @@ immediately after a client connects.
|
|
|
328
331
|
```text
|
|
329
332
|
event: state
|
|
330
333
|
id: 42
|
|
331
|
-
data: {"protocolName":"
|
|
334
|
+
data: {"protocolName":"clankerbend","protocolVersion":"0.1","sequence":42,...}
|
|
332
335
|
```
|
|
333
336
|
|
|
334
337
|
SSE rules:
|
|
335
338
|
|
|
336
|
-
1. `id` is the decimal string form of `
|
|
337
|
-
2. `state` events contain a complete `
|
|
338
|
-
3. `app-state` events may contain one `
|
|
339
|
+
1. `id` is the decimal string form of `ClankerBendPublicState.sequence`.
|
|
340
|
+
2. `state` events contain a complete `ClankerBendPublicState`.
|
|
341
|
+
3. `app-state` events may contain one `ClankerBendAppState`; clients must still be
|
|
339
342
|
able to recover from `state`.
|
|
340
|
-
4. `action` events contain `
|
|
343
|
+
4. `action` events contain `ClankerBendActionResult`.
|
|
341
344
|
5. `heartbeat` events have empty JSON object data and should be sent at least
|
|
342
345
|
every 15 seconds.
|
|
343
|
-
6. `error` events contain `
|
|
346
|
+
6. `error` events contain `ClankerBendError["error"]`.
|
|
344
347
|
7. Hosts should avoid emitting `state` when nothing semantically changed.
|
|
345
348
|
8. Clients may reconnect with `Last-Event-ID`; hosts may replay missed events
|
|
346
349
|
but must send a fresh `state` event even if replay is unavailable.
|
|
@@ -353,7 +356,7 @@ The host exposes negotiated capabilities in every state snapshot so apps can
|
|
|
353
356
|
degrade cleanly.
|
|
354
357
|
|
|
355
358
|
```ts
|
|
356
|
-
type
|
|
359
|
+
type ClankerBendCapabilities = {
|
|
357
360
|
protocolVersion: "0.1";
|
|
358
361
|
host: {
|
|
359
362
|
apps: boolean;
|
|
@@ -409,11 +412,11 @@ transcript operation until a future protocol version adds dynamic arbitration.
|
|
|
409
412
|
|
|
410
413
|
### Forward Compatibility
|
|
411
414
|
|
|
412
|
-
|
|
415
|
+
ClankerBend clients must treat minor-version protocol changes as additive.
|
|
413
416
|
Unknown object fields, unknown capability keys, unknown app state entry fields,
|
|
414
417
|
and unknown action result data must be ignored by default. Apps must gate
|
|
415
|
-
optional behavior on the capabilities they read from `GET /
|
|
416
|
-
or `GET /
|
|
418
|
+
optional behavior on the capabilities they read from `GET /clankerbend/manifest`
|
|
419
|
+
or `GET /clankerbend/state`; they must not infer availability from the host name,
|
|
417
420
|
adapter name, or Codex Desktop version alone.
|
|
418
421
|
|
|
419
422
|
Hosts must keep the `ok` envelope, error envelope, endpoint names, and required
|
|
@@ -442,27 +445,27 @@ lowercase reverse-DNS-style names such as `onewill.vim-nav`.
|
|
|
442
445
|
Apps must read host capabilities before assuming any optional feature exists.
|
|
443
446
|
|
|
444
447
|
```text
|
|
445
|
-
GET /
|
|
446
|
-
GET /
|
|
447
|
-
GET /
|
|
448
|
+
GET /clankerbend/manifest
|
|
449
|
+
GET /clankerbend/state
|
|
450
|
+
GET /clankerbend/events
|
|
448
451
|
```
|
|
449
452
|
|
|
450
|
-
`GET /
|
|
453
|
+
`GET /clankerbend/manifest` returns host identity, protocol version, and mounted
|
|
451
454
|
app manifests.
|
|
452
455
|
|
|
453
456
|
```ts
|
|
454
|
-
type GetManifestResponse =
|
|
457
|
+
type GetManifestResponse = ClankerBendResponse<ClankerBendHostManifest>;
|
|
455
458
|
|
|
456
|
-
type
|
|
457
|
-
|
|
459
|
+
type ClankerBendHostManifest = {
|
|
460
|
+
clankerbendVersion: "0.1";
|
|
458
461
|
hostId: string;
|
|
459
462
|
hostName: string;
|
|
460
|
-
capabilities:
|
|
463
|
+
capabilities: ClankerBendCapabilities;
|
|
461
464
|
security?: {
|
|
462
465
|
auth: "bearer" | "none";
|
|
463
466
|
localDevInsecure?: boolean;
|
|
464
467
|
};
|
|
465
|
-
apps:
|
|
468
|
+
apps: ClankerBendAppManifest[];
|
|
466
469
|
};
|
|
467
470
|
```
|
|
468
471
|
|
|
@@ -474,32 +477,32 @@ render a degraded state rather than attempting direct CDP or app-server access.
|
|
|
474
477
|
The host exposes mounted apps through a registry.
|
|
475
478
|
|
|
476
479
|
```text
|
|
477
|
-
GET /
|
|
478
|
-
GET /
|
|
479
|
-
GET /
|
|
480
|
-
GET /
|
|
481
|
-
POST /
|
|
480
|
+
GET /clankerbend/apps
|
|
481
|
+
GET /clankerbend/apps/:appId/manifest
|
|
482
|
+
GET /clankerbend/apps/:appId/state
|
|
483
|
+
GET /clankerbend/apps/:appId/actions/:actionId
|
|
484
|
+
POST /clankerbend/apps/:appId/actions
|
|
482
485
|
```
|
|
483
486
|
|
|
484
487
|
The registry lets one host serve multiple apps without each app owning a
|
|
485
488
|
separate CDP adapter or injected bridge.
|
|
486
489
|
|
|
487
490
|
```ts
|
|
488
|
-
type GetAppsResponse =
|
|
489
|
-
apps:
|
|
491
|
+
type GetAppsResponse = ClankerBendResponse<{
|
|
492
|
+
apps: ClankerBendAppSummary[];
|
|
490
493
|
}>;
|
|
491
494
|
|
|
492
|
-
type
|
|
495
|
+
type ClankerBendAppSummary = {
|
|
493
496
|
appId: string;
|
|
494
497
|
name: string;
|
|
495
|
-
status:
|
|
498
|
+
status: ClankerBendAppStatus;
|
|
496
499
|
entry?: string;
|
|
497
500
|
};
|
|
498
501
|
|
|
499
|
-
type GetAppManifestResponse =
|
|
500
|
-
type GetAppStateResponse =
|
|
502
|
+
type GetAppManifestResponse = ClankerBendResponse<ClankerBendAppManifest>;
|
|
503
|
+
type GetAppStateResponse = ClankerBendResponse<ClankerBendAppState>;
|
|
501
504
|
|
|
502
|
-
type
|
|
505
|
+
type ClankerBendAppStatus =
|
|
503
506
|
| "mounted"
|
|
504
507
|
| "loading"
|
|
505
508
|
| "ready"
|
|
@@ -507,15 +510,15 @@ type OneWhackAppStatus =
|
|
|
507
510
|
| "disabled"
|
|
508
511
|
| "error";
|
|
509
512
|
|
|
510
|
-
type
|
|
511
|
-
|
|
513
|
+
type ClankerBendAppManifest = {
|
|
514
|
+
clankerbendVersion: "0.1";
|
|
512
515
|
appId: string;
|
|
513
516
|
version: string;
|
|
514
517
|
name: string;
|
|
515
518
|
description?: string;
|
|
516
519
|
entry: string;
|
|
517
|
-
distribution?:
|
|
518
|
-
entrypoint?:
|
|
520
|
+
distribution?: ClankerBendAppDistribution;
|
|
521
|
+
entrypoint?: ClankerBendAppEntrypoint;
|
|
519
522
|
capabilities?: {
|
|
520
523
|
panel?: boolean;
|
|
521
524
|
annotations?: boolean;
|
|
@@ -554,11 +557,11 @@ type OneWhackAppManifest = {
|
|
|
554
557
|
reloadPolicy: "preserve" | "reload";
|
|
555
558
|
preferredWidth?: number;
|
|
556
559
|
};
|
|
557
|
-
rendererBridge?:
|
|
558
|
-
lifecycle?:
|
|
560
|
+
rendererBridge?: ClankerBendRendererBridgeManifest;
|
|
561
|
+
lifecycle?: ClankerBendAppLifecycleManifest;
|
|
559
562
|
};
|
|
560
563
|
|
|
561
|
-
type
|
|
564
|
+
type ClankerBendAppDistribution = {
|
|
562
565
|
kind: "local" | "npm" | "tarball" | "binary";
|
|
563
566
|
source?: string;
|
|
564
567
|
integrity?: string;
|
|
@@ -569,7 +572,7 @@ type OneWhackAppDistribution = {
|
|
|
569
572
|
};
|
|
570
573
|
};
|
|
571
574
|
|
|
572
|
-
type
|
|
575
|
+
type ClankerBendAppEntrypoint = {
|
|
573
576
|
kind: "module" | "binary" | "static";
|
|
574
577
|
module?: string;
|
|
575
578
|
factory?: string;
|
|
@@ -578,7 +581,7 @@ type OneWhackAppEntrypoint = {
|
|
|
578
581
|
publicDir?: string;
|
|
579
582
|
};
|
|
580
583
|
|
|
581
|
-
type
|
|
584
|
+
type ClankerBendRendererBridgeManifest = {
|
|
582
585
|
script: string;
|
|
583
586
|
primary?: boolean;
|
|
584
587
|
provides?: (
|
|
@@ -597,7 +600,7 @@ type OneWhackRendererBridgeManifest = {
|
|
|
597
600
|
};
|
|
598
601
|
};
|
|
599
602
|
|
|
600
|
-
type
|
|
603
|
+
type ClankerBendAppLifecycleManifest = {
|
|
601
604
|
install?: { kind: string };
|
|
602
605
|
start?: { kind: string };
|
|
603
606
|
stop?: { kind: string };
|
|
@@ -613,14 +616,14 @@ approval rules.
|
|
|
613
616
|
|
|
614
617
|
```ts
|
|
615
618
|
type PostAppActionRequest = {
|
|
616
|
-
action:
|
|
619
|
+
action: ClankerBendAction;
|
|
617
620
|
};
|
|
618
621
|
|
|
619
|
-
type PostAppActionResponse =
|
|
620
|
-
|
|
622
|
+
type PostAppActionResponse = ClankerBendResponse<
|
|
623
|
+
ClankerBendActionResult | ClankerBendActionReceipt
|
|
621
624
|
>;
|
|
622
625
|
|
|
623
|
-
type
|
|
626
|
+
type ClankerBendAction = {
|
|
624
627
|
actionId: string;
|
|
625
628
|
appId: string;
|
|
626
629
|
type: string;
|
|
@@ -631,7 +634,7 @@ type OneWhackAction = {
|
|
|
631
634
|
requestedAt: string;
|
|
632
635
|
};
|
|
633
636
|
|
|
634
|
-
type
|
|
637
|
+
type ClankerBendActionReceipt = {
|
|
635
638
|
actionId: string;
|
|
636
639
|
appId: string;
|
|
637
640
|
accepted: true;
|
|
@@ -639,7 +642,7 @@ type OneWhackActionReceipt = {
|
|
|
639
642
|
resultUrl?: string;
|
|
640
643
|
};
|
|
641
644
|
|
|
642
|
-
type
|
|
645
|
+
type ClankerBendActionResult = {
|
|
643
646
|
actionId: string;
|
|
644
647
|
appId: string;
|
|
645
648
|
ok: boolean;
|
|
@@ -663,7 +666,7 @@ Action rules:
|
|
|
663
666
|
7. Unavailable adapters must return `503` with code `adapter_unavailable` or
|
|
664
667
|
`capability_unavailable`.
|
|
665
668
|
8. When `resultUrl` is present, it must point to
|
|
666
|
-
`/
|
|
669
|
+
`/clankerbend/apps/:appId/actions/:actionId`.
|
|
667
670
|
9. App handlers must return an action result with boolean `ok`. Invalid handler
|
|
668
671
|
returns must fail with `500` and code `action_failed`.
|
|
669
672
|
|
|
@@ -676,12 +679,12 @@ this document, but they must follow the same action contract.
|
|
|
676
679
|
Hosts must retain completed action results for at least 60 seconds.
|
|
677
680
|
|
|
678
681
|
```text
|
|
679
|
-
GET /
|
|
682
|
+
GET /clankerbend/apps/:appId/actions/:actionId
|
|
680
683
|
```
|
|
681
684
|
|
|
682
685
|
```ts
|
|
683
|
-
type GetAppActionResponse =
|
|
684
|
-
|
|
686
|
+
type GetAppActionResponse = ClankerBendResponse<
|
|
687
|
+
ClankerBendActionResult | ClankerBendActionReceipt
|
|
685
688
|
>;
|
|
686
689
|
```
|
|
687
690
|
|
|
@@ -689,24 +692,24 @@ This endpoint lets clients recover when they miss an SSE `action` event.
|
|
|
689
692
|
|
|
690
693
|
### App Lifecycle
|
|
691
694
|
|
|
692
|
-
|
|
695
|
+
ClankerBend `0.1` defines manifest-based app registration. A host may install apps
|
|
693
696
|
from local manifests, npm packages, tarballs, or binary-style bundles. Download
|
|
694
697
|
and update transports are implementation details, but every installed app must
|
|
695
|
-
resolve to a validated `
|
|
698
|
+
resolve to a validated `ClankerBendAppManifest`.
|
|
696
699
|
|
|
697
700
|
The public launcher command is intentionally small:
|
|
698
701
|
|
|
699
702
|
```text
|
|
700
|
-
clankerbend
|
|
703
|
+
clankerbend
|
|
701
704
|
```
|
|
702
705
|
|
|
703
706
|
App installation, enablement, update, and removal are product configuration
|
|
704
|
-
concerns owned by the running
|
|
707
|
+
concerns owned by the running ClankerBend experience. The registry model remains
|
|
705
708
|
part of the host protocol, but apps should not depend on a public app-management
|
|
706
709
|
CLI being present.
|
|
707
710
|
|
|
708
711
|
Regardless of loading mechanism, a mounted app must appear in
|
|
709
|
-
`GET /
|
|
712
|
+
`GET /clankerbend/apps`.
|
|
710
713
|
|
|
711
714
|
Lifecycle rules:
|
|
712
715
|
|
|
@@ -728,7 +731,7 @@ Lifecycle rules:
|
|
|
728
731
|
|
|
729
732
|
### Panel Model
|
|
730
733
|
|
|
731
|
-
|
|
734
|
+
ClankerBend `0.1` assumes one Codex Desktop side panel controlled by the host. The
|
|
732
735
|
host may implement that side panel as:
|
|
733
736
|
|
|
734
737
|
- a shared shell that routes between mounted apps, or
|
|
@@ -737,9 +740,9 @@ host may implement that side panel as:
|
|
|
737
740
|
In either model:
|
|
738
741
|
|
|
739
742
|
1. `PanelStatus.activeAppId` identifies the app currently shown when known.
|
|
740
|
-
2. `POST /
|
|
743
|
+
2. `POST /clankerbend/panel/open` opens or focuses the host panel and may switch
|
|
741
744
|
the active panel app by `appId`.
|
|
742
|
-
3. `POST /
|
|
745
|
+
3. `POST /clankerbend/apps/:appId/actions` may switch the active app when the
|
|
743
746
|
action requires app UI.
|
|
744
747
|
4. Opening an already-loaded panel must not reload the page unless the app
|
|
745
748
|
manifest uses `reloadPolicy: "reload"`.
|
|
@@ -749,15 +752,19 @@ In either model:
|
|
|
749
752
|
|
|
750
753
|
### Launch
|
|
751
754
|
|
|
752
|
-
|
|
755
|
+
ClankerBend can launch Codex Desktop with CDP:
|
|
753
756
|
|
|
754
757
|
```sh
|
|
755
758
|
/Applications/Codex.app/Contents/MacOS/Codex \
|
|
756
759
|
--remote-debugging-address=127.0.0.1 \
|
|
757
760
|
--remote-debugging-port=<free> \
|
|
758
|
-
--user-data-dir=<test-or-
|
|
761
|
+
--user-data-dir=<test-or-clankerbend-profile>
|
|
759
762
|
```
|
|
760
763
|
|
|
764
|
+
When account profiles are enabled, the adapter also launches Desktop with the
|
|
765
|
+
selected profile's `CODEX_HOME` and isolated Electron user-data directory.
|
|
766
|
+
ClankerBend `0.1` runs one managed Codex Desktop instance at a time.
|
|
767
|
+
|
|
761
768
|
The host must not patch `/Applications/Codex.app`.
|
|
762
769
|
|
|
763
770
|
### Attach
|
|
@@ -787,14 +794,14 @@ type DesktopStatus = {
|
|
|
787
794
|
The CDP adapter initializes a single renderer runtime at:
|
|
788
795
|
|
|
789
796
|
```ts
|
|
790
|
-
window.
|
|
797
|
+
window.__clankerbendRuntime
|
|
791
798
|
```
|
|
792
799
|
|
|
793
800
|
Apps register into runtime-owned app slots. The runtime is the only privileged
|
|
794
801
|
renderer global; individual apps must not require their own global bridge names.
|
|
795
802
|
|
|
796
803
|
```ts
|
|
797
|
-
type
|
|
804
|
+
type ClankerBendRendererRuntime = {
|
|
798
805
|
protocolVersion: string;
|
|
799
806
|
hostUrl: string;
|
|
800
807
|
apps: Record<string, RendererAppSlot>;
|
|
@@ -874,7 +881,7 @@ type RendererSnapshot = {
|
|
|
874
881
|
anchors: TranscriptAnchor[];
|
|
875
882
|
visibleCount: number;
|
|
876
883
|
annotationCount: number;
|
|
877
|
-
selection:
|
|
884
|
+
selection: ClankerBendSelection | null;
|
|
878
885
|
};
|
|
879
886
|
```
|
|
880
887
|
|
|
@@ -919,11 +926,11 @@ app-server identity.
|
|
|
919
926
|
|
|
920
927
|
### Identity Tiers
|
|
921
928
|
|
|
922
|
-
|
|
929
|
+
ClankerBend uses separate identity tiers because exact app-server-to-DOM mapping is
|
|
923
930
|
not always available.
|
|
924
931
|
|
|
925
932
|
```ts
|
|
926
|
-
type
|
|
933
|
+
type ClankerBendIdentity = {
|
|
927
934
|
anchorId?: string;
|
|
928
935
|
entryId?: string;
|
|
929
936
|
markerId?: string;
|
|
@@ -1054,7 +1061,7 @@ Annotation ownership rules:
|
|
|
1054
1061
|
3. Lower `priority` values render earlier; omitted priority defaults to `100`.
|
|
1055
1062
|
Ties sort by `appId`, then `markerId`, both lexicographically.
|
|
1056
1063
|
4. Marker clicks route to the contributing app through
|
|
1057
|
-
`POST /
|
|
1064
|
+
`POST /clankerbend/apps/:appId/actions`.
|
|
1058
1065
|
5. The renderer bridge must remove stale markers for an app without removing
|
|
1059
1066
|
markers owned by other apps.
|
|
1060
1067
|
6. Marker identity is scoped by `(appId, markerId)`. Two apps may use the same
|
|
@@ -1069,10 +1076,10 @@ Annotation ownership rules:
|
|
|
1069
1076
|
|
|
1070
1077
|
The panel talks only to the host server. It does not need direct CDP access.
|
|
1071
1078
|
|
|
1072
|
-
### `POST /
|
|
1079
|
+
### `POST /clankerbend/panel/open`
|
|
1073
1080
|
|
|
1074
1081
|
Asks the host to open/focus the panel in Codex Desktop. The host calls
|
|
1075
|
-
`window.
|
|
1082
|
+
`window.__clankerbendRuntime.getBridge(activeAppId).openPanel()` or an equivalent
|
|
1076
1083
|
app-id-scoped bridge call via CDP.
|
|
1077
1084
|
|
|
1078
1085
|
```ts
|
|
@@ -1085,10 +1092,10 @@ When `appId` is provided, the host must set `PanelStatus.activeAppId` to that
|
|
|
1085
1092
|
registered app before loading the panel URL. Unknown app ids return `404`.
|
|
1086
1093
|
|
|
1087
1094
|
```ts
|
|
1088
|
-
type OpenPanelResponse =
|
|
1095
|
+
type OpenPanelResponse = ClankerBendResponse<PanelOpenResult>;
|
|
1089
1096
|
```
|
|
1090
1097
|
|
|
1091
|
-
### `POST /
|
|
1098
|
+
### `POST /clankerbend/transcript/scroll`
|
|
1092
1099
|
|
|
1093
1100
|
```json
|
|
1094
1101
|
{
|
|
@@ -1105,10 +1112,10 @@ type ScrollToAnchorRequest = {
|
|
|
1105
1112
|
block?: "start" | "center" | "end" | "nearest";
|
|
1106
1113
|
};
|
|
1107
1114
|
|
|
1108
|
-
type ScrollToAnchorResponse =
|
|
1115
|
+
type ScrollToAnchorResponse = ClankerBendResponse<ScrollResult>;
|
|
1109
1116
|
```
|
|
1110
1117
|
|
|
1111
|
-
### `POST /
|
|
1118
|
+
### `POST /clankerbend/transcript/highlight`
|
|
1112
1119
|
|
|
1113
1120
|
```json
|
|
1114
1121
|
{
|
|
@@ -1123,10 +1130,10 @@ type HighlightAnchorRequest = {
|
|
|
1123
1130
|
durationMs?: number;
|
|
1124
1131
|
};
|
|
1125
1132
|
|
|
1126
|
-
type HighlightAnchorResponse =
|
|
1133
|
+
type HighlightAnchorResponse = ClankerBendResponse<HighlightResult>;
|
|
1127
1134
|
```
|
|
1128
1135
|
|
|
1129
|
-
### `POST /
|
|
1136
|
+
### `POST /clankerbend/selection`
|
|
1130
1137
|
|
|
1131
1138
|
Updates global host selection.
|
|
1132
1139
|
|
|
@@ -1145,11 +1152,11 @@ Updates global host selection.
|
|
|
1145
1152
|
|
|
1146
1153
|
```ts
|
|
1147
1154
|
type PostSelectionRequest = {
|
|
1148
|
-
selection:
|
|
1155
|
+
selection: ClankerBendSelection;
|
|
1149
1156
|
};
|
|
1150
1157
|
|
|
1151
|
-
type PostSelectionResponse =
|
|
1152
|
-
selection:
|
|
1158
|
+
type PostSelectionResponse = ClankerBendResponse<{
|
|
1159
|
+
selection: ClankerBendSelection;
|
|
1153
1160
|
stale?: boolean;
|
|
1154
1161
|
}>;
|
|
1155
1162
|
```
|
|
@@ -1160,14 +1167,14 @@ is older than the current accepted selection. If two selections have the same
|
|
|
1160
1167
|
assign a monotonically increasing `sequence` and `acceptedAt` timestamp.
|
|
1161
1168
|
|
|
1162
1169
|
```ts
|
|
1163
|
-
type
|
|
1170
|
+
type ClankerBendSelection = {
|
|
1164
1171
|
selectionId: string;
|
|
1165
1172
|
sequence?: number;
|
|
1166
1173
|
source: "panel" | "transcript" | "adapter";
|
|
1167
1174
|
appId?: string;
|
|
1168
1175
|
anchorId?: string;
|
|
1169
1176
|
quote?: string;
|
|
1170
|
-
range?:
|
|
1177
|
+
range?: ClankerBendTextRange;
|
|
1171
1178
|
markerId?: string;
|
|
1172
1179
|
entryId?: string;
|
|
1173
1180
|
appServer?: {
|
|
@@ -1188,10 +1195,10 @@ older transcript click after the user selected a newer side-panel entry.
|
|
|
1188
1195
|
|
|
1189
1196
|
Apps must not inspect Codex Desktop DOM to interpret selected text. When the
|
|
1190
1197
|
adapter observes a transcript text selection, it normalizes that selection into
|
|
1191
|
-
a `
|
|
1198
|
+
a `ClankerBendTextRange` and updates host `selection`.
|
|
1192
1199
|
|
|
1193
1200
|
```ts
|
|
1194
|
-
type
|
|
1201
|
+
type ClankerBendTextRange = {
|
|
1195
1202
|
anchorId: string;
|
|
1196
1203
|
text: string;
|
|
1197
1204
|
quote?: string;
|
|
@@ -1208,12 +1215,12 @@ Apps may store them for later source matching, but they must treat `anchorId`
|
|
|
1208
1215
|
plus selected `text` as the only required fields.
|
|
1209
1216
|
|
|
1210
1217
|
```text
|
|
1211
|
-
POST /
|
|
1218
|
+
POST /clankerbend/transcript/highlight-range
|
|
1212
1219
|
```
|
|
1213
1220
|
|
|
1214
1221
|
```ts
|
|
1215
1222
|
type PostHighlightRangeRequest = {
|
|
1216
|
-
range:
|
|
1223
|
+
range: ClankerBendTextRange;
|
|
1217
1224
|
durationMs?: number;
|
|
1218
1225
|
behavior?: "auto" | "smooth";
|
|
1219
1226
|
block?: "start" | "center" | "end" | "nearest";
|
|
@@ -1229,7 +1236,7 @@ Apps can contribute actions for host-rendered transcript selection menus through
|
|
|
1229
1236
|
app state. The host or renderer runtime owns menu placement and click routing.
|
|
1230
1237
|
|
|
1231
1238
|
```ts
|
|
1232
|
-
type
|
|
1239
|
+
type ClankerBendSelectionAction = {
|
|
1233
1240
|
actionId: string;
|
|
1234
1241
|
appId: string;
|
|
1235
1242
|
type: string;
|
|
@@ -1242,7 +1249,7 @@ type OneWhackSelectionAction = {
|
|
|
1242
1249
|
```
|
|
1243
1250
|
|
|
1244
1251
|
When the user chooses a selection action, the host sends a normal
|
|
1245
|
-
`POST /
|
|
1252
|
+
`POST /clankerbend/apps/:appId/actions` request with the current selection in the
|
|
1246
1253
|
action payload or app context.
|
|
1247
1254
|
|
|
1248
1255
|
### Anchored Overlays
|
|
@@ -1251,31 +1258,31 @@ Apps can request a host-rendered overlay anchored to an anchor or text range.
|
|
|
1251
1258
|
Apps provide fields and actions; the host/adapter owns DOM placement.
|
|
1252
1259
|
|
|
1253
1260
|
```text
|
|
1254
|
-
POST /
|
|
1255
|
-
POST /
|
|
1261
|
+
POST /clankerbend/overlay/open
|
|
1262
|
+
POST /clankerbend/overlay/close
|
|
1256
1263
|
```
|
|
1257
1264
|
|
|
1258
1265
|
```ts
|
|
1259
|
-
type
|
|
1266
|
+
type ClankerBendOverlay = {
|
|
1260
1267
|
overlayId: string;
|
|
1261
1268
|
appId: string;
|
|
1262
1269
|
kind: "form" | "menu" | "notice";
|
|
1263
1270
|
title?: string;
|
|
1264
1271
|
anchorId?: string;
|
|
1265
|
-
range?:
|
|
1266
|
-
fields?:
|
|
1267
|
-
actions?:
|
|
1272
|
+
range?: ClankerBendTextRange;
|
|
1273
|
+
fields?: ClankerBendOverlayField[];
|
|
1274
|
+
actions?: ClankerBendOverlayAction[];
|
|
1268
1275
|
openedAt?: string;
|
|
1269
1276
|
};
|
|
1270
1277
|
|
|
1271
|
-
type
|
|
1278
|
+
type ClankerBendOverlayField = {
|
|
1272
1279
|
fieldId: string;
|
|
1273
1280
|
kind: "text" | "textarea" | "hidden";
|
|
1274
1281
|
label?: string;
|
|
1275
1282
|
value?: string;
|
|
1276
1283
|
};
|
|
1277
1284
|
|
|
1278
|
-
type
|
|
1285
|
+
type ClankerBendOverlayAction = {
|
|
1279
1286
|
label: string;
|
|
1280
1287
|
type: string;
|
|
1281
1288
|
payload?: unknown;
|
|
@@ -1289,8 +1296,8 @@ host. Apps add/remove items and ask the host to serialize them into a draft; the
|
|
|
1289
1296
|
adapter owns Codex composer DOM details.
|
|
1290
1297
|
|
|
1291
1298
|
```ts
|
|
1292
|
-
type
|
|
1293
|
-
contextItems:
|
|
1299
|
+
type ClankerBendComposerState = {
|
|
1300
|
+
contextItems: ClankerBendComposerContextItem[];
|
|
1294
1301
|
draft: {
|
|
1295
1302
|
text: string;
|
|
1296
1303
|
mode: "replace" | "append" | "prepend";
|
|
@@ -1300,13 +1307,13 @@ type OneWhackComposerState = {
|
|
|
1300
1307
|
lastSubmittedAt?: string;
|
|
1301
1308
|
};
|
|
1302
1309
|
|
|
1303
|
-
type
|
|
1310
|
+
type ClankerBendComposerContextItem = {
|
|
1304
1311
|
itemId: string;
|
|
1305
1312
|
appId: string;
|
|
1306
1313
|
label: string;
|
|
1307
1314
|
body: string;
|
|
1308
1315
|
anchorId?: string;
|
|
1309
|
-
range?:
|
|
1316
|
+
range?: ClankerBendTextRange;
|
|
1310
1317
|
status: "queued" | "sent" | "resolved";
|
|
1311
1318
|
createdAt?: string;
|
|
1312
1319
|
updatedAt?: string;
|
|
@@ -1314,10 +1321,10 @@ type OneWhackComposerContextItem = {
|
|
|
1314
1321
|
```
|
|
1315
1322
|
|
|
1316
1323
|
```text
|
|
1317
|
-
POST /
|
|
1318
|
-
POST /
|
|
1319
|
-
POST /
|
|
1320
|
-
POST /
|
|
1324
|
+
POST /clankerbend/composer/context
|
|
1325
|
+
POST /clankerbend/composer/context/remove
|
|
1326
|
+
POST /clankerbend/composer/draft
|
|
1327
|
+
POST /clankerbend/composer/submit
|
|
1321
1328
|
```
|
|
1322
1329
|
|
|
1323
1330
|
`/composer/draft` accepts `{ text, mode, contextItemIds }`. `/composer/submit`
|
|
@@ -1333,9 +1340,9 @@ The bridge records events in renderer globals:
|
|
|
1333
1340
|
|
|
1334
1341
|
```ts
|
|
1335
1342
|
type RendererEventBuffer = {
|
|
1336
|
-
selection?:
|
|
1343
|
+
selection?: ClankerBendSelection;
|
|
1337
1344
|
panelOpen?: PanelOpenResult & { at: string };
|
|
1338
|
-
hostEvents?:
|
|
1345
|
+
hostEvents?: ClankerBendRendererHostEvent[];
|
|
1339
1346
|
};
|
|
1340
1347
|
```
|
|
1341
1348
|
|
|
@@ -1345,10 +1352,10 @@ chips, the renderer queues typed host events. The CDP adapter drains those
|
|
|
1345
1352
|
events and invokes the same host APIs that app panels use.
|
|
1346
1353
|
|
|
1347
1354
|
```ts
|
|
1348
|
-
type
|
|
1355
|
+
type ClankerBendRendererHostEvent =
|
|
1349
1356
|
| {
|
|
1350
1357
|
kind: "selection";
|
|
1351
|
-
selection:
|
|
1358
|
+
selection: ClankerBendSelection;
|
|
1352
1359
|
}
|
|
1353
1360
|
| {
|
|
1354
1361
|
kind: "appAction";
|
|
@@ -1367,7 +1374,7 @@ type OneWhackRendererHostEvent =
|
|
|
1367
1374
|
}
|
|
1368
1375
|
| {
|
|
1369
1376
|
kind: "highlightRange";
|
|
1370
|
-
range:
|
|
1377
|
+
range: ClankerBendTextRange;
|
|
1371
1378
|
}
|
|
1372
1379
|
| {
|
|
1373
1380
|
kind: "highlightAnchor";
|
|
@@ -1380,25 +1387,25 @@ host HTTP endpoints directly as an optimization.
|
|
|
1380
1387
|
|
|
1381
1388
|
## App State
|
|
1382
1389
|
|
|
1383
|
-
|
|
1390
|
+
ClankerBend does not prescribe an app state provider. App state can come from
|
|
1384
1391
|
app-server, a local audit system, an MCP server, a test runner, a diagnostics
|
|
1385
1392
|
collector, a bookmark store, an editor-navigation model, or simple DOM
|
|
1386
1393
|
inference.
|
|
1387
1394
|
|
|
1388
1395
|
```ts
|
|
1389
|
-
type
|
|
1396
|
+
type ClankerBendAppState = {
|
|
1390
1397
|
appId: string;
|
|
1391
|
-
status:
|
|
1398
|
+
status: ClankerBendAppStatus;
|
|
1392
1399
|
source: string;
|
|
1393
1400
|
connected: boolean;
|
|
1394
|
-
entries:
|
|
1401
|
+
entries: ClankerBendAppEntry[];
|
|
1395
1402
|
annotations?: AnchorAnnotation[];
|
|
1396
|
-
commands?:
|
|
1397
|
-
selectionActions?:
|
|
1403
|
+
commands?: ClankerBendCommand[];
|
|
1404
|
+
selectionActions?: ClankerBendSelectionAction[];
|
|
1398
1405
|
updatedAt?: string;
|
|
1399
1406
|
};
|
|
1400
1407
|
|
|
1401
|
-
type
|
|
1408
|
+
type ClankerBendAppEntry = {
|
|
1402
1409
|
entryId: string;
|
|
1403
1410
|
appId: string;
|
|
1404
1411
|
anchorId?: string;
|
|
@@ -1418,7 +1425,7 @@ type OneWhackAppEntry = {
|
|
|
1418
1425
|
occurredAt?: string;
|
|
1419
1426
|
};
|
|
1420
1427
|
|
|
1421
|
-
type
|
|
1428
|
+
type ClankerBendCommand = {
|
|
1422
1429
|
commandId: string;
|
|
1423
1430
|
appId: string;
|
|
1424
1431
|
label: string;
|
|
@@ -1429,13 +1436,13 @@ type OneWhackCommand = {
|
|
|
1429
1436
|
};
|
|
1430
1437
|
```
|
|
1431
1438
|
|
|
1432
|
-
Panel UIs should render `
|
|
1439
|
+
Panel UIs should render `ClankerBendAppEntry[]` without knowing the provider. A
|
|
1433
1440
|
timeline, review list, trace viewer, bookmark list, or diagnostics stream can
|
|
1434
1441
|
all be represented as entries bound optionally to transcript anchors.
|
|
1435
1442
|
|
|
1436
1443
|
## App-Server Adapter
|
|
1437
1444
|
|
|
1438
|
-
The app-server adapter is separate from Desktop control.
|
|
1445
|
+
The app-server adapter is separate from Desktop control. ClankerBend hosts are
|
|
1439
1446
|
app-server-aware and report app-server status even when they cannot connect.
|
|
1440
1447
|
|
|
1441
1448
|
Useful app-server methods include:
|
|
@@ -1450,7 +1457,7 @@ Useful app-server methods include:
|
|
|
1450
1457
|
- approval request/response methods
|
|
1451
1458
|
- `thread/rollback`
|
|
1452
1459
|
|
|
1453
|
-
|
|
1460
|
+
ClankerBend should expose app-server data as correlation metadata, not as a replacement
|
|
1454
1461
|
transport.
|
|
1455
1462
|
|
|
1456
1463
|
```ts
|
|
@@ -1463,39 +1470,39 @@ type AppServerStatus = {
|
|
|
1463
1470
|
```
|
|
1464
1471
|
|
|
1465
1472
|
If an app sends approval responses through app-server, that path must be
|
|
1466
|
-
explicit and user-driven.
|
|
1473
|
+
explicit and user-driven. ClankerBend must not auto-approve or auto-deny actions.
|
|
1467
1474
|
|
|
1468
1475
|
## Minimal Endpoint Set
|
|
1469
1476
|
|
|
1470
|
-
A
|
|
1477
|
+
A ClankerBend host must implement these endpoints:
|
|
1471
1478
|
|
|
1472
1479
|
```text
|
|
1473
|
-
GET /
|
|
1474
|
-
GET /
|
|
1475
|
-
GET /
|
|
1476
|
-
GET /
|
|
1477
|
-
GET /
|
|
1478
|
-
GET /
|
|
1479
|
-
GET /
|
|
1480
|
-
POST /
|
|
1481
|
-
POST /
|
|
1482
|
-
POST /
|
|
1483
|
-
POST /
|
|
1484
|
-
POST /
|
|
1480
|
+
GET /clankerbend/manifest
|
|
1481
|
+
GET /clankerbend/state
|
|
1482
|
+
GET /clankerbend/events
|
|
1483
|
+
GET /clankerbend/apps
|
|
1484
|
+
GET /clankerbend/apps/:appId/manifest
|
|
1485
|
+
GET /clankerbend/apps/:appId/state
|
|
1486
|
+
GET /clankerbend/apps/:appId/actions/:actionId
|
|
1487
|
+
POST /clankerbend/apps/:appId/actions
|
|
1488
|
+
POST /clankerbend/panel/open
|
|
1489
|
+
POST /clankerbend/transcript/scroll
|
|
1490
|
+
POST /clankerbend/transcript/highlight
|
|
1491
|
+
POST /clankerbend/selection
|
|
1485
1492
|
```
|
|
1486
1493
|
|
|
1487
1494
|
## Manifest
|
|
1488
1495
|
|
|
1489
|
-
|
|
1496
|
+
ClankerBend hosts and apps describe themselves with manifests. Manifests are
|
|
1490
1497
|
normative for the host launcher, app registry, and panel UI; Codex Desktop does
|
|
1491
1498
|
not need to understand them. A packaged app should keep its manifest on disk,
|
|
1492
1499
|
and a running host must expose resolved manifests through the host API.
|
|
1493
1500
|
|
|
1494
1501
|
```json
|
|
1495
1502
|
{
|
|
1496
|
-
"
|
|
1497
|
-
"hostId": "onewill.
|
|
1498
|
-
"hostName": "
|
|
1503
|
+
"clankerbendVersion": "0.1",
|
|
1504
|
+
"hostId": "onewill.clankerbend.local",
|
|
1505
|
+
"hostName": "ClankerBend Host",
|
|
1499
1506
|
"security": {
|
|
1500
1507
|
"auth": "bearer",
|
|
1501
1508
|
"localDevInsecure": false
|
|
@@ -1533,7 +1540,7 @@ and a running host must expose resolved manifests through the host API.
|
|
|
1533
1540
|
},
|
|
1534
1541
|
"apps": [
|
|
1535
1542
|
{
|
|
1536
|
-
"
|
|
1543
|
+
"clankerbendVersion": "0.1",
|
|
1537
1544
|
"appId": "example.app",
|
|
1538
1545
|
"version": "0.1.0",
|
|
1539
1546
|
"name": "Example App",
|
|
@@ -1582,9 +1589,9 @@ and a running host must expose resolved manifests through the host API.
|
|
|
1582
1589
|
}
|
|
1583
1590
|
```
|
|
1584
1591
|
|
|
1585
|
-
## Public Example:
|
|
1592
|
+
## Public Example: VimNav App
|
|
1586
1593
|
|
|
1587
|
-
The public reference app for
|
|
1594
|
+
The public reference app for ClankerBend `0.1` is VimNav, a Vim-style transcript navigator.
|
|
1588
1595
|
Other apps should use the same host/app contract and document any app-specific
|
|
1589
1596
|
state or action vocabulary they expose.
|
|
1590
1597
|
|
|
@@ -1592,10 +1599,10 @@ state or action vocabulary they expose.
|
|
|
1592
1599
|
|
|
1593
1600
|
```json
|
|
1594
1601
|
{
|
|
1595
|
-
"
|
|
1602
|
+
"clankerbendVersion": "0.1",
|
|
1596
1603
|
"appId": "onewill.vim-nav",
|
|
1597
1604
|
"version": "0.1.0",
|
|
1598
|
-
"name": "
|
|
1605
|
+
"name": "VimNav",
|
|
1599
1606
|
"entry": "http://127.0.0.1:49152/apps/onewill.vim-nav/",
|
|
1600
1607
|
"distribution": {
|
|
1601
1608
|
"kind": "local",
|
|
@@ -1605,7 +1612,7 @@ state or action vocabulary they expose.
|
|
|
1605
1612
|
"entrypoint": {
|
|
1606
1613
|
"kind": "module",
|
|
1607
1614
|
"module": "./src/vim-nav-app.js",
|
|
1608
|
-
"factory": "
|
|
1615
|
+
"factory": "createVimNavApp",
|
|
1609
1616
|
"publicDir": "./public"
|
|
1610
1617
|
},
|
|
1611
1618
|
"capabilities": {
|
|
@@ -1632,7 +1639,7 @@ state or action vocabulary they expose.
|
|
|
1632
1639
|
"appServerRollback": false
|
|
1633
1640
|
},
|
|
1634
1641
|
"panel": {
|
|
1635
|
-
"title": "
|
|
1642
|
+
"title": "VimNav",
|
|
1636
1643
|
"reloadPolicy": "preserve",
|
|
1637
1644
|
"preferredWidth": 360
|
|
1638
1645
|
}
|
|
@@ -1738,7 +1745,7 @@ state or action vocabulary they expose.
|
|
|
1738
1745
|
|
|
1739
1746
|
## Security And Lifecycle
|
|
1740
1747
|
|
|
1741
|
-
|
|
1748
|
+
ClankerBend is local integration glue, not a sandbox or security boundary. The
|
|
1742
1749
|
host still needs explicit safety rules because it can reach CDP, app-server,
|
|
1743
1750
|
and user-visible UI.
|
|
1744
1751
|
|
|
@@ -1755,7 +1762,7 @@ and user-visible UI.
|
|
|
1755
1762
|
App manifests declare requested permissions. The host must enforce them.
|
|
1756
1763
|
|
|
1757
1764
|
```ts
|
|
1758
|
-
type
|
|
1765
|
+
type ClankerBendAppPermissions = {
|
|
1759
1766
|
transcriptRead?: boolean;
|
|
1760
1767
|
transcriptAnnotate?: boolean;
|
|
1761
1768
|
transcriptNavigate?: boolean;
|
|
@@ -1765,7 +1772,7 @@ type OneWhackAppPermissions = {
|
|
|
1765
1772
|
};
|
|
1766
1773
|
```
|
|
1767
1774
|
|
|
1768
|
-
For
|
|
1775
|
+
For ClankerBend `0.1`, an explicit `false` means the host must deny that
|
|
1769
1776
|
capability. Omitted permissions are interpreted by host policy, but reference
|
|
1770
1777
|
hosts should treat omitted transcript permissions as allowed for local
|
|
1771
1778
|
development apps. A host that denies `transcriptRead` must provide an empty
|
|
@@ -1784,7 +1791,7 @@ user-driven, and visible in app state.
|
|
|
1784
1791
|
- Prefer launching through the host so CDP is enabled at process startup.
|
|
1785
1792
|
- Attach to an already-running Desktop session only when CDP is already
|
|
1786
1793
|
available.
|
|
1787
|
-
- Use a test or
|
|
1794
|
+
- Use a test or ClankerBend profile when launching Desktop unless the user
|
|
1788
1795
|
explicitly requests their normal profile.
|
|
1789
1796
|
- Clean up host-launched Codex and Node processes on exit.
|
|
1790
1797
|
|
|
@@ -1797,27 +1804,27 @@ user-driven, and visible in app state.
|
|
|
1797
1804
|
|
|
1798
1805
|
## Appendix: Non-Normative Reference Mapping
|
|
1799
1806
|
|
|
1800
|
-
The public
|
|
1807
|
+
The public VimNav example maps implementation files to ClankerBend terms as
|
|
1801
1808
|
follows:
|
|
1802
1809
|
|
|
1803
|
-
|
|
|
1810
|
+
| VimNav implementation | ClankerBend term |
|
|
1804
1811
|
| --- | --- |
|
|
1805
|
-
| `
|
|
1806
|
-
| `
|
|
1807
|
-
| `
|
|
1808
|
-
| `
|
|
1809
|
-
| `
|
|
1810
|
-
| `
|
|
1811
|
-
| `
|
|
1812
|
+
| `clankerbend/host/src/index.js` | Reusable ClankerBend Host |
|
|
1813
|
+
| `clankerbend/apps/vim-nav/server.mjs` | Host launcher registering one app |
|
|
1814
|
+
| `clankerbend/apps/vim-nav/src/vim-nav-app.js` | VimNav app manifest, state, and actions |
|
|
1815
|
+
| `clankerbend/apps/vim-nav/public/index.html` | Hosted app panel |
|
|
1816
|
+
| `clankerbend/apps/vim-nav/public/app.js` | App client |
|
|
1817
|
+
| `clankerbend/apps/vim-nav/public/styles.css` | App panel styling |
|
|
1818
|
+
| `clankerbend/host/src/codex-desktop-renderer-bridge.js` | Host-owned injected renderer bridge |
|
|
1812
1819
|
| CDP `Runtime.evaluate` | Desktop CDP Adapter call, not used by mock tests |
|
|
1813
|
-
| `window.
|
|
1814
|
-
| `window.
|
|
1815
|
-
| `window.
|
|
1816
|
-
| `window.
|
|
1820
|
+
| `window.__clankerbendRuntime.getBridge("onewill.vim-nav").snapshot()` | `RendererBridge.snapshot()` |
|
|
1821
|
+
| `window.__clankerbendRuntime.getBridge("onewill.vim-nav").openPanel()` | `RendererBridge.openPanel()` |
|
|
1822
|
+
| `window.__clankerbendRuntime.getBridge("onewill.vim-nav").scrollToAnchor(anchorId)` | `scrollToAnchor(anchorId)` |
|
|
1823
|
+
| `window.__clankerbendRuntime.getBridge("onewill.vim-nav").highlightAnchor(anchorId)` | `highlightAnchor(anchorId)` |
|
|
1817
1824
|
| numbered transcript ruler pills | `AnchorAnnotation.markers` |
|
|
1818
|
-
| `/
|
|
1819
|
-
| `/
|
|
1820
|
-
| `
|
|
1825
|
+
| `/clankerbend/apps/onewill.vim-nav/state` | App state endpoint |
|
|
1826
|
+
| `/clankerbend/apps/onewill.vim-nav/actions` | App action endpoint |
|
|
1827
|
+
| `clankerbend/apps/vim-nav/test-clankerbend.mjs` | Fast host/app behavior e2e |
|
|
1821
1828
|
|
|
1822
1829
|
## Operational Rules
|
|
1823
1830
|
|
|
@@ -1832,7 +1839,7 @@ follows:
|
|
|
1832
1839
|
|
|
1833
1840
|
## Behavior Profiles
|
|
1834
1841
|
|
|
1835
|
-
|
|
1842
|
+
ClankerBend `0.1` describes behavior in four profiles. Implementations should state
|
|
1836
1843
|
which profiles they support and cover those behaviors with e2e tests.
|
|
1837
1844
|
|
|
1838
1845
|
### Core Host
|
|
@@ -1841,25 +1848,25 @@ A Core Host must implement:
|
|
|
1841
1848
|
|
|
1842
1849
|
- JSON envelope and error model.
|
|
1843
1850
|
- Session token behavior or explicit `localDevInsecure`.
|
|
1844
|
-
- `GET /
|
|
1845
|
-
- `GET /
|
|
1846
|
-
- `GET /
|
|
1847
|
-
- `GET /
|
|
1848
|
-
- `GET /
|
|
1849
|
-
- `GET /
|
|
1850
|
-
- `GET /
|
|
1851
|
-
- `POST /
|
|
1851
|
+
- `GET /clankerbend/manifest`.
|
|
1852
|
+
- `GET /clankerbend/state`.
|
|
1853
|
+
- `GET /clankerbend/events`.
|
|
1854
|
+
- `GET /clankerbend/apps`.
|
|
1855
|
+
- `GET /clankerbend/apps/:appId/manifest`.
|
|
1856
|
+
- `GET /clankerbend/apps/:appId/state`.
|
|
1857
|
+
- `GET /clankerbend/apps/:appId/actions/:actionId`.
|
|
1858
|
+
- `POST /clankerbend/apps/:appId/actions`.
|
|
1852
1859
|
- App lifecycle states and permission enforcement.
|
|
1853
1860
|
|
|
1854
1861
|
### Transcript Host
|
|
1855
1862
|
|
|
1856
1863
|
A Transcript Host is a Core Host that also implements:
|
|
1857
1864
|
|
|
1858
|
-
- Transcript anchors in `
|
|
1859
|
-
- `POST /
|
|
1860
|
-
- `POST /
|
|
1861
|
-
- `POST /
|
|
1862
|
-
- `POST /
|
|
1865
|
+
- Transcript anchors in `ClankerBendPublicState`.
|
|
1866
|
+
- `POST /clankerbend/panel/open`.
|
|
1867
|
+
- `POST /clankerbend/transcript/scroll`.
|
|
1868
|
+
- `POST /clankerbend/transcript/highlight`.
|
|
1869
|
+
- `POST /clankerbend/selection`.
|
|
1863
1870
|
- Annotation ownership and merge rules.
|
|
1864
1871
|
|
|
1865
1872
|
### Codex Desktop Adapter
|
|
@@ -1873,48 +1880,48 @@ process cleanup.
|
|
|
1873
1880
|
|
|
1874
1881
|
An App Client must:
|
|
1875
1882
|
|
|
1876
|
-
- Read `GET /
|
|
1877
|
-
- Use `GET /
|
|
1883
|
+
- Read `GET /clankerbend/manifest` and verify required capabilities.
|
|
1884
|
+
- Use `GET /clankerbend/apps/:appId/state` or `GET /clankerbend/state` as its source
|
|
1878
1885
|
of truth.
|
|
1879
|
-
- Use `POST /
|
|
1880
|
-
- Use `GET /
|
|
1886
|
+
- Use `POST /clankerbend/apps/:appId/actions` for app commands.
|
|
1887
|
+
- Use `GET /clankerbend/apps/:appId/actions/:actionId` to recover action results
|
|
1881
1888
|
when needed.
|
|
1882
|
-
- Treat `
|
|
1889
|
+
- Treat `ClankerBendError` as the only failure contract.
|
|
1883
1890
|
- Degrade cleanly when capabilities or permissions are unavailable.
|
|
1884
1891
|
|
|
1885
1892
|
## Required E2E Coverage
|
|
1886
1893
|
|
|
1887
|
-
A
|
|
1894
|
+
A ClankerBend `0.1` implementation should include fast e2e coverage for these
|
|
1888
1895
|
observable behaviors:
|
|
1889
1896
|
|
|
1890
|
-
1. `GET /
|
|
1897
|
+
1. `GET /clankerbend/manifest` returns `ok: true`, `clankerbendVersion: "0.1"`,
|
|
1891
1898
|
host capabilities, security posture, and app manifests.
|
|
1892
|
-
2. `GET /
|
|
1899
|
+
2. `GET /clankerbend/state` returns `ok: true` with `protocolName: "clankerbend"`,
|
|
1893
1900
|
`protocolVersion: "0.1"`, numeric `sequence`, ISO `generatedAt`, host
|
|
1894
1901
|
status, transcript state, app states, and app-server status.
|
|
1895
|
-
3. `GET /
|
|
1902
|
+
3. `GET /clankerbend/events` emits an initial `state` event, monotonically
|
|
1896
1903
|
increasing event ids, and `heartbeat` events.
|
|
1897
|
-
4. Unknown apps return `404` with `
|
|
1904
|
+
4. Unknown apps return `404` with `ClankerBendError.error.code: "not_found"`.
|
|
1898
1905
|
5. Missing or invalid bearer tokens return `401` unless `localDevInsecure` is
|
|
1899
1906
|
explicitly enabled.
|
|
1900
1907
|
6. Duplicate `(appId, actionId)` submissions return the same receipt or result
|
|
1901
1908
|
without executing twice.
|
|
1902
1909
|
7. Async action completion is observable through both SSE `action` events and
|
|
1903
|
-
`GET /
|
|
1910
|
+
`GET /clankerbend/apps/:appId/actions/:actionId`.
|
|
1904
1911
|
8. Stale selections return the current accepted selection with `stale: true`
|
|
1905
1912
|
and do not change host `selection`.
|
|
1906
1913
|
9. Annotation merge order is stable by `priority`, then `appId`, then
|
|
1907
1914
|
`markerId`.
|
|
1908
1915
|
10. Disabling or unmounting an app removes only that app's annotations.
|
|
1909
|
-
11. `POST /
|
|
1910
|
-
`POST /
|
|
1916
|
+
11. `POST /clankerbend/transcript/scroll` and
|
|
1917
|
+
`POST /clankerbend/transcript/highlight` return `404` for unknown anchors and do
|
|
1911
1918
|
not mutate selection.
|
|
1912
1919
|
12. Opening an already-loaded panel does not reload it when
|
|
1913
1920
|
`reloadPolicy: "preserve"`.
|
|
1914
1921
|
|
|
1915
1922
|
## Versioning
|
|
1916
1923
|
|
|
1917
|
-
|
|
1924
|
+
ClankerBend version `0.1` is the minimal hack-path protocol:
|
|
1918
1925
|
|
|
1919
1926
|
- local host state/event endpoints
|
|
1920
1927
|
- host/app manifest negotiation
|
|
@@ -1931,5 +1938,5 @@ Future versions may add alternate adapters, but they should preserve the same
|
|
|
1931
1938
|
host-facing concepts. Minor versions should add optional capabilities or fields
|
|
1932
1939
|
behind capability checks. Major versions may change required fields or endpoint
|
|
1933
1940
|
semantics, but a host must make that visible through `protocolVersion`,
|
|
1934
|
-
`
|
|
1941
|
+
`clankerbendVersion`, and `capabilities.protocolVersion` before app actions are
|
|
1935
1942
|
required.
|