@standardagents/code-plugin-sdk 1.0.0-alpha.4-hover.2 → 1.0.0-alpha.6-surfaces.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/REFERENCE.md +78 -6
- package/package.json +1 -1
- package/src/index.d.ts +18 -2
- package/src/protocol.mjs +1 -1
- package/src/runtime.mjs +2 -1
- package/src/testing.mjs +2 -1
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ Its `package.json` includes a static `standardPlugin` manifest.
|
|
|
11
11
|
{
|
|
12
12
|
"name": "example-status",
|
|
13
13
|
"type": "module",
|
|
14
|
-
"peerDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.
|
|
15
|
-
"devDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.
|
|
14
|
+
"peerDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.6-surfaces.0" },
|
|
15
|
+
"devDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.6-surfaces.0" },
|
|
16
16
|
"standardPlugin": {
|
|
17
17
|
"apiVersion": 1,
|
|
18
18
|
"id": "example-status",
|
|
@@ -177,8 +177,15 @@ methods.
|
|
|
177
177
|
|
|
178
178
|
`ctx.state` is per-plugin key and value storage. Standard Code stores these
|
|
179
179
|
values in the user's account, so every machine the user has reads and writes
|
|
180
|
-
the same keys.
|
|
181
|
-
|
|
180
|
+
the same keys. `ctx.state.keys()` returns the sorted key names for this
|
|
181
|
+
plugin's account namespace. It returns an empty array when the plugin has no
|
|
182
|
+
keys and accepts only `RequestOptions`; it has no prefix or pagination
|
|
183
|
+
arguments. Another plugin cannot read these keys, even when it uses the same
|
|
184
|
+
account. Key names are 1 to 128 bytes. The first character is ASCII
|
|
185
|
+
alphanumeric or `_`; later characters are ASCII alphanumeric, `_`, `.`, `:`,
|
|
186
|
+
or `-`. A plugin holds at most 256 keys, and one value is at most 64 KiB of
|
|
187
|
+
JSON. The host applies the same account authority and revocation checks to
|
|
188
|
+
`keys()` as it applies to `get()` and `set()`.
|
|
182
189
|
The public context declarations are in `src/index.d.ts`.
|
|
183
190
|
|
|
184
191
|
## Releases
|
package/REFERENCE.md
CHANGED
|
@@ -5,7 +5,7 @@ The public declarations live in
|
|
|
5
5
|
plugin authors.
|
|
6
6
|
|
|
7
7
|
The bundled SDK and the npm package use the same public declarations. This
|
|
8
|
-
reference describes SDK `1.0.0-alpha.
|
|
8
|
+
reference describes SDK `1.0.0-alpha.5-usage.0`. Plugins should declare the
|
|
9
9
|
version published for their product build in both `peerDependencies` and
|
|
10
10
|
`devDependencies`; the manifest `apiVersion` remains `1`.
|
|
11
11
|
|
|
@@ -44,6 +44,12 @@ panels:
|
|
|
44
44
|
|
|
45
45
|
`PRESENTATIONS` lists the accepted presentation values.
|
|
46
46
|
|
|
47
|
+
Account-level sidebar cards prefer a live producer on the viewing machine.
|
|
48
|
+
This keeps card actions and attached panels local to that viewer. If no live
|
|
49
|
+
local producer exists, the host uses its deterministic remote-source fallback;
|
|
50
|
+
remote source authority restrictions still apply. Plugins aggregate shared
|
|
51
|
+
account data themselves through the public state API.
|
|
52
|
+
|
|
47
53
|
An anchor determines the entity that owns a contribution. Machine anchors use
|
|
48
54
|
a machine entity, project anchors use a project entity, and pane anchors use a
|
|
49
55
|
pane entity. The `plugins` and `account` anchors are account-level surfaces;
|
|
@@ -228,6 +234,11 @@ add node types.
|
|
|
228
234
|
|
|
229
235
|
### Card views
|
|
230
236
|
|
|
237
|
+
Titles come from the contribution's `title`. The host adds no status dot,
|
|
238
|
+
health text, or machine label to a card title or panel. Authors can include
|
|
239
|
+
indicators in their own title or content. Runtime health remains available in
|
|
240
|
+
plugin management through `ctx.health.set()`.
|
|
241
|
+
|
|
231
242
|
A card shows a short summary. Its view can contain `stack`, `row`, `text`,
|
|
232
243
|
`badge`, `dot`, `progress`, `segments`, `stat`, and `divider` nodes, and it
|
|
233
244
|
is at most 6 lines tall. A `stack` is as tall as its children plus `gap`
|
|
@@ -305,17 +316,48 @@ export default definePlugin({
|
|
|
305
316
|
`packages/plugin-sdk/test/fixtures/view-builds-monitor.json` holds a complete
|
|
306
317
|
view that matches the protocol's round-trip test.
|
|
307
318
|
|
|
319
|
+
### Pane header and footer slots
|
|
320
|
+
|
|
321
|
+
A `slot` on `pane.header` or `pane.footer` contributes rows inside the pane
|
|
322
|
+
frame, above or below its terminal body. Declarations use `width: "full"`
|
|
323
|
+
(the default) or `width: "half"`. Contributions stack in ascending plugin
|
|
324
|
+
order, then plugin ID and contribution ID. Full contributions occupy their
|
|
325
|
+
own rows. Consecutive half contributions share a row, with the odd column
|
|
326
|
+
assigned to the left. An unpaired half leaves the right half empty.
|
|
327
|
+
Multiline half contributions share a block as tall as the taller contribution.
|
|
328
|
+
|
|
329
|
+
Rows, styled text, and non-interactive canvas cells render in these slots.
|
|
330
|
+
Each slot is bounded to 32 rows. Small panes clip slot rows while preserving
|
|
331
|
+
terminal space. Slot height participates in the same geometry used for terminal
|
|
332
|
+
painting, pointer input, graphics clipping, and controller-owned resizing.
|
|
333
|
+
Remote viewers receive the same contributions. Missing panes, stale producer
|
|
334
|
+
snapshots, and mismatched pane generations withdraw their rows.
|
|
335
|
+
|
|
336
|
+
Pane-slot actions, input capture, hover popovers, and persisted slot collapse
|
|
337
|
+
remain unconnected. They are separate from the implemented row rendering.
|
|
338
|
+
|
|
339
|
+
`ctx.context.get()` exposes `projects`, `panes`, and `complete` for the owning
|
|
340
|
+
machine. Project records contain `{ entity, name, path }`; pane records contain
|
|
341
|
+
`{ entity, projectId, name, cwd }`. Entities include machine ID and a decimal
|
|
342
|
+
string generation. The pane `cwd` is its configured project launch directory.
|
|
343
|
+
Live shell-directory observations are a separate future capability. Each list
|
|
344
|
+
has at most 128 records, and their combined JSON is bounded to 32 KiB. `complete`
|
|
345
|
+
is false when a local path could not be decoded or a bound omitted records.
|
|
346
|
+
Commands, launch environment, prompts, and encrypted payloads are excluded.
|
|
347
|
+
Plugins must perform filesystem work on their owning machine and clear
|
|
348
|
+
contributions when their target disappears or changes generation.
|
|
349
|
+
|
|
308
350
|
### Current frontend coverage
|
|
309
351
|
|
|
310
|
-
Sections, plugin cards, machine slots, canvas surfaces, host-rendered views,
|
|
352
|
+
Sections, plugin cards, machine slots, stacked pane header/footer slots, canvas surfaces, host-rendered views,
|
|
311
353
|
global command palette entries, and the three
|
|
312
|
-
panel presentations are implemented. A popover
|
|
313
|
-
modal area. A column attaches to the sidebar and resizes the workspace. A
|
|
354
|
+
panel presentations are implemented. A popover follows the content width and height within the
|
|
355
|
+
modal area. Its title is the contribution title and its body starts beneath it. A column attaches to the sidebar and resizes the workspace. A
|
|
314
356
|
plugin pane has a tab and focus, drag, close, and reveal behavior; its width
|
|
315
357
|
follows content and it does not create a tmux pane or resize a terminal.
|
|
316
358
|
|
|
317
|
-
Project slot anchors, standalone badges attached to hierarchy rows,
|
|
318
|
-
|
|
359
|
+
Project slot anchors, standalone badges attached to hierarchy rows,
|
|
360
|
+
menu registrations, key registrations, link registrations,
|
|
319
361
|
and entity-scoped command palette entries have protocol and composition support
|
|
320
362
|
but do not have connected production frontend surfaces. Badge nodes inside a
|
|
321
363
|
view tree are rendered. The `overlay`
|
|
@@ -348,6 +390,18 @@ write and focus, subscription changes, health, and webhook acknowledgements.
|
|
|
348
390
|
Each operation has a typed input and output. `RequestOptions` accepts an abort
|
|
349
391
|
signal and a timeout.
|
|
350
392
|
|
|
393
|
+
`ctx.state` provides account-backed `get(key)`, `set(key, value)`, and
|
|
394
|
+
`keys()` methods. State is isolated by the plugin ID. `keys()` takes no input
|
|
395
|
+
besides optional `RequestOptions` and returns a sorted `string[]` containing
|
|
396
|
+
the plugin's key names. The result is empty when no keys exist, is limited to
|
|
397
|
+
256 keys, and has no prefix or pagination parameters. A key written by one
|
|
398
|
+
machine is visible to the other machines signed into the same account, so a
|
|
399
|
+
plugin can use distinct machine-specific key names without maintaining a
|
|
400
|
+
shared index. Key names are 1 to 128 bytes. The first character is ASCII
|
|
401
|
+
alphanumeric or `_`; later characters are ASCII alphanumeric, `_`, `.`, `:`,
|
|
402
|
+
or `-`. Values remain limited to 64 KiB of JSON. The host applies the same
|
|
403
|
+
account authority and revocation checks to all three methods.
|
|
404
|
+
|
|
351
405
|
The public SDK applies the same capability checks and request bounds to every
|
|
352
406
|
plugin. Plugin IDs do not grant fetch, pane, project, or secret exceptions.
|
|
353
407
|
The current native host connects fetch, secrets, configuration, account state,
|
|
@@ -373,6 +427,9 @@ capabilities govern SDK/host operations and do not provide OS sandboxing.
|
|
|
373
427
|
{
|
|
374
428
|
"accountId": "account-id",
|
|
375
429
|
"machineId": "machine-id",
|
|
430
|
+
"projects": [],
|
|
431
|
+
"panes": [],
|
|
432
|
+
"complete": true,
|
|
376
433
|
"build": {
|
|
377
434
|
"version": "0.14.1-branch.3bd7f23c.98a422",
|
|
378
435
|
"commit": "<40 hex digits>",
|
|
@@ -421,6 +478,21 @@ the daemon does not deliver it when a plugin starts. Read `context.get` at
|
|
|
421
478
|
activation for the initial values. The daemon pushes each change, so a plugin
|
|
422
479
|
needs no schedule to track the followed channel.
|
|
423
480
|
|
|
481
|
+
Plugins with `events` can subscribe to `onEvent('pane-context', handler)`.
|
|
482
|
+
Its `data` replaces the local `machineId`, `projects`, `panes`, and `complete`
|
|
483
|
+
fields from `context.get`. Only a changed local context produces an event.
|
|
484
|
+
Subscribe before the initial `context.get` request and discard that response
|
|
485
|
+
if an event arrives while it is pending. Pane removal and generation changes
|
|
486
|
+
must withdraw publishers for the old entity. Project launch directories are
|
|
487
|
+
configured paths; shell `cd` changes are outside this event's current contract.
|
|
488
|
+
|
|
489
|
+
A plugin can watch these directories with Node's native filesystem events.
|
|
490
|
+
Debounce bursts with a one-shot timer, retain changes that arrive during a read,
|
|
491
|
+
and close watchers on disposal or target removal. Do not schedule directory
|
|
492
|
+
or Git refresh polling. Watch linked-worktree Git metadata and shared refs as
|
|
493
|
+
well as the working tree. Filesystem notification errors should report degraded
|
|
494
|
+
health; they must not silently switch to polling.
|
|
495
|
+
|
|
424
496
|
## Hooks and delivery
|
|
425
497
|
|
|
426
498
|
Hook dispatch and durable webhook delivery are planned host integrations.
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -235,13 +235,27 @@ export type FleetPolicy = {
|
|
|
235
235
|
/** The pinned or target version, or null when it is not known. */
|
|
236
236
|
version: string | null;
|
|
237
237
|
};
|
|
238
|
+
/** A local project available to plugins on its owning machine. */
|
|
239
|
+
export interface PluginProjectContext { entity: EntityRef; name: string; path: string }
|
|
240
|
+
/** Pane context with its configured project launch directory. */
|
|
241
|
+
export interface PluginPaneContext { entity: EntityRef; projectId: string; name: string; cwd: string }
|
|
238
242
|
/** The `context.get` result. `build` and `fleet` are null when unknown. */
|
|
239
243
|
export type PluginContextInfo = {
|
|
240
244
|
accountId: string;
|
|
241
245
|
machineId: string;
|
|
246
|
+
projects: PluginProjectContext[];
|
|
247
|
+
panes: PluginPaneContext[];
|
|
248
|
+
/** False when local records could not be included or decoded. */
|
|
249
|
+
complete: boolean;
|
|
242
250
|
build?: BuildInfo | null;
|
|
243
251
|
fleet?: FleetPolicy | null;
|
|
244
252
|
};
|
|
253
|
+
/** Replaces the local pane/project context after an account change. */
|
|
254
|
+
export type PaneContextEventData = Pick<PluginContextInfo, 'machineId' | 'projects' | 'panes' | 'complete'>;
|
|
255
|
+
export interface PaneContextEvent extends PluginEvent {
|
|
256
|
+
name: 'pane-context';
|
|
257
|
+
data: PaneContextEventData;
|
|
258
|
+
}
|
|
245
259
|
/** The `data` of a `build-context` event. */
|
|
246
260
|
export type BuildContextEventData = {
|
|
247
261
|
build: BuildInfo | null;
|
|
@@ -275,6 +289,7 @@ export interface OperationMap {
|
|
|
275
289
|
'config.get': { input: Record<string, never>; output: Record<string, Json> };
|
|
276
290
|
'state.get': { input: { key: string }; output: Json };
|
|
277
291
|
'state.set': { input: { key: string; value: Json }; output: null };
|
|
292
|
+
'state.keys': { input: Record<string, never>; output: string[] };
|
|
278
293
|
'context.get': { input: Record<string, never>; output: PluginContextInfo };
|
|
279
294
|
'popover.open': { input: Popover; output: { choiceId?: string; values?: Record<string, Json>; confirmationId?: string } | null };
|
|
280
295
|
'canvas.write': { input: { key: ContributionKey; ansi: string }; output: null };
|
|
@@ -331,6 +346,7 @@ export interface PluginContext {
|
|
|
331
346
|
key(id: string, handler: ActionHandler): KeyRegistration;
|
|
332
347
|
link(id: string, options: LinkOptions, handler: LinkHandler): Subscription;
|
|
333
348
|
link(id: string, handler: LinkHandler): Subscription;
|
|
349
|
+
onEvent(name: 'pane-context', handler: (event: PaneContextEvent, context: HandlerContext) => void | Promise<void>, condition?: Condition): Subscription;
|
|
334
350
|
onEvent(name: 'build-context', handler: (event: BuildContextEvent, context: HandlerContext) => void | Promise<void>, condition?: Condition): Subscription;
|
|
335
351
|
onEvent(name: string, handler: (event: PluginEvent, context: HandlerContext) => void | Promise<void>, condition?: Condition): Subscription;
|
|
336
352
|
onHook(name: string, handler: (event: HookEvent, context: HandlerContext) => HookResult | Promise<HookResult>): Subscription;
|
|
@@ -357,8 +373,8 @@ export interface PluginContext {
|
|
|
357
373
|
fetch(args: OperationMap['fetch']['input'], options?: RequestOptions): Promise<OperationMap['fetch']['output']>;
|
|
358
374
|
secrets: { get(name: string, options?: RequestOptions): Promise<string | null> };
|
|
359
375
|
config: { get(options?: RequestOptions): Promise<Record<string, Json>> };
|
|
360
|
-
/** Per-plugin key and value storage, held in the user's account and shared across their machines: 256 keys per plugin, 64 KiB per value. */
|
|
361
|
-
state: { get(key: string, options?: RequestOptions): Promise<Json>; set(key: string, value: Json, options?: RequestOptions): Promise<null> };
|
|
376
|
+
/** Per-plugin key and value storage, held in the user's account and shared across their machines: 256 keys per plugin, 64 KiB per value. `keys()` returns the sorted key names. */
|
|
377
|
+
state: { get(key: string, options?: RequestOptions): Promise<Json>; set(key: string, value: Json, options?: RequestOptions): Promise<null>; keys(options?: RequestOptions): Promise<string[]> };
|
|
362
378
|
context: { get(options?: RequestOptions): Promise<PluginContextInfo> };
|
|
363
379
|
popover: { open(args: Popover, options?: RequestOptions): Promise<OperationMap['popover.open']['output']> };
|
|
364
380
|
health: { set(args: OperationMap['health.set']['input'], options?: RequestOptions): Promise<null> };
|
package/src/protocol.mjs
CHANGED
|
@@ -8,7 +8,7 @@ export const OPERATIONS = Object.freeze({
|
|
|
8
8
|
'pane.input': 'panes', 'pane.focus': 'panes', 'pane.wait': 'panes',
|
|
9
9
|
'project.create': 'projects', 'project.remove': 'projects',
|
|
10
10
|
'notification.show': 'notifications', 'url.open': 'url', fetch: 'fetch',
|
|
11
|
-
'secret.get': 'secrets', 'config.get': null, 'state.get': null, 'state.set': null,
|
|
11
|
+
'secret.get': 'secrets', 'config.get': null, 'state.get': null, 'state.set': null, 'state.keys': null,
|
|
12
12
|
'context.get': null, 'popover.open': 'surfaces', 'canvas.write': 'surfaces',
|
|
13
13
|
'canvas.focus': 'surfaces', 'subscription.add': null, 'subscription.remove': null,
|
|
14
14
|
'health.set': null, 'webhook.ack': 'webhook',
|
package/src/runtime.mjs
CHANGED
|
@@ -372,7 +372,8 @@ export function createRuntime({ manifest: input, producer, send, clock = realClo
|
|
|
372
372
|
secrets: Object.freeze({ get: (name, options) => request('secret.get', { name }, options) }),
|
|
373
373
|
config: Object.freeze({ get: options => request('config.get', {}, options) }),
|
|
374
374
|
state: Object.freeze({ get: (key, options) => request('state.get', { key }, options),
|
|
375
|
-
set: (key, value, options) => request('state.set', { key, value }, options)
|
|
375
|
+
set: (key, value, options) => request('state.set', { key, value }, options),
|
|
376
|
+
keys: options => request('state.keys', {}, options) }),
|
|
376
377
|
context: Object.freeze({ get: options => request('context.get', {}, options) }),
|
|
377
378
|
popover: Object.freeze({ open: method('popover.open') }),
|
|
378
379
|
health: Object.freeze({ set: method('health.set') }),
|
package/src/testing.mjs
CHANGED
|
@@ -43,8 +43,9 @@ export function createHarness({ manifest: input, machineId = 'test-machine', epo
|
|
|
43
43
|
ensure(accountState.has(args.key) || accountState.size < LIMITS.stateKeys, 'queue_full', 'Harness state holds 256 keys for this plugin')
|
|
44
44
|
jsonBytes(args.value, LIMITS.stateValueBytes)
|
|
45
45
|
accountState.set(args.key, structuredClone(args.value)); return null
|
|
46
|
+
case 'state.keys': return [...accountState.keys()].sort()
|
|
46
47
|
case 'config.get': return {}
|
|
47
|
-
case 'context.get': return { accountId: 'test-account', machineId, build: null, fleet: null }
|
|
48
|
+
case 'context.get': return { accountId: 'test-account', machineId, build: null, fleet: null, projects: [], panes: [], complete: true }
|
|
48
49
|
case 'health.set': return null
|
|
49
50
|
default: throw new PluginError('missing_fixture', `Provide a harness handler for ${operation.op}`)
|
|
50
51
|
}
|