@ryuhq/sdk 0.0.17 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-SKVIJH5I.js → chunk-XTUK5I6I.js} +27 -1
- package/dist/cli.cjs +26 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +37 -5
- package/dist/index.js +12 -5
- package/dist/manifest.cjs +28 -1
- package/dist/manifest.d.cts +40 -2
- package/dist/manifest.d.ts +40 -2
- package/dist/manifest.js +3 -1
- package/package.json +2 -2
- package/src/generated/plugin-manifest.ts +265 -4
- package/src/manifest.ts +52 -0
- package/src/runnable/app.ts +4 -0
- package/src/runnable/turn-hook.ts +6 -3
|
@@ -110,6 +110,14 @@ var HookEventContributionSchema = z.object({
|
|
|
110
110
|
/** Example of the `ctx.event` payload. Documentation, not a validated schema. */
|
|
111
111
|
payload_example: z.record(z.string(), z.unknown()).optional()
|
|
112
112
|
});
|
|
113
|
+
var PiExtensionContributionSchema = z.object({
|
|
114
|
+
/** Stable id for this extension within the plugin (`[a-z0-9][a-z0-9._-]*`). */
|
|
115
|
+
id: z.string().min(1),
|
|
116
|
+
/** Path to the source, relative to the plugin root: `pi-extensions/<name>.ts`. */
|
|
117
|
+
file: z.string().min(1),
|
|
118
|
+
/** Optional one-liner describing what the extension adds to the agent. */
|
|
119
|
+
description: z.string().optional()
|
|
120
|
+
});
|
|
113
121
|
var DEFAULT_WIDGET_MIME = "text/html+skybridge";
|
|
114
122
|
var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
|
|
115
123
|
var WidgetContributionSchema = z.object({
|
|
@@ -171,6 +179,14 @@ var ContributesSchema = z.object({
|
|
|
171
179
|
* Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
|
|
172
180
|
* strip the dock panel an app declares here. */
|
|
173
181
|
dock_panels: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
182
|
+
/** Deletable data categories the app owns — one "Delete all X" row in Settings
|
|
183
|
+
* → Danger Zone. Mirrors the Rust-side `Contributes.data_categories`; without
|
|
184
|
+
* it the CLI's zod parse would strip the declaration before signing, and the
|
|
185
|
+
* app's danger-zone row would simply never appear on any node that installed
|
|
186
|
+
* the packed bundle. Loosely typed here for the same reason as the surfaces
|
|
187
|
+
* above — Core is the layer that types it, because Core is the layer that has
|
|
188
|
+
* to resolve the id to something that can actually delete the rows. */
|
|
189
|
+
data_categories: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
174
190
|
/** Language servers the plugin declares, keyed by server name — the mirror of
|
|
175
191
|
* Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
|
|
176
192
|
* loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
|
|
@@ -183,7 +199,16 @@ var ContributesSchema = z.object({
|
|
|
183
199
|
* on its way through `ryu pack` — the same silent-deletion bug this field
|
|
184
200
|
* exists to fix, one level down. Core is the layer that types it, because Core
|
|
185
201
|
* is the layer that acts on it. */
|
|
186
|
-
lsp_servers: z.record(z.string(), z.record(z.string(), z.unknown())).default({})
|
|
202
|
+
lsp_servers: z.record(z.string(), z.record(z.string(), z.unknown())).default({}),
|
|
203
|
+
/** Pi extensions the plugin ships — TypeScript the managed `ryu` (Pi) agent
|
|
204
|
+
* loads at process start. Mirrors the Rust-side `Contributes.pi_extensions`;
|
|
205
|
+
* without it the CLI's zod parse would strip the declaration before signing,
|
|
206
|
+
* and the packed plugin would ship a `pi-extensions/` folder nothing loads.
|
|
207
|
+
*
|
|
208
|
+
* Typed (not a loose record) because Ryu owns this vocabulary — three fields,
|
|
209
|
+
* all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
|
|
210
|
+
* Claude Code's to extend. */
|
|
211
|
+
pi_extensions: z.array(PiExtensionContributionSchema).default([])
|
|
187
212
|
});
|
|
188
213
|
var SetupStepSchema = z.object({
|
|
189
214
|
/** Card heading (e.g. the companion app name). */
|
|
@@ -420,6 +445,7 @@ export {
|
|
|
420
445
|
CompanionSurfaceSchema,
|
|
421
446
|
TurnHookContributionSchema,
|
|
422
447
|
HookEventContributionSchema,
|
|
448
|
+
PiExtensionContributionSchema,
|
|
423
449
|
WidgetContributionSchema,
|
|
424
450
|
ToolAppConfigSchema,
|
|
425
451
|
ContributesSchema,
|
package/dist/cli.cjs
CHANGED
|
@@ -396,6 +396,14 @@ var HookEventContributionSchema = import_zod.z.object({
|
|
|
396
396
|
/** Example of the `ctx.event` payload. Documentation, not a validated schema. */
|
|
397
397
|
payload_example: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
398
398
|
});
|
|
399
|
+
var PiExtensionContributionSchema = import_zod.z.object({
|
|
400
|
+
/** Stable id for this extension within the plugin (`[a-z0-9][a-z0-9._-]*`). */
|
|
401
|
+
id: import_zod.z.string().min(1),
|
|
402
|
+
/** Path to the source, relative to the plugin root: `pi-extensions/<name>.ts`. */
|
|
403
|
+
file: import_zod.z.string().min(1),
|
|
404
|
+
/** Optional one-liner describing what the extension adds to the agent. */
|
|
405
|
+
description: import_zod.z.string().optional()
|
|
406
|
+
});
|
|
399
407
|
var DEFAULT_WIDGET_MIME = "text/html+skybridge";
|
|
400
408
|
var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
|
|
401
409
|
var WidgetContributionSchema = import_zod.z.object({
|
|
@@ -457,6 +465,14 @@ var ContributesSchema = import_zod.z.object({
|
|
|
457
465
|
* Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
|
|
458
466
|
* strip the dock panel an app declares here. */
|
|
459
467
|
dock_panels: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
468
|
+
/** Deletable data categories the app owns — one "Delete all X" row in Settings
|
|
469
|
+
* → Danger Zone. Mirrors the Rust-side `Contributes.data_categories`; without
|
|
470
|
+
* it the CLI's zod parse would strip the declaration before signing, and the
|
|
471
|
+
* app's danger-zone row would simply never appear on any node that installed
|
|
472
|
+
* the packed bundle. Loosely typed here for the same reason as the surfaces
|
|
473
|
+
* above — Core is the layer that types it, because Core is the layer that has
|
|
474
|
+
* to resolve the id to something that can actually delete the rows. */
|
|
475
|
+
data_categories: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
460
476
|
/** Language servers the plugin declares, keyed by server name — the mirror of
|
|
461
477
|
* Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
|
|
462
478
|
* loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
|
|
@@ -469,7 +485,16 @@ var ContributesSchema = import_zod.z.object({
|
|
|
469
485
|
* on its way through `ryu pack` — the same silent-deletion bug this field
|
|
470
486
|
* exists to fix, one level down. Core is the layer that types it, because Core
|
|
471
487
|
* is the layer that acts on it. */
|
|
472
|
-
lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({})
|
|
488
|
+
lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({}),
|
|
489
|
+
/** Pi extensions the plugin ships — TypeScript the managed `ryu` (Pi) agent
|
|
490
|
+
* loads at process start. Mirrors the Rust-side `Contributes.pi_extensions`;
|
|
491
|
+
* without it the CLI's zod parse would strip the declaration before signing,
|
|
492
|
+
* and the packed plugin would ship a `pi-extensions/` folder nothing loads.
|
|
493
|
+
*
|
|
494
|
+
* Typed (not a loose record) because Ryu owns this vocabulary — three fields,
|
|
495
|
+
* all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
|
|
496
|
+
* Claude Code's to extend. */
|
|
497
|
+
pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([])
|
|
473
498
|
});
|
|
474
499
|
var SetupStepSchema = import_zod.z.object({
|
|
475
500
|
/** Card heading (e.g. the companion app name). */
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -940,6 +940,14 @@ var HookEventContributionSchema = import_zod.z.object({
|
|
|
940
940
|
/** Example of the `ctx.event` payload. Documentation, not a validated schema. */
|
|
941
941
|
payload_example: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
942
942
|
});
|
|
943
|
+
var PiExtensionContributionSchema = import_zod.z.object({
|
|
944
|
+
/** Stable id for this extension within the plugin (`[a-z0-9][a-z0-9._-]*`). */
|
|
945
|
+
id: import_zod.z.string().min(1),
|
|
946
|
+
/** Path to the source, relative to the plugin root: `pi-extensions/<name>.ts`. */
|
|
947
|
+
file: import_zod.z.string().min(1),
|
|
948
|
+
/** Optional one-liner describing what the extension adds to the agent. */
|
|
949
|
+
description: import_zod.z.string().optional()
|
|
950
|
+
});
|
|
943
951
|
var DEFAULT_WIDGET_MIME = "text/html+skybridge";
|
|
944
952
|
var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
|
|
945
953
|
var WidgetContributionSchema = import_zod.z.object({
|
|
@@ -1001,6 +1009,14 @@ var ContributesSchema = import_zod.z.object({
|
|
|
1001
1009
|
* Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
|
|
1002
1010
|
* strip the dock panel an app declares here. */
|
|
1003
1011
|
dock_panels: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
1012
|
+
/** Deletable data categories the app owns — one "Delete all X" row in Settings
|
|
1013
|
+
* → Danger Zone. Mirrors the Rust-side `Contributes.data_categories`; without
|
|
1014
|
+
* it the CLI's zod parse would strip the declaration before signing, and the
|
|
1015
|
+
* app's danger-zone row would simply never appear on any node that installed
|
|
1016
|
+
* the packed bundle. Loosely typed here for the same reason as the surfaces
|
|
1017
|
+
* above — Core is the layer that types it, because Core is the layer that has
|
|
1018
|
+
* to resolve the id to something that can actually delete the rows. */
|
|
1019
|
+
data_categories: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
1004
1020
|
/** Language servers the plugin declares, keyed by server name — the mirror of
|
|
1005
1021
|
* Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
|
|
1006
1022
|
* loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
|
|
@@ -1013,7 +1029,16 @@ var ContributesSchema = import_zod.z.object({
|
|
|
1013
1029
|
* on its way through `ryu pack` — the same silent-deletion bug this field
|
|
1014
1030
|
* exists to fix, one level down. Core is the layer that types it, because Core
|
|
1015
1031
|
* is the layer that acts on it. */
|
|
1016
|
-
lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({})
|
|
1032
|
+
lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({}),
|
|
1033
|
+
/** Pi extensions the plugin ships — TypeScript the managed `ryu` (Pi) agent
|
|
1034
|
+
* loads at process start. Mirrors the Rust-side `Contributes.pi_extensions`;
|
|
1035
|
+
* without it the CLI's zod parse would strip the declaration before signing,
|
|
1036
|
+
* and the packed plugin would ship a `pi-extensions/` folder nothing loads.
|
|
1037
|
+
*
|
|
1038
|
+
* Typed (not a loose record) because Ryu owns this vocabulary — three fields,
|
|
1039
|
+
* all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
|
|
1040
|
+
* Claude Code's to extend. */
|
|
1041
|
+
pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([])
|
|
1017
1042
|
});
|
|
1018
1043
|
var SetupStepSchema = import_zod.z.object({
|
|
1019
1044
|
/** Card heading (e.g. the companion app name). */
|
|
@@ -1310,6 +1335,10 @@ function defineApp(options) {
|
|
|
1310
1335
|
// takes no `contributes` passthrough. An app that wants to declare language
|
|
1311
1336
|
// servers writes them in a hand-authored `manifest.json`.
|
|
1312
1337
|
lsp_servers: {},
|
|
1338
|
+
// Same reason again: a danger-zone category and a Pi extension are both
|
|
1339
|
+
// hand-authored declarations, not something derivable from runnables.
|
|
1340
|
+
data_categories: [],
|
|
1341
|
+
pi_extensions: [],
|
|
1313
1342
|
widgets
|
|
1314
1343
|
};
|
|
1315
1344
|
const raw = {
|
|
@@ -1929,13 +1958,16 @@ function definePlugin(options) {
|
|
|
1929
1958
|
settings_tabs: options.settingsTabs ?? [],
|
|
1930
1959
|
slash_commands: options.slashCommands ?? [],
|
|
1931
1960
|
lsp_servers: options.lspServers ?? {},
|
|
1932
|
-
// A turn-hook plugin contributes no app widgets, sidebar entries
|
|
1933
|
-
// panels
|
|
1934
|
-
// defaults applied), so set them
|
|
1961
|
+
// A turn-hook plugin contributes no app widgets, sidebar entries, dock
|
|
1962
|
+
// panels, danger-zone categories or Pi extensions; the fields are required
|
|
1963
|
+
// on the resolved `Contributes` type (zod defaults applied), so set them
|
|
1964
|
+
// explicitly.
|
|
1935
1965
|
widgets: [],
|
|
1936
1966
|
sidebar_sections: [],
|
|
1937
1967
|
sidebar_buttons: [],
|
|
1938
|
-
dock_panels: []
|
|
1968
|
+
dock_panels: [],
|
|
1969
|
+
data_categories: [],
|
|
1970
|
+
pi_extensions: []
|
|
1939
1971
|
};
|
|
1940
1972
|
const tools = options.tools ?? [];
|
|
1941
1973
|
const runnables = tools.map((t) => inlineToolRunnable(t));
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
coreManifestJsonSchema,
|
|
13
13
|
validateManifestStrict,
|
|
14
14
|
validatePluginId
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XTUK5I6I.js";
|
|
16
16
|
import {
|
|
17
17
|
Agent,
|
|
18
18
|
PRIMITIVE_BINDINGS,
|
|
@@ -98,6 +98,10 @@ function defineApp(options) {
|
|
|
98
98
|
// takes no `contributes` passthrough. An app that wants to declare language
|
|
99
99
|
// servers writes them in a hand-authored `manifest.json`.
|
|
100
100
|
lsp_servers: {},
|
|
101
|
+
// Same reason again: a danger-zone category and a Pi extension are both
|
|
102
|
+
// hand-authored declarations, not something derivable from runnables.
|
|
103
|
+
data_categories: [],
|
|
104
|
+
pi_extensions: [],
|
|
101
105
|
widgets
|
|
102
106
|
};
|
|
103
107
|
const raw = {
|
|
@@ -717,13 +721,16 @@ function definePlugin(options) {
|
|
|
717
721
|
settings_tabs: options.settingsTabs ?? [],
|
|
718
722
|
slash_commands: options.slashCommands ?? [],
|
|
719
723
|
lsp_servers: options.lspServers ?? {},
|
|
720
|
-
// A turn-hook plugin contributes no app widgets, sidebar entries
|
|
721
|
-
// panels
|
|
722
|
-
// defaults applied), so set them
|
|
724
|
+
// A turn-hook plugin contributes no app widgets, sidebar entries, dock
|
|
725
|
+
// panels, danger-zone categories or Pi extensions; the fields are required
|
|
726
|
+
// on the resolved `Contributes` type (zod defaults applied), so set them
|
|
727
|
+
// explicitly.
|
|
723
728
|
widgets: [],
|
|
724
729
|
sidebar_sections: [],
|
|
725
730
|
sidebar_buttons: [],
|
|
726
|
-
dock_panels: []
|
|
731
|
+
dock_panels: [],
|
|
732
|
+
data_categories: [],
|
|
733
|
+
pi_extensions: []
|
|
727
734
|
};
|
|
728
735
|
const tools = options.tools ?? [];
|
|
729
736
|
const runnables = tools.map((t) => inlineToolRunnable(t));
|
package/dist/manifest.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(manifest_exports, {
|
|
|
25
25
|
CompanionSurfaceSchema: () => CompanionSurfaceSchema,
|
|
26
26
|
ContributesSchema: () => ContributesSchema,
|
|
27
27
|
HookEventContributionSchema: () => HookEventContributionSchema,
|
|
28
|
+
PiExtensionContributionSchema: () => PiExtensionContributionSchema,
|
|
28
29
|
PluginManifestSchema: () => PluginManifestSchema,
|
|
29
30
|
RequiresSchema: () => RequiresSchema,
|
|
30
31
|
RunnableKindSchema: () => RunnableKindSchema,
|
|
@@ -157,6 +158,14 @@ var HookEventContributionSchema = import_zod.z.object({
|
|
|
157
158
|
/** Example of the `ctx.event` payload. Documentation, not a validated schema. */
|
|
158
159
|
payload_example: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
159
160
|
});
|
|
161
|
+
var PiExtensionContributionSchema = import_zod.z.object({
|
|
162
|
+
/** Stable id for this extension within the plugin (`[a-z0-9][a-z0-9._-]*`). */
|
|
163
|
+
id: import_zod.z.string().min(1),
|
|
164
|
+
/** Path to the source, relative to the plugin root: `pi-extensions/<name>.ts`. */
|
|
165
|
+
file: import_zod.z.string().min(1),
|
|
166
|
+
/** Optional one-liner describing what the extension adds to the agent. */
|
|
167
|
+
description: import_zod.z.string().optional()
|
|
168
|
+
});
|
|
160
169
|
var DEFAULT_WIDGET_MIME = "text/html+skybridge";
|
|
161
170
|
var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
|
|
162
171
|
var WidgetContributionSchema = import_zod.z.object({
|
|
@@ -218,6 +227,14 @@ var ContributesSchema = import_zod.z.object({
|
|
|
218
227
|
* Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
|
|
219
228
|
* strip the dock panel an app declares here. */
|
|
220
229
|
dock_panels: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
230
|
+
/** Deletable data categories the app owns — one "Delete all X" row in Settings
|
|
231
|
+
* → Danger Zone. Mirrors the Rust-side `Contributes.data_categories`; without
|
|
232
|
+
* it the CLI's zod parse would strip the declaration before signing, and the
|
|
233
|
+
* app's danger-zone row would simply never appear on any node that installed
|
|
234
|
+
* the packed bundle. Loosely typed here for the same reason as the surfaces
|
|
235
|
+
* above — Core is the layer that types it, because Core is the layer that has
|
|
236
|
+
* to resolve the id to something that can actually delete the rows. */
|
|
237
|
+
data_categories: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
221
238
|
/** Language servers the plugin declares, keyed by server name — the mirror of
|
|
222
239
|
* Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
|
|
223
240
|
* loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
|
|
@@ -230,7 +247,16 @@ var ContributesSchema = import_zod.z.object({
|
|
|
230
247
|
* on its way through `ryu pack` — the same silent-deletion bug this field
|
|
231
248
|
* exists to fix, one level down. Core is the layer that types it, because Core
|
|
232
249
|
* is the layer that acts on it. */
|
|
233
|
-
lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({})
|
|
250
|
+
lsp_servers: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default({}),
|
|
251
|
+
/** Pi extensions the plugin ships — TypeScript the managed `ryu` (Pi) agent
|
|
252
|
+
* loads at process start. Mirrors the Rust-side `Contributes.pi_extensions`;
|
|
253
|
+
* without it the CLI's zod parse would strip the declaration before signing,
|
|
254
|
+
* and the packed plugin would ship a `pi-extensions/` folder nothing loads.
|
|
255
|
+
*
|
|
256
|
+
* Typed (not a loose record) because Ryu owns this vocabulary — three fields,
|
|
257
|
+
* all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
|
|
258
|
+
* Claude Code's to extend. */
|
|
259
|
+
pi_extensions: import_zod.z.array(PiExtensionContributionSchema).default([])
|
|
234
260
|
});
|
|
235
261
|
var SetupStepSchema = import_zod.z.object({
|
|
236
262
|
/** Card heading (e.g. the companion app name). */
|
|
@@ -466,6 +492,7 @@ function coreManifestJsonSchema() {
|
|
|
466
492
|
CompanionSurfaceSchema,
|
|
467
493
|
ContributesSchema,
|
|
468
494
|
HookEventContributionSchema,
|
|
495
|
+
PiExtensionContributionSchema,
|
|
469
496
|
PluginManifestSchema,
|
|
470
497
|
RequiresSchema,
|
|
471
498
|
RunnableKindSchema,
|
package/dist/manifest.d.cts
CHANGED
|
@@ -115,6 +115,32 @@ declare const HookEventContributionSchema: z.ZodObject<{
|
|
|
115
115
|
payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
116
116
|
}, z.core.$strip>;
|
|
117
117
|
type HookEventContribution = z.infer<typeof HookEventContributionSchema>;
|
|
118
|
+
/**
|
|
119
|
+
* One Pi extension the plugin ships — a TypeScript file the managed `ryu` (Pi)
|
|
120
|
+
* agent loads at process start. Mirrors Rust `PiExtensionContribution`.
|
|
121
|
+
*
|
|
122
|
+
* Carries a PATH, never a body: unlike `turn_hooks` there is no inline `code`
|
|
123
|
+
* twin, because nothing downstream reads the source as a string.
|
|
124
|
+
*
|
|
125
|
+
* That makes it a SIDECAR FILE, and `ryu pack` emits a single JSON bundle — so a
|
|
126
|
+
* plugin installed from a packed bundle arrives without its `pi-extensions/`
|
|
127
|
+
* directory and Core resolves the declaration to a visible skip. Same open gap as
|
|
128
|
+
* `skills/**`, which the bundle likewise does not carry. Today the path that works
|
|
129
|
+
* is a plugin whose directory is on disk (a built-in, a satellite checkout, a dev
|
|
130
|
+
* tree). Do not "fix" this by inlining the source into the manifest: a 50 KB
|
|
131
|
+
* TypeScript program escaped into a JSON string is the unauditable form the whole
|
|
132
|
+
* `code_file` extraction exists to prevent.
|
|
133
|
+
*
|
|
134
|
+
* Note this is UNSANDBOXED code: it runs inside the agent process with full host
|
|
135
|
+
* privilege, so Core gates it behind the operator-only `pi:extension` grant for
|
|
136
|
+
* any non-built-in plugin.
|
|
137
|
+
*/
|
|
138
|
+
declare const PiExtensionContributionSchema: z.ZodObject<{
|
|
139
|
+
id: z.ZodString;
|
|
140
|
+
file: z.ZodString;
|
|
141
|
+
description: z.ZodOptional<z.ZodString>;
|
|
142
|
+
}, z.core.$strip>;
|
|
143
|
+
type PiExtensionContribution = z.infer<typeof PiExtensionContributionSchema>;
|
|
118
144
|
/**
|
|
119
145
|
* One app-widget contribution (Ryu Apps). Binds the render tool that produces the
|
|
120
146
|
* widget to its `ui://widget/<slug>.html` template. Shape-identical to Core's
|
|
@@ -187,7 +213,13 @@ declare const ContributesSchema: z.ZodObject<{
|
|
|
187
213
|
sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
188
214
|
sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
189
215
|
dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
216
|
+
data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
190
217
|
lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
218
|
+
pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
219
|
+
id: z.ZodString;
|
|
220
|
+
file: z.ZodString;
|
|
221
|
+
description: z.ZodOptional<z.ZodString>;
|
|
222
|
+
}, z.core.$strip>>>;
|
|
191
223
|
}, z.core.$strip>;
|
|
192
224
|
type Contributes = z.infer<typeof ContributesSchema>;
|
|
193
225
|
/**
|
|
@@ -338,7 +370,13 @@ declare const PluginManifestSchema: z.ZodObject<{
|
|
|
338
370
|
sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
339
371
|
sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
340
372
|
dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
373
|
+
data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
341
374
|
lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
375
|
+
pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
376
|
+
id: z.ZodString;
|
|
377
|
+
file: z.ZodString;
|
|
378
|
+
description: z.ZodOptional<z.ZodString>;
|
|
379
|
+
}, z.core.$strip>>>;
|
|
342
380
|
}, z.core.$strip>>;
|
|
343
381
|
requires: z.ZodOptional<z.ZodObject<{
|
|
344
382
|
apps: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -365,8 +403,8 @@ declare const PluginManifestSchema: z.ZodObject<{
|
|
|
365
403
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
366
404
|
rule: z.ZodOptional<z.ZodObject<{
|
|
367
405
|
type: z.ZodEnum<{
|
|
368
|
-
flat: "flat";
|
|
369
406
|
percent: "percent";
|
|
407
|
+
flat: "flat";
|
|
370
408
|
}>;
|
|
371
409
|
value: z.ZodNumber;
|
|
372
410
|
recurring: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -435,4 +473,4 @@ declare function validateManifestStrict(manifestJson: string): string;
|
|
|
435
473
|
*/
|
|
436
474
|
declare function coreManifestJsonSchema(): unknown;
|
|
437
475
|
|
|
438
|
-
export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
|
|
476
|
+
export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type PiExtensionContribution, PiExtensionContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
|
package/dist/manifest.d.ts
CHANGED
|
@@ -115,6 +115,32 @@ declare const HookEventContributionSchema: z.ZodObject<{
|
|
|
115
115
|
payload_example: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
116
116
|
}, z.core.$strip>;
|
|
117
117
|
type HookEventContribution = z.infer<typeof HookEventContributionSchema>;
|
|
118
|
+
/**
|
|
119
|
+
* One Pi extension the plugin ships — a TypeScript file the managed `ryu` (Pi)
|
|
120
|
+
* agent loads at process start. Mirrors Rust `PiExtensionContribution`.
|
|
121
|
+
*
|
|
122
|
+
* Carries a PATH, never a body: unlike `turn_hooks` there is no inline `code`
|
|
123
|
+
* twin, because nothing downstream reads the source as a string.
|
|
124
|
+
*
|
|
125
|
+
* That makes it a SIDECAR FILE, and `ryu pack` emits a single JSON bundle — so a
|
|
126
|
+
* plugin installed from a packed bundle arrives without its `pi-extensions/`
|
|
127
|
+
* directory and Core resolves the declaration to a visible skip. Same open gap as
|
|
128
|
+
* `skills/**`, which the bundle likewise does not carry. Today the path that works
|
|
129
|
+
* is a plugin whose directory is on disk (a built-in, a satellite checkout, a dev
|
|
130
|
+
* tree). Do not "fix" this by inlining the source into the manifest: a 50 KB
|
|
131
|
+
* TypeScript program escaped into a JSON string is the unauditable form the whole
|
|
132
|
+
* `code_file` extraction exists to prevent.
|
|
133
|
+
*
|
|
134
|
+
* Note this is UNSANDBOXED code: it runs inside the agent process with full host
|
|
135
|
+
* privilege, so Core gates it behind the operator-only `pi:extension` grant for
|
|
136
|
+
* any non-built-in plugin.
|
|
137
|
+
*/
|
|
138
|
+
declare const PiExtensionContributionSchema: z.ZodObject<{
|
|
139
|
+
id: z.ZodString;
|
|
140
|
+
file: z.ZodString;
|
|
141
|
+
description: z.ZodOptional<z.ZodString>;
|
|
142
|
+
}, z.core.$strip>;
|
|
143
|
+
type PiExtensionContribution = z.infer<typeof PiExtensionContributionSchema>;
|
|
118
144
|
/**
|
|
119
145
|
* One app-widget contribution (Ryu Apps). Binds the render tool that produces the
|
|
120
146
|
* widget to its `ui://widget/<slug>.html` template. Shape-identical to Core's
|
|
@@ -187,7 +213,13 @@ declare const ContributesSchema: z.ZodObject<{
|
|
|
187
213
|
sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
188
214
|
sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
189
215
|
dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
216
|
+
data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
190
217
|
lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
218
|
+
pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
219
|
+
id: z.ZodString;
|
|
220
|
+
file: z.ZodString;
|
|
221
|
+
description: z.ZodOptional<z.ZodString>;
|
|
222
|
+
}, z.core.$strip>>>;
|
|
191
223
|
}, z.core.$strip>;
|
|
192
224
|
type Contributes = z.infer<typeof ContributesSchema>;
|
|
193
225
|
/**
|
|
@@ -338,7 +370,13 @@ declare const PluginManifestSchema: z.ZodObject<{
|
|
|
338
370
|
sidebar_sections: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
339
371
|
sidebar_buttons: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
340
372
|
dock_panels: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
373
|
+
data_categories: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
341
374
|
lsp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
375
|
+
pi_extensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
376
|
+
id: z.ZodString;
|
|
377
|
+
file: z.ZodString;
|
|
378
|
+
description: z.ZodOptional<z.ZodString>;
|
|
379
|
+
}, z.core.$strip>>>;
|
|
342
380
|
}, z.core.$strip>>;
|
|
343
381
|
requires: z.ZodOptional<z.ZodObject<{
|
|
344
382
|
apps: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -365,8 +403,8 @@ declare const PluginManifestSchema: z.ZodObject<{
|
|
|
365
403
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
366
404
|
rule: z.ZodOptional<z.ZodObject<{
|
|
367
405
|
type: z.ZodEnum<{
|
|
368
|
-
flat: "flat";
|
|
369
406
|
percent: "percent";
|
|
407
|
+
flat: "flat";
|
|
370
408
|
}>;
|
|
371
409
|
value: z.ZodNumber;
|
|
372
410
|
recurring: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -435,4 +473,4 @@ declare function validateManifestStrict(manifestJson: string): string;
|
|
|
435
473
|
*/
|
|
436
474
|
declare function coreManifestJsonSchema(): unknown;
|
|
437
475
|
|
|
438
|
-
export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
|
|
476
|
+
export { type AppDependency, AppDependencySchema, type CapabilityReq, CapabilityReqSchema, type CompanionSurface, CompanionSurfaceSchema, type Contributes, ContributesSchema, type HookEventContribution, HookEventContributionSchema, type PiExtensionContribution, PiExtensionContributionSchema, type PluginManifest, PluginManifestSchema, type Requires, RequiresSchema, type RunnableKind, RunnableKindSchema, type RunnableMeta, RunnableMetaSchema, type SetupStep, SetupStepSchema, type Surface, SurfaceSchema, type ToolAppConfig, ToolAppConfigSchema, type TurnHookContribution, TurnHookContributionSchema, type WidgetContribution, WidgetContributionSchema, coreManifestJsonSchema, labelImpersonatesSystemChrome, validateManifestStrict, validatePluginId };
|
package/dist/manifest.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CompanionSurfaceSchema,
|
|
5
5
|
ContributesSchema,
|
|
6
6
|
HookEventContributionSchema,
|
|
7
|
+
PiExtensionContributionSchema,
|
|
7
8
|
PluginManifestSchema,
|
|
8
9
|
RequiresSchema,
|
|
9
10
|
RunnableKindSchema,
|
|
@@ -17,13 +18,14 @@ import {
|
|
|
17
18
|
labelImpersonatesSystemChrome,
|
|
18
19
|
validateManifestStrict,
|
|
19
20
|
validatePluginId
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-XTUK5I6I.js";
|
|
21
22
|
export {
|
|
22
23
|
AppDependencySchema,
|
|
23
24
|
CapabilityReqSchema,
|
|
24
25
|
CompanionSurfaceSchema,
|
|
25
26
|
ContributesSchema,
|
|
26
27
|
HookEventContributionSchema,
|
|
28
|
+
PiExtensionContributionSchema,
|
|
27
29
|
PluginManifestSchema,
|
|
28
30
|
RequiresSchema,
|
|
29
31
|
RunnableKindSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuhq/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ryu developer SDK: typed builders and CLI for authoring manifest.json Plugin bundles",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"clean": "rm -rf dist"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@ryuhq/sdk-native": "0.1.
|
|
47
|
+
"@ryuhq/sdk-native": "0.1.3",
|
|
48
48
|
"zod": "^4.1.13"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
@@ -49,7 +49,7 @@ export type ProviderTarget = "local-machine" | "remote-desktop";
|
|
|
49
49
|
* An **empty/absent** `targets` list means the plugin runs on *every* surface —
|
|
50
50
|
* that is the backward-compatible default and MUST NOT be read as "hidden".
|
|
51
51
|
*/
|
|
52
|
-
export type Surface = "gateway" | "core" | "desktop" | "island" | "mobile" | "extension" | "web" | "cli";
|
|
52
|
+
export type Surface = "gateway" | "core" | "desktop" | "island" | "mobile" | "extension" | "web" | "cli" | "unknown";
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* An installable Ryu App manifest (`manifest.json`).
|
|
@@ -226,8 +226,48 @@ export interface PluginManifest {
|
|
|
226
226
|
* Permission grants this app declares it needs (e.g. `"mcp:web_search"`).
|
|
227
227
|
* These are *declarations only* at this layer — no enforcement happens here;
|
|
228
228
|
* the Gateway owns grant enforcement.
|
|
229
|
+
*
|
|
230
|
+
* This is the **app→host** lane and has nothing to do with
|
|
231
|
+
* [`permission_levels`], the **app→human** lane. See that field's doc comment
|
|
232
|
+
* for the three-way table; conflating the two is the likeliest future bug here.
|
|
233
|
+
*
|
|
234
|
+
* [`permission_levels`]: PluginManifest::permission_levels
|
|
229
235
|
*/
|
|
230
236
|
permission_grants?: string[];
|
|
237
|
+
/**
|
|
238
|
+
* **The user-facing permission vocabulary this app declares** — the set of
|
|
239
|
+
* levels ("read", "edit", …) an administrator can later grant to a person or a
|
|
240
|
+
* team *inside* this app. Absent/empty = the app declares no vocabulary, which
|
|
241
|
+
* is every manifest predating this field.
|
|
242
|
+
*
|
|
243
|
+
* Spaces declaring `read` and `edit` is what makes "team X may edit in Spaces"
|
|
244
|
+
* expressible at all: a grant has to name a level, and a UI has to render a
|
|
245
|
+
* list of them. Without a declaration there is nothing to bind to.
|
|
246
|
+
*
|
|
247
|
+
* # Three lanes, one prefix — do not conflate them
|
|
248
|
+
*
|
|
249
|
+
* | field | direction | who decides | what it means |
|
|
250
|
+
* |---|---|---|---|
|
|
251
|
+
* | [`permission_grants`] | app → host | the **Gateway**, at install/enable | which host capabilities the app may *ask* for |
|
|
252
|
+
* | [`permissions`] ([`PermissionSet`]) | app → sandbox | **Core**, at spawn/exec | what the app's code may *touch* (FS paths, hosts, subprocess) |
|
|
253
|
+
* | `permission_levels` | app → human | an **admin**, per person/team | what a *person* may do inside the app |
|
|
254
|
+
*
|
|
255
|
+
* Only the first two are enforced today. This field is **declaration only**:
|
|
256
|
+
* nothing consumes it yet, so declaring `edit` gates nothing by itself. It is
|
|
257
|
+
* the vocabulary the ACL layer will bind grants against.
|
|
258
|
+
*
|
|
259
|
+
* # Ordering and implication
|
|
260
|
+
*
|
|
261
|
+
* Declaration order is display order — render the list as written. Strength is
|
|
262
|
+
* expressed with [`PermissionLevel::implies`] rather than a separate rank, so
|
|
263
|
+
* there is exactly one ordering and it cannot contradict itself: `edit` implying
|
|
264
|
+
* `read` means granting `edit` already conveys `read`, and no admin has to grant
|
|
265
|
+
* the same person both.
|
|
266
|
+
*
|
|
267
|
+
* [`permission_grants`]: PluginManifest::permission_grants
|
|
268
|
+
* [`permissions`]: PluginManifest::permissions
|
|
269
|
+
*/
|
|
270
|
+
permission_levels?: PermissionLevel[];
|
|
231
271
|
/**
|
|
232
272
|
* **Unified, deny-by-default runtime permission set** — the single typed
|
|
233
273
|
* grammar (`{fs, child_process, network, tool}`) Core lowers to every sandbox
|
|
@@ -236,17 +276,21 @@ export interface PluginManifest {
|
|
|
236
276
|
* predating this field), so an app that declares nothing keeps today's exact
|
|
237
277
|
* zero-permission sandbox posture.
|
|
238
278
|
*
|
|
239
|
-
* # Relationship to [`permission_grants`]
|
|
279
|
+
* # Relationship to [`permission_grants`] and [`permission_levels`]
|
|
240
280
|
*
|
|
241
|
-
* These are **
|
|
281
|
+
* These are **three distinct lanes** that must not be conflated:
|
|
242
282
|
* - [`permission_grants`] are opaque strings the **Gateway** approves at
|
|
243
283
|
* install/enable time — the *approval* lane (who is allowed to ask).
|
|
244
284
|
* - `permissions` is the typed set **Core** lowers into the actual sandbox at
|
|
245
285
|
* spawn/exec time — the *runtime-enforcement* lane (what the code can touch).
|
|
286
|
+
* - [`permission_levels`] is the app's *user-facing* vocabulary an admin grants
|
|
287
|
+
* to a person or team — it never reaches the sandbox at all.
|
|
246
288
|
*
|
|
247
289
|
* A grant says "this app may use the filesystem capability"; `permissions.fs`
|
|
248
290
|
* says "…and here are the exact read/write paths the sandbox is opened with."
|
|
249
291
|
*
|
|
292
|
+
* [`permission_levels`]: PluginManifest::permission_levels
|
|
293
|
+
*
|
|
250
294
|
* # Altitude (manifest-level, per-runnable override is a followup)
|
|
251
295
|
*
|
|
252
296
|
* Declared at the manifest root because **both** current enforcement sites
|
|
@@ -487,6 +531,40 @@ export interface Contributes {
|
|
|
487
531
|
* older shells" instead of breaking the composer.
|
|
488
532
|
*/
|
|
489
533
|
composer_controls?: unknown[];
|
|
534
|
+
/**
|
|
535
|
+
* **Deletable data categories** the app owns — one "Delete all X" row in
|
|
536
|
+
* Settings → Danger Zone (see [`DataCategoryContribution`]).
|
|
537
|
+
*
|
|
538
|
+
* The danger zone used to be two hardcoded lists that had to be edited
|
|
539
|
+
* together: a `DataCategory` enum in Core and a `CATEGORIES` array carrying the
|
|
540
|
+
* user-facing copy in the closed desktop source. Monitors and Meetings are
|
|
541
|
+
* app-owned data, so both lists named apps — which meant a node where Monitors
|
|
542
|
+
* was never enabled still offered to delete monitors, and the count was always
|
|
543
|
+
* 0. Declaring the category here makes the owning app the single source of both
|
|
544
|
+
* its existence and its wording, and makes the row appear and disappear with
|
|
545
|
+
* the app instead of with a client-side feature-detect.
|
|
546
|
+
*
|
|
547
|
+
* # Core-interpreted, so a typed struct — and NOT on the contributions endpoint
|
|
548
|
+
*
|
|
549
|
+
* Core has to resolve the id to something that can actually count and delete
|
|
550
|
+
* the rows, so per this type's own doc comment this gets a typed struct rather
|
|
551
|
+
* than opaque JSON, and it is gathered at its consumption site
|
|
552
|
+
* (`GET /api/data/counts`, which serves each category's descriptor next to its
|
|
553
|
+
* live count) rather than at `GET /api/plugins/contributions` — the same
|
|
554
|
+
* disposition as [`Contributes::tool_filters`] and [`Contributes::lsp_servers`].
|
|
555
|
+
*
|
|
556
|
+
* # Declaration, not implementation
|
|
557
|
+
*
|
|
558
|
+
* A declared category is served only when Core knows how to clear it; an id
|
|
559
|
+
* Core does not implement is skipped with a warn rather than being offered as a
|
|
560
|
+
* button that 400s. That split is deliberate and not a stepping stone to a
|
|
561
|
+
* generic HTTP truncate: clearing monitors has to tear down each monitor's
|
|
562
|
+
* backing scheduler job, and clearing meetings has to broadcast on the meetings
|
|
563
|
+
* SSE stream, so a blind `DELETE /monitors` would leave jobs ticking forever.
|
|
564
|
+
* The manifest owns *whether the row exists and what it says*; Core owns *what
|
|
565
|
+
* deleting actually entails*.
|
|
566
|
+
*/
|
|
567
|
+
data_categories?: DataCategoryContribution[];
|
|
490
568
|
/**
|
|
491
569
|
* App-registered **workspace dock panels** — a tab in the desktop's bottom or
|
|
492
570
|
* right dock (Terminal / Code Review / Browser / Simulator live there today).
|
|
@@ -582,6 +660,45 @@ export interface Contributes {
|
|
|
582
660
|
lsp_servers?: {
|
|
583
661
|
[k: string]: LspServerContribution;
|
|
584
662
|
};
|
|
663
|
+
/**
|
|
664
|
+
* **Pi extensions** the plugin ships — TypeScript files the managed `ryu` (Pi)
|
|
665
|
+
* agent loads at process start:
|
|
666
|
+
*
|
|
667
|
+
* ```json
|
|
668
|
+
* "pi_extensions": [
|
|
669
|
+
* { "id": "shell", "file": "pi-extensions/ryu-shell.ts",
|
|
670
|
+
* "description": "background bash for the managed Pi agent" }
|
|
671
|
+
* ]
|
|
672
|
+
* ```
|
|
673
|
+
*
|
|
674
|
+
* Pi ships none of plan mode, sub-agents, permission prompts or background bash
|
|
675
|
+
* and says so deliberately in its own docs — "you can build or install those
|
|
676
|
+
* workflows as extensions or packages". This surface is that seam: the
|
|
677
|
+
* capabilities Core used to hardcode into the spawn path become plugins the user
|
|
678
|
+
* can enable and disable, and a third party can ship one at all.
|
|
679
|
+
*
|
|
680
|
+
* # This is UNSANDBOXED code, and the tier gate is not optional
|
|
681
|
+
*
|
|
682
|
+
* A [`Contributes::turn_hooks`] body runs in the deny-by-default Deno sandbox
|
|
683
|
+
* behind capability-gated `host.*` calls. A file named here runs **inside the Pi
|
|
684
|
+
* process** with full host privilege: the first-party ones spawn children and
|
|
685
|
+
* POST to Core. That is the same arbitrary-code-execution class as
|
|
686
|
+
* [`PluginManifest::mcp_servers`], so Core gates it identically — Core tier is
|
|
687
|
+
* auto-allowed, Community tier needs an operator-allowlisted grant, and the gate
|
|
688
|
+
* sits at the materializer, because writing the file is what makes it run.
|
|
689
|
+
*
|
|
690
|
+
* # Core-interpreted, so a typed struct — and NOT on the contributions endpoint
|
|
691
|
+
*
|
|
692
|
+
* Core resolves each `file` and projects it into the managed Pi's config dir, so
|
|
693
|
+
* per this type's own doc comment it gets a typed struct and is gathered at its
|
|
694
|
+
* consumption site (`pi_config::app_extensions`) rather than served from
|
|
695
|
+
* `GET /api/plugins/contributions` — the same disposition as
|
|
696
|
+
* [`Contributes::lsp_servers`].
|
|
697
|
+
*
|
|
698
|
+
* The `file` is deliberately NOT hydrated into an inline string the way a
|
|
699
|
+
* `code_file` is; see [`PluginManifest::pi_extension_refs`] for why.
|
|
700
|
+
*/
|
|
701
|
+
pi_extensions?: PiExtensionContribution[];
|
|
585
702
|
/**
|
|
586
703
|
* Gateway policies the plugin contributes (referenced by runnable id).
|
|
587
704
|
*/
|
|
@@ -699,6 +816,42 @@ export interface ContributionId {
|
|
|
699
816
|
*/
|
|
700
817
|
title?: string | null;
|
|
701
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* One **deletable data category** an app owns (see [`Contributes::data_categories`]).
|
|
821
|
+
*
|
|
822
|
+
* Everything the Danger Zone needs to draw and arm one destructive row, so the copy
|
|
823
|
+
* lives with the app whose data it describes rather than in the desktop's source.
|
|
824
|
+
*/
|
|
825
|
+
export interface DataCategoryContribution {
|
|
826
|
+
/**
|
|
827
|
+
* The word the user must type to arm the delete. Absent = the [`noun`], which is
|
|
828
|
+
* the right default often enough that requiring it would just be ceremony.
|
|
829
|
+
* Matched case-insensitively by the client.
|
|
830
|
+
*
|
|
831
|
+
* [`noun`]: DataCategoryContribution::noun
|
|
832
|
+
*/
|
|
833
|
+
confirm_word?: string | null;
|
|
834
|
+
/**
|
|
835
|
+
* Exactly what disappears, shown in the confirm dialog. Required, and required
|
|
836
|
+
* to be specific: this is the last thing the user reads before an irreversible
|
|
837
|
+
* delete, and "this cannot be undone" tells them nothing they did not know.
|
|
838
|
+
*/
|
|
839
|
+
detail: string;
|
|
840
|
+
/**
|
|
841
|
+
* Stable id — this is the `category` a `POST /api/data/clear` names, so it is
|
|
842
|
+
* the app's half of the delete contract and renaming it breaks the button.
|
|
843
|
+
*/
|
|
844
|
+
id: string;
|
|
845
|
+
/**
|
|
846
|
+
* Plural noun for the live count line ("42 monitors" / "No monitors") and the
|
|
847
|
+
* "N deleted" toast. Lower-case: it is used mid-sentence.
|
|
848
|
+
*/
|
|
849
|
+
noun: string;
|
|
850
|
+
/**
|
|
851
|
+
* The destructive button label and confirm-dialog title ("Delete all monitors").
|
|
852
|
+
*/
|
|
853
|
+
title: string;
|
|
854
|
+
}
|
|
702
855
|
/**
|
|
703
856
|
* One app-registered **workspace dock panel** — a tab in the desktop's bottom or
|
|
704
857
|
* right dock (see [`Contributes::dock_panels`]).
|
|
@@ -955,6 +1108,31 @@ export interface LspServerContribution {
|
|
|
955
1108
|
*/
|
|
956
1109
|
workspaceFolder?: string | null;
|
|
957
1110
|
}
|
|
1111
|
+
/**
|
|
1112
|
+
* One **Pi extension** a plugin ships (a [`Contributes::pi_extensions`] row).
|
|
1113
|
+
*
|
|
1114
|
+
* Carries a path, never a body: unlike [`TurnHookContribution`] there is no inline
|
|
1115
|
+
* `code` twin, because nothing downstream reads the source as a string.
|
|
1116
|
+
*/
|
|
1117
|
+
export interface PiExtensionContribution {
|
|
1118
|
+
/**
|
|
1119
|
+
* Optional human-facing one-liner (what the extension adds to the agent).
|
|
1120
|
+
*/
|
|
1121
|
+
description?: string | null;
|
|
1122
|
+
/**
|
|
1123
|
+
* Path to the TypeScript source, relative to the plugin root — exactly
|
|
1124
|
+
* `pi-extensions/<name>.ts`. See [`validate_pi_extension_path`].
|
|
1125
|
+
*/
|
|
1126
|
+
file: string;
|
|
1127
|
+
/**
|
|
1128
|
+
* Stable id for this extension within the plugin (`[a-z0-9][a-z0-9._-]*`).
|
|
1129
|
+
*
|
|
1130
|
+
* Part of the materialized file name, so it is what makes one plugin's
|
|
1131
|
+
* extensions distinguishable from another's on disk — and why it is validated
|
|
1132
|
+
* with the same alphabet as an event name rather than left free-form.
|
|
1133
|
+
*/
|
|
1134
|
+
id: string;
|
|
1135
|
+
}
|
|
958
1136
|
/**
|
|
959
1137
|
* One **settings tab** a plugin contributes (see [`Contributes::settings_tabs`]).
|
|
960
1138
|
*
|
|
@@ -1369,6 +1547,51 @@ export interface McpServerDecl {
|
|
|
1369
1547
|
[k: string]: string;
|
|
1370
1548
|
};
|
|
1371
1549
|
}
|
|
1550
|
+
/**
|
|
1551
|
+
* One entry in an app's **user-facing permission vocabulary** — a level an admin
|
|
1552
|
+
* can grant to a person or a team inside that app (see
|
|
1553
|
+
* [`PluginManifest::permission_levels`], which also explains why this is a
|
|
1554
|
+
* different axis from `permission_grants` and `permissions`).
|
|
1555
|
+
*
|
|
1556
|
+
* Deliberately self-describing: an admin UI renders the grant picker from `label`
|
|
1557
|
+
* + `description` alone, so a level whose meaning lives only in the app's own docs
|
|
1558
|
+
* cannot exist.
|
|
1559
|
+
*/
|
|
1560
|
+
export interface PermissionLevel {
|
|
1561
|
+
/**
|
|
1562
|
+
* One sentence telling an admin what granting this level actually allows.
|
|
1563
|
+
* Required for the same reason as [`label`]: the admin deciding is usually
|
|
1564
|
+
* not the person who wrote the app.
|
|
1565
|
+
*
|
|
1566
|
+
* [`label`]: PermissionLevel::label
|
|
1567
|
+
*/
|
|
1568
|
+
description: string;
|
|
1569
|
+
/**
|
|
1570
|
+
* Stable machine id (e.g. `"read"`). Lower-case ASCII alphanumerics plus
|
|
1571
|
+
* `-`, `_` and `.`, at most [`MAX_PLUGIN_ID_LEN`] bytes, and unique within the
|
|
1572
|
+
* manifest.
|
|
1573
|
+
*
|
|
1574
|
+
* The alphabet is narrower than a plugin id's on purpose: these ids end up in
|
|
1575
|
+
* API paths and in persisted grant strings, so `Read` and `read` must not be
|
|
1576
|
+
* two levels that look identical to a human granting them.
|
|
1577
|
+
*/
|
|
1578
|
+
id: string;
|
|
1579
|
+
/**
|
|
1580
|
+
* Ids of other levels in **this same manifest** that this level subsumes.
|
|
1581
|
+
*
|
|
1582
|
+
* This is the whole ordering mechanism — there is no separate rank, so the
|
|
1583
|
+
* order can never contradict itself. `edit` implying `read` means a person
|
|
1584
|
+
* granted `edit` already holds `read`; granting both is redundant, never
|
|
1585
|
+
* required. Resolved transitively by
|
|
1586
|
+
* [`resolve_implied_permission_levels`].
|
|
1587
|
+
*/
|
|
1588
|
+
implies?: string[];
|
|
1589
|
+
/**
|
|
1590
|
+
* Short human label for the grant picker (e.g. `"Can edit"`). Required —
|
|
1591
|
+
* an unlabelled level is unrenderable.
|
|
1592
|
+
*/
|
|
1593
|
+
label: string;
|
|
1594
|
+
}
|
|
1372
1595
|
/**
|
|
1373
1596
|
* The single, typed, **deny-by-default** permission set a plugin manifest
|
|
1374
1597
|
* declares, lowered by Core to every sandbox backend.
|
|
@@ -2109,6 +2332,44 @@ export interface RouteSpec {
|
|
|
2109
2332
|
* sidecar's REST routes (`/inboxes/:id`) can be declared faithfully.
|
|
2110
2333
|
*/
|
|
2111
2334
|
path: string;
|
|
2335
|
+
/**
|
|
2336
|
+
* The [`PluginManifest::permission_levels`] id a caller must hold to reach this
|
|
2337
|
+
* route. Absent (the default) = ungated: Core forwards exactly as it always did,
|
|
2338
|
+
* so annotating is opt-in and no existing app changes behaviour.
|
|
2339
|
+
*
|
|
2340
|
+
* This is the only place a route→permission mapping can honestly live: Core
|
|
2341
|
+
* cannot know that an app's `/tabs/:id/close` is destructive, and the sidecar
|
|
2342
|
+
* cannot enforce it (it never sees the caller's identity, only Core's minted
|
|
2343
|
+
* hop token). Declaring it HERE — on the same [`RouteSpec`] the proxy already
|
|
2344
|
+
* matches to decide forward-or-404 — means the gate and the forward can never
|
|
2345
|
+
* disagree about which route is in play.
|
|
2346
|
+
*
|
|
2347
|
+
* Must name a level THIS manifest declares (enforced by
|
|
2348
|
+
* [`crate::manifest::validate_route_permissions`]); an app cannot gate its
|
|
2349
|
+
* routes on another app's vocabulary or on a level nobody can see to grant.
|
|
2350
|
+
*
|
|
2351
|
+
* Never annotate an [`RouteAuth::Public`] route: a public route exists for a
|
|
2352
|
+
* caller who holds no identity at all (an external webhook), and on an
|
|
2353
|
+
* org-bound node an anonymous caller is refused outright — the annotation would
|
|
2354
|
+
* turn a working inbound webhook into a permanent 403.
|
|
2355
|
+
*
|
|
2356
|
+
* [`PluginManifest::permission_levels`]: crate::manifest::PluginManifest::permission_levels
|
|
2357
|
+
*/
|
|
2358
|
+
permission?: string | null;
|
|
2359
|
+
/**
|
|
2360
|
+
* Which `:param` of [`path`] names the resource [`permission`] is checked
|
|
2361
|
+
* against, so one route can be granted per-object (`"id"` on `/tabs/:id` gates
|
|
2362
|
+
* each tab separately). Absent = the whole app is the resource, which is what an
|
|
2363
|
+
* admin grants when the route identifies nothing (a `/settings` POST).
|
|
2364
|
+
*
|
|
2365
|
+
* Only meaningful alongside [`permission`], and the named param must actually
|
|
2366
|
+
* appear in [`path`] — both enforced at validation, because a typo here would
|
|
2367
|
+
* silently widen a rule the author wrote as per-object into a per-app one.
|
|
2368
|
+
*
|
|
2369
|
+
* [`path`]: RouteSpec::path
|
|
2370
|
+
* [`permission`]: RouteSpec::permission
|
|
2371
|
+
*/
|
|
2372
|
+
resource_param?: string | null;
|
|
2112
2373
|
}
|
|
2113
2374
|
/**
|
|
2114
2375
|
* A single downloaded executable: fetched (checksum-verified) into the
|
|
@@ -2209,7 +2470,7 @@ export interface SurfaceEntry {
|
|
|
2209
2470
|
/**
|
|
2210
2471
|
* How much of the plugin this surface supports.
|
|
2211
2472
|
*/
|
|
2212
|
-
support?: "full" | "limited" | "list" | "commands" | "none";
|
|
2473
|
+
support?: "full" | "limited" | "list" | "commands" | "none" | "unknown";
|
|
2213
2474
|
/**
|
|
2214
2475
|
* Optional surface-specific UI descriptor (bundle id, mount point, …),
|
|
2215
2476
|
* interpreted by the surface's app host. Opaque to the contract.
|
package/src/manifest.ts
CHANGED
|
@@ -239,6 +239,41 @@ export const HookEventContributionSchema = z.object({
|
|
|
239
239
|
|
|
240
240
|
export type HookEventContribution = z.infer<typeof HookEventContributionSchema>;
|
|
241
241
|
|
|
242
|
+
// ── PiExtensionContribution ───────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* One Pi extension the plugin ships — a TypeScript file the managed `ryu` (Pi)
|
|
246
|
+
* agent loads at process start. Mirrors Rust `PiExtensionContribution`.
|
|
247
|
+
*
|
|
248
|
+
* Carries a PATH, never a body: unlike `turn_hooks` there is no inline `code`
|
|
249
|
+
* twin, because nothing downstream reads the source as a string.
|
|
250
|
+
*
|
|
251
|
+
* That makes it a SIDECAR FILE, and `ryu pack` emits a single JSON bundle — so a
|
|
252
|
+
* plugin installed from a packed bundle arrives without its `pi-extensions/`
|
|
253
|
+
* directory and Core resolves the declaration to a visible skip. Same open gap as
|
|
254
|
+
* `skills/**`, which the bundle likewise does not carry. Today the path that works
|
|
255
|
+
* is a plugin whose directory is on disk (a built-in, a satellite checkout, a dev
|
|
256
|
+
* tree). Do not "fix" this by inlining the source into the manifest: a 50 KB
|
|
257
|
+
* TypeScript program escaped into a JSON string is the unauditable form the whole
|
|
258
|
+
* `code_file` extraction exists to prevent.
|
|
259
|
+
*
|
|
260
|
+
* Note this is UNSANDBOXED code: it runs inside the agent process with full host
|
|
261
|
+
* privilege, so Core gates it behind the operator-only `pi:extension` grant for
|
|
262
|
+
* any non-built-in plugin.
|
|
263
|
+
*/
|
|
264
|
+
export const PiExtensionContributionSchema = z.object({
|
|
265
|
+
/** Stable id for this extension within the plugin (`[a-z0-9][a-z0-9._-]*`). */
|
|
266
|
+
id: z.string().min(1),
|
|
267
|
+
/** Path to the source, relative to the plugin root: `pi-extensions/<name>.ts`. */
|
|
268
|
+
file: z.string().min(1),
|
|
269
|
+
/** Optional one-liner describing what the extension adds to the agent. */
|
|
270
|
+
description: z.string().optional(),
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
export type PiExtensionContribution = z.infer<
|
|
274
|
+
typeof PiExtensionContributionSchema
|
|
275
|
+
>;
|
|
276
|
+
|
|
242
277
|
// ── WidgetContribution (Ryu Apps) ─────────────────────────────────────────────
|
|
243
278
|
|
|
244
279
|
/** Default widget MIME dialect. Mirrors Core `default_widget_mime`. */
|
|
@@ -335,6 +370,14 @@ export const ContributesSchema = z.object({
|
|
|
335
370
|
* Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
|
|
336
371
|
* strip the dock panel an app declares here. */
|
|
337
372
|
dock_panels: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
373
|
+
/** Deletable data categories the app owns — one "Delete all X" row in Settings
|
|
374
|
+
* → Danger Zone. Mirrors the Rust-side `Contributes.data_categories`; without
|
|
375
|
+
* it the CLI's zod parse would strip the declaration before signing, and the
|
|
376
|
+
* app's danger-zone row would simply never appear on any node that installed
|
|
377
|
+
* the packed bundle. Loosely typed here for the same reason as the surfaces
|
|
378
|
+
* above — Core is the layer that types it, because Core is the layer that has
|
|
379
|
+
* to resolve the id to something that can actually delete the rows. */
|
|
380
|
+
data_categories: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
338
381
|
/** Language servers the plugin declares, keyed by server name — the mirror of
|
|
339
382
|
* Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
|
|
340
383
|
* loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
|
|
@@ -350,6 +393,15 @@ export const ContributesSchema = z.object({
|
|
|
350
393
|
lsp_servers: z
|
|
351
394
|
.record(z.string(), z.record(z.string(), z.unknown()))
|
|
352
395
|
.default({}),
|
|
396
|
+
/** Pi extensions the plugin ships — TypeScript the managed `ryu` (Pi) agent
|
|
397
|
+
* loads at process start. Mirrors the Rust-side `Contributes.pi_extensions`;
|
|
398
|
+
* without it the CLI's zod parse would strip the declaration before signing,
|
|
399
|
+
* and the packed plugin would ship a `pi-extensions/` folder nothing loads.
|
|
400
|
+
*
|
|
401
|
+
* Typed (not a loose record) because Ryu owns this vocabulary — three fields,
|
|
402
|
+
* all of them Core-interpreted — unlike `lsp_servers`, whose entry shape is
|
|
403
|
+
* Claude Code's to extend. */
|
|
404
|
+
pi_extensions: z.array(PiExtensionContributionSchema).default([]),
|
|
353
405
|
});
|
|
354
406
|
|
|
355
407
|
export type Contributes = z.infer<typeof ContributesSchema>;
|
package/src/runnable/app.ts
CHANGED
|
@@ -228,6 +228,10 @@ export function defineApp(options: DefineAppOptions): PluginManifest {
|
|
|
228
228
|
// takes no `contributes` passthrough. An app that wants to declare language
|
|
229
229
|
// servers writes them in a hand-authored `manifest.json`.
|
|
230
230
|
lsp_servers: {},
|
|
231
|
+
// Same reason again: a danger-zone category and a Pi extension are both
|
|
232
|
+
// hand-authored declarations, not something derivable from runnables.
|
|
233
|
+
data_categories: [],
|
|
234
|
+
pi_extensions: [],
|
|
231
235
|
widgets,
|
|
232
236
|
};
|
|
233
237
|
|
|
@@ -176,13 +176,16 @@ export function definePlugin(options: DefinePluginOptions): PluginManifest {
|
|
|
176
176
|
settings_tabs: options.settingsTabs ?? [],
|
|
177
177
|
slash_commands: options.slashCommands ?? [],
|
|
178
178
|
lsp_servers: options.lspServers ?? {},
|
|
179
|
-
// A turn-hook plugin contributes no app widgets, sidebar entries
|
|
180
|
-
// panels
|
|
181
|
-
// defaults applied), so set them
|
|
179
|
+
// A turn-hook plugin contributes no app widgets, sidebar entries, dock
|
|
180
|
+
// panels, danger-zone categories or Pi extensions; the fields are required
|
|
181
|
+
// on the resolved `Contributes` type (zod defaults applied), so set them
|
|
182
|
+
// explicitly.
|
|
182
183
|
widgets: [],
|
|
183
184
|
sidebar_sections: [],
|
|
184
185
|
sidebar_buttons: [],
|
|
185
186
|
dock_panels: [],
|
|
187
|
+
data_categories: [],
|
|
188
|
+
pi_extensions: [],
|
|
186
189
|
};
|
|
187
190
|
// Ship each inline tool as a `kind:"tool"` runnable (Core's `inline_deno`
|
|
188
191
|
// backend). Shipping tools requires the `tool:execute` grant; add it once.
|