@standardagents/code-plugin-sdk 1.0.0-alpha.4-hover.1 → 1.0.0-alpha.5-usage.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 +14 -7
- package/REFERENCE.md +82 -9
- package/package.json +1 -1
- package/src/index.d.ts +20 -4
- package/src/protocol.mjs +1 -1
- package/src/runtime.mjs +34 -3
- 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.5-usage.0" },
|
|
15
|
+
"devDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.5-usage.0" },
|
|
16
16
|
"standardPlugin": {
|
|
17
17
|
"apiVersion": 1,
|
|
18
18
|
"id": "example-status",
|
|
@@ -46,9 +46,9 @@ plugin's own test suite.
|
|
|
46
46
|
|
|
47
47
|
The complete rendering contract is in [REFERENCE.md](./REFERENCE.md). It
|
|
48
48
|
covers rows, styled text, badges, cards, slots, panels, overlays, host-rendered
|
|
49
|
-
views, ANSI canvases, canvas hover text, input focus,
|
|
50
|
-
presentations, remote read-only behavior, and the frontend
|
|
51
|
-
still planned.
|
|
49
|
+
views, ANSI canvases, styled canvas hover text, input focus,
|
|
50
|
+
popover/column/pane presentations, remote read-only behavior, and the frontend
|
|
51
|
+
surfaces that are still planned.
|
|
52
52
|
|
|
53
53
|
Plugin source imports the default package entry. Plugin tests may import
|
|
54
54
|
`@standardagents/code-plugin-sdk/testing` for `createHarness`. Runner internals,
|
|
@@ -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;
|
|
@@ -139,11 +145,15 @@ wraps or scrolls. Wide characters take two cells. Cells in default colours
|
|
|
139
145
|
take the viewer's theme, and a blank default cell shows the surface under the
|
|
140
146
|
canvas.
|
|
141
147
|
|
|
142
|
-
`hover` is an optional one-line string
|
|
143
|
-
fit within 512 UTF-8 bytes, and
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
`hover` is an optional one-line string or an array of styled `TextSpan` values.
|
|
149
|
+
The string must be non-empty after trimming, fit within 512 UTF-8 bytes, and
|
|
150
|
+
contain no C0, C1, DEL, or bidirectional formatting character. Styled hover
|
|
151
|
+
values contain at most 32 spans whose joined text follows the same bounds and
|
|
152
|
+
must be non-empty. Span styling supports `foreground`, `background`, `bold`,
|
|
153
|
+
`italic`, and `underline`; `actionId` is rejected because hover text is
|
|
154
|
+
passive. `Canvas.replace` publishes the canvas metadata, so a plugin can update
|
|
155
|
+
the hover text with another `replace` call while retaining the same canvas
|
|
156
|
+
grid.
|
|
147
157
|
|
|
148
158
|
On the current frontend, live canvases in `machine.before` and
|
|
149
159
|
`machine.after` slots are hover targets. The viewer waits 300 milliseconds
|
|
@@ -152,6 +162,8 @@ line owned by the host. Leaving the canvas dismisses it immediately. Hover
|
|
|
152
162
|
does not focus the canvas or change its geometry. The dwell timer and pointer
|
|
153
163
|
state belong to each viewer, so remote viewers can see hover text for a
|
|
154
164
|
machine slot independently.
|
|
165
|
+
Hover overlays for these machine slots use the same 22% sidebar machine colour
|
|
166
|
+
blend as the machine name bar over the viewer's background.
|
|
155
167
|
|
|
156
168
|
`focus(true)` asks for keyboard input and needs `captureInput: true` in the
|
|
157
169
|
canvas spec. While the canvas is shown in a pop-over, column, or plugin pane
|
|
@@ -299,17 +311,48 @@ export default definePlugin({
|
|
|
299
311
|
`packages/plugin-sdk/test/fixtures/view-builds-monitor.json` holds a complete
|
|
300
312
|
view that matches the protocol's round-trip test.
|
|
301
313
|
|
|
314
|
+
### Pane header and footer slots
|
|
315
|
+
|
|
316
|
+
A `slot` on `pane.header` or `pane.footer` contributes rows inside the pane
|
|
317
|
+
frame, above or below its terminal body. Declarations use `width: "full"`
|
|
318
|
+
(the default) or `width: "half"`. Contributions stack in ascending plugin
|
|
319
|
+
order, then plugin ID and contribution ID. Full contributions occupy their
|
|
320
|
+
own rows. Consecutive half contributions share a row, with the odd column
|
|
321
|
+
assigned to the left. An unpaired half leaves the right half empty.
|
|
322
|
+
Multiline half contributions share a block as tall as the taller contribution.
|
|
323
|
+
|
|
324
|
+
Rows, styled text, and non-interactive canvas cells render in these slots.
|
|
325
|
+
Each slot is bounded to 32 rows. Small panes clip slot rows while preserving
|
|
326
|
+
terminal space. Slot height participates in the same geometry used for terminal
|
|
327
|
+
painting, pointer input, graphics clipping, and controller-owned resizing.
|
|
328
|
+
Remote viewers receive the same contributions. Missing panes, stale producer
|
|
329
|
+
snapshots, and mismatched pane generations withdraw their rows.
|
|
330
|
+
|
|
331
|
+
Pane-slot actions, input capture, hover popovers, and persisted slot collapse
|
|
332
|
+
remain unconnected. They are separate from the implemented row rendering.
|
|
333
|
+
|
|
334
|
+
`ctx.context.get()` exposes `projects`, `panes`, and `complete` for the owning
|
|
335
|
+
machine. Project records contain `{ entity, name, path }`; pane records contain
|
|
336
|
+
`{ entity, projectId, name, cwd }`. Entities include machine ID and a decimal
|
|
337
|
+
string generation. The pane `cwd` is its configured project launch directory.
|
|
338
|
+
Live shell-directory observations are a separate future capability. Each list
|
|
339
|
+
has at most 128 records, and their combined JSON is bounded to 32 KiB. `complete`
|
|
340
|
+
is false when a local path could not be decoded or a bound omitted records.
|
|
341
|
+
Commands, launch environment, prompts, and encrypted payloads are excluded.
|
|
342
|
+
Plugins must perform filesystem work on their owning machine and clear
|
|
343
|
+
contributions when their target disappears or changes generation.
|
|
344
|
+
|
|
302
345
|
### Current frontend coverage
|
|
303
346
|
|
|
304
|
-
Sections, plugin cards, machine slots, canvas surfaces, host-rendered views,
|
|
347
|
+
Sections, plugin cards, machine slots, stacked pane header/footer slots, canvas surfaces, host-rendered views,
|
|
305
348
|
global command palette entries, and the three
|
|
306
349
|
panel presentations are implemented. A popover is content-sized within the
|
|
307
350
|
modal area. A column attaches to the sidebar and resizes the workspace. A
|
|
308
351
|
plugin pane has a tab and focus, drag, close, and reveal behavior; its width
|
|
309
352
|
follows content and it does not create a tmux pane or resize a terminal.
|
|
310
353
|
|
|
311
|
-
Project slot anchors, standalone badges attached to hierarchy rows,
|
|
312
|
-
|
|
354
|
+
Project slot anchors, standalone badges attached to hierarchy rows,
|
|
355
|
+
menu registrations, key registrations, link registrations,
|
|
313
356
|
and entity-scoped command palette entries have protocol and composition support
|
|
314
357
|
but do not have connected production frontend surfaces. Badge nodes inside a
|
|
315
358
|
view tree are rendered. The `overlay`
|
|
@@ -342,6 +385,18 @@ write and focus, subscription changes, health, and webhook acknowledgements.
|
|
|
342
385
|
Each operation has a typed input and output. `RequestOptions` accepts an abort
|
|
343
386
|
signal and a timeout.
|
|
344
387
|
|
|
388
|
+
`ctx.state` provides account-backed `get(key)`, `set(key, value)`, and
|
|
389
|
+
`keys()` methods. State is isolated by the plugin ID. `keys()` takes no input
|
|
390
|
+
besides optional `RequestOptions` and returns a sorted `string[]` containing
|
|
391
|
+
the plugin's key names. The result is empty when no keys exist, is limited to
|
|
392
|
+
256 keys, and has no prefix or pagination parameters. A key written by one
|
|
393
|
+
machine is visible to the other machines signed into the same account, so a
|
|
394
|
+
plugin can use distinct machine-specific key names without maintaining a
|
|
395
|
+
shared index. Key names are 1 to 128 bytes. The first character is ASCII
|
|
396
|
+
alphanumeric or `_`; later characters are ASCII alphanumeric, `_`, `.`, `:`,
|
|
397
|
+
or `-`. Values remain limited to 64 KiB of JSON. The host applies the same
|
|
398
|
+
account authority and revocation checks to all three methods.
|
|
399
|
+
|
|
345
400
|
The public SDK applies the same capability checks and request bounds to every
|
|
346
401
|
plugin. Plugin IDs do not grant fetch, pane, project, or secret exceptions.
|
|
347
402
|
The current native host connects fetch, secrets, configuration, account state,
|
|
@@ -367,6 +422,9 @@ capabilities govern SDK/host operations and do not provide OS sandboxing.
|
|
|
367
422
|
{
|
|
368
423
|
"accountId": "account-id",
|
|
369
424
|
"machineId": "machine-id",
|
|
425
|
+
"projects": [],
|
|
426
|
+
"panes": [],
|
|
427
|
+
"complete": true,
|
|
370
428
|
"build": {
|
|
371
429
|
"version": "0.14.1-branch.3bd7f23c.98a422",
|
|
372
430
|
"commit": "<40 hex digits>",
|
|
@@ -415,6 +473,21 @@ the daemon does not deliver it when a plugin starts. Read `context.get` at
|
|
|
415
473
|
activation for the initial values. The daemon pushes each change, so a plugin
|
|
416
474
|
needs no schedule to track the followed channel.
|
|
417
475
|
|
|
476
|
+
Plugins with `events` can subscribe to `onEvent('pane-context', handler)`.
|
|
477
|
+
Its `data` replaces the local `machineId`, `projects`, `panes`, and `complete`
|
|
478
|
+
fields from `context.get`. Only a changed local context produces an event.
|
|
479
|
+
Subscribe before the initial `context.get` request and discard that response
|
|
480
|
+
if an event arrives while it is pending. Pane removal and generation changes
|
|
481
|
+
must withdraw publishers for the old entity. Project launch directories are
|
|
482
|
+
configured paths; shell `cd` changes are outside this event's current contract.
|
|
483
|
+
|
|
484
|
+
A plugin can watch these directories with Node's native filesystem events.
|
|
485
|
+
Debounce bursts with a one-shot timer, retain changes that arrive during a read,
|
|
486
|
+
and close watchers on disposal or target removal. Do not schedule directory
|
|
487
|
+
or Git refresh polling. Watch linked-worktree Git metadata and shared refs as
|
|
488
|
+
well as the working tree. Filesystem notification errors should report degraded
|
|
489
|
+
health; they must not silently switch to polling.
|
|
490
|
+
|
|
418
491
|
## Hooks and delivery
|
|
419
492
|
|
|
420
493
|
Hook dispatch and durable webhook delivery are planned host integrations.
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -90,8 +90,8 @@ export interface CanvasSpec {
|
|
|
90
90
|
transparent?: boolean;
|
|
91
91
|
shade?: number;
|
|
92
92
|
captureInput?: boolean;
|
|
93
|
-
/** A bounded one-line string shown while the pointer rests over the canvas. */
|
|
94
|
-
hover?: string;
|
|
93
|
+
/** A bounded one-line string or styled spans shown while the pointer rests over the canvas. */
|
|
94
|
+
hover?: string | TextSpan[];
|
|
95
95
|
}
|
|
96
96
|
export type CanvasContent = { kind: 'canvas'; canvas: CanvasSpec };
|
|
97
97
|
/**
|
|
@@ -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
|
@@ -12,9 +12,39 @@ function boundedText(value, label, limit = 512) {
|
|
|
12
12
|
}
|
|
13
13
|
const HOVER_FORBIDDEN = /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/
|
|
14
14
|
function validateCanvasHover(value) {
|
|
15
|
-
|
|
15
|
+
if (typeof value === 'string') {
|
|
16
|
+
ensure(value.trim().length > 0 && !HOVER_FORBIDDEN.test(value),
|
|
17
|
+
'invalid_payload', 'Invalid canvas hover text')
|
|
18
|
+
ensure(Buffer.byteLength(value) <= LIMITS.canvasHoverBytes,
|
|
19
|
+
'payload_too_large', `Canvas hover text exceeds ${LIMITS.canvasHoverBytes} bytes`)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
ensure(Array.isArray(value), 'invalid_payload', 'Invalid canvas hover spans')
|
|
23
|
+
ensure(value.length > 0, 'invalid_payload', 'Canvas hover spans must contain text')
|
|
24
|
+
ensure(value.length <= 32, 'payload_too_large', 'Canvas hover spans exceed 32 spans')
|
|
25
|
+
let text = ''
|
|
26
|
+
for (const span of value) {
|
|
27
|
+
ensure(object(span), 'invalid_payload', 'Invalid canvas hover span')
|
|
28
|
+
ensure(typeof span.text === 'string' && !HOVER_FORBIDDEN.test(span.text),
|
|
29
|
+
'invalid_payload', 'Invalid canvas hover span text')
|
|
30
|
+
for (const field of ['foreground', 'background']) {
|
|
31
|
+
if (span[field] !== undefined) {
|
|
32
|
+
ensure(typeof span[field] === 'string' && span[field].trim().length > 0 &&
|
|
33
|
+
!HOVER_FORBIDDEN.test(span[field]), 'invalid_payload', `Invalid canvas hover span ${field}`)
|
|
34
|
+
ensure(Buffer.byteLength(span[field]) <= 64,
|
|
35
|
+
'payload_too_large', `Canvas hover span ${field} exceeds 64 bytes`)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const field of ['bold', 'italic', 'underline']) {
|
|
39
|
+
ensure(span[field] === undefined || typeof span[field] === 'boolean',
|
|
40
|
+
'invalid_payload', `Invalid canvas hover span ${field}`)
|
|
41
|
+
}
|
|
42
|
+
ensure(span.actionId === undefined, 'invalid_payload', 'Canvas hover spans cannot contain actionId')
|
|
43
|
+
text += span.text
|
|
44
|
+
}
|
|
45
|
+
ensure(text.trim().length > 0 && !HOVER_FORBIDDEN.test(text),
|
|
16
46
|
'invalid_payload', 'Invalid canvas hover text')
|
|
17
|
-
ensure(Buffer.byteLength(
|
|
47
|
+
ensure(Buffer.byteLength(text) <= LIMITS.canvasHoverBytes,
|
|
18
48
|
'payload_too_large', `Canvas hover text exceeds ${LIMITS.canvasHoverBytes} bytes`)
|
|
19
49
|
}
|
|
20
50
|
function validateEntity(entity) {
|
|
@@ -342,7 +372,8 @@ export function createRuntime({ manifest: input, producer, send, clock = realClo
|
|
|
342
372
|
secrets: Object.freeze({ get: (name, options) => request('secret.get', { name }, options) }),
|
|
343
373
|
config: Object.freeze({ get: options => request('config.get', {}, options) }),
|
|
344
374
|
state: Object.freeze({ get: (key, options) => request('state.get', { key }, options),
|
|
345
|
-
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) }),
|
|
346
377
|
context: Object.freeze({ get: options => request('context.get', {}, options) }),
|
|
347
378
|
popover: Object.freeze({ open: method('popover.open') }),
|
|
348
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
|
}
|