@ryuhq/sdk 0.0.5 → 0.0.17
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/agent.cjs +4 -1
- package/dist/agent.d.cts +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{chunk-KPKMMGVC.js → chunk-MTUBUPIV.js} +4 -1
- package/dist/{chunk-GXHL5CO7.js → chunk-SKVIJH5I.js} +84 -2
- package/dist/cli.cjs +138 -10
- package/dist/cli.js +56 -9
- package/dist/{index-DAxq7Y0R.d.ts → index-B6SkaAjJ.d.ts} +4 -4
- package/dist/{index-CEbS1SlS.d.cts → index-BvAB5eMk.d.cts} +4 -4
- package/dist/index.cjs +135 -9
- package/dist/index.d.cts +30 -12
- package/dist/index.d.ts +30 -12
- package/dist/index.js +50 -8
- package/dist/manifest.cjs +85 -2
- package/dist/manifest.d.cts +93 -10
- package/dist/manifest.d.ts +93 -10
- package/dist/manifest.js +3 -1
- package/package.json +2 -2
- package/src/builder.ts +4 -4
- package/src/cli.ts +98 -13
- package/src/contracts-lockstep.test.ts +4 -4
- package/src/generated/plugin-manifest.ts +1148 -22
- package/src/manifest-schema.test.ts +182 -0
- package/src/manifest.fixtures.test.ts +447 -0
- package/src/manifest.test.ts +100 -16
- package/src/manifest.ts +137 -17
- package/src/plugin/ryu-plugin.ts +1 -1
- package/src/runnable/agent.ts +3 -3
- package/src/runnable/app.test.ts +67 -0
- package/src/runnable/app.ts +54 -4
- package/src/runnable/primitives.test.ts +1 -5
- package/src/runnable/primitives.ts +6 -5
- package/src/runnable/tool.ts +4 -4
- package/src/runnable/turn-hook.ts +30 -4
package/dist/agent.cjs
CHANGED
|
@@ -152,7 +152,10 @@ function dataUrlToBytes(dataUrl) {
|
|
|
152
152
|
}
|
|
153
153
|
return { bytes, mediaType };
|
|
154
154
|
}
|
|
155
|
-
return {
|
|
155
|
+
return {
|
|
156
|
+
bytes: new TextEncoder().encode(decodeURIComponent(payload)),
|
|
157
|
+
mediaType
|
|
158
|
+
};
|
|
156
159
|
}
|
|
157
160
|
function bytesToDataUrl(bytes, mediaType) {
|
|
158
161
|
let binary = "";
|
package/dist/agent.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { A as Agent, b as AgentConfig, c as AgentEvent, U as AgentEventAuthRequired, V as AgentEventError, W as AgentEventResult, X as AgentEventText, Y as AgentEventToolCall, Z as AgentEventToolResult, d as AgentTool, _ as AssistantMessage, $ as Elicitation, E as Endpoint, h as GenerateResult, a0 as LoopConfig, a1 as LoopMessage, a2 as ModelCallOptions, a3 as ModelCallResult, a4 as ModelUsage, Q as QueryInput, n as QueryOptions, t as RemoteToolRef, a5 as RyuToolOptions, a6 as ToolCall, a7 as ToolExecContext, a8 as ToolExecResult, a9 as ToolFunctionDef, aa as callModelWithTools, y as createAgent, ab as detectElicitation, ac as executeTool, N as query, ad as resolveToolDefs, ae as runAgentLoop, O as ryuTool } from './index-
|
|
1
|
+
export { A as Agent, b as AgentConfig, c as AgentEvent, U as AgentEventAuthRequired, V as AgentEventError, W as AgentEventResult, X as AgentEventText, Y as AgentEventToolCall, Z as AgentEventToolResult, d as AgentTool, _ as AssistantMessage, $ as Elicitation, E as Endpoint, h as GenerateResult, a0 as LoopConfig, a1 as LoopMessage, a2 as ModelCallOptions, a3 as ModelCallResult, a4 as ModelUsage, Q as QueryInput, n as QueryOptions, t as RemoteToolRef, a5 as RyuToolOptions, a6 as ToolCall, a7 as ToolExecContext, a8 as ToolExecResult, a9 as ToolFunctionDef, aa as callModelWithTools, y as createAgent, ab as detectElicitation, ac as executeTool, N as query, ad as resolveToolDefs, ae as runAgentLoop, O as ryuTool } from './index-BvAB5eMk.cjs';
|
|
2
2
|
import './manifest.cjs';
|
|
3
3
|
import 'zod';
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { A as Agent, b as AgentConfig, c as AgentEvent, U as AgentEventAuthRequired, V as AgentEventError, W as AgentEventResult, X as AgentEventText, Y as AgentEventToolCall, Z as AgentEventToolResult, d as AgentTool, _ as AssistantMessage, $ as Elicitation, E as Endpoint, h as GenerateResult, a0 as LoopConfig, a1 as LoopMessage, a2 as ModelCallOptions, a3 as ModelCallResult, a4 as ModelUsage, Q as QueryInput, n as QueryOptions, t as RemoteToolRef, a5 as RyuToolOptions, a6 as ToolCall, a7 as ToolExecContext, a8 as ToolExecResult, a9 as ToolFunctionDef, aa as callModelWithTools, y as createAgent, ab as detectElicitation, ac as executeTool, N as query, ad as resolveToolDefs, ae as runAgentLoop, O as ryuTool } from './index-
|
|
1
|
+
export { A as Agent, b as AgentConfig, c as AgentEvent, U as AgentEventAuthRequired, V as AgentEventError, W as AgentEventResult, X as AgentEventText, Y as AgentEventToolCall, Z as AgentEventToolResult, d as AgentTool, _ as AssistantMessage, $ as Elicitation, E as Endpoint, h as GenerateResult, a0 as LoopConfig, a1 as LoopMessage, a2 as ModelCallOptions, a3 as ModelCallResult, a4 as ModelUsage, Q as QueryInput, n as QueryOptions, t as RemoteToolRef, a5 as RyuToolOptions, a6 as ToolCall, a7 as ToolExecContext, a8 as ToolExecResult, a9 as ToolFunctionDef, aa as callModelWithTools, y as createAgent, ab as detectElicitation, ac as executeTool, N as query, ad as resolveToolDefs, ae as runAgentLoop, O as ryuTool } from './index-B6SkaAjJ.js';
|
|
2
2
|
import './manifest.js';
|
|
3
3
|
import 'zod';
|
package/dist/agent.js
CHANGED
|
@@ -24,7 +24,10 @@ function dataUrlToBytes(dataUrl) {
|
|
|
24
24
|
}
|
|
25
25
|
return { bytes, mediaType };
|
|
26
26
|
}
|
|
27
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
bytes: new TextEncoder().encode(decodeURIComponent(payload)),
|
|
29
|
+
mediaType
|
|
30
|
+
};
|
|
28
31
|
}
|
|
29
32
|
function bytesToDataUrl(bytes, mediaType) {
|
|
30
33
|
let binary = "";
|
|
@@ -76,7 +76,39 @@ var TurnHookContributionSchema = z.object({
|
|
|
76
76
|
/** Turn boundary this fires on. Today only `"post_assistant_turn"`. */
|
|
77
77
|
on: z.string().min(1).default("post_assistant_turn"),
|
|
78
78
|
/** The JS hook body executed in the sandbox (returns a directive). */
|
|
79
|
-
code: z.string().min(1)
|
|
79
|
+
code: z.string().min(1).optional(),
|
|
80
|
+
/** Path to the hook body, relative to the plugin root (`hooks/<name>.js`). */
|
|
81
|
+
code_file: z.string().min(1).optional(),
|
|
82
|
+
/**
|
|
83
|
+
* Cheap pre-gate mirroring Core's `HookMatch` (serde name `match` on
|
|
84
|
+
* `TurnHookContribution.run_when`). MUST round-trip through this schema:
|
|
85
|
+
* `ryu pack`/`publish` persist `safeParse(...).data`, so a field missing here
|
|
86
|
+
* is silently STRIPPED before signing — a tool-gated `pre_tool_use` hook
|
|
87
|
+
* (e.g. `tools: ["bash*"]`) would lose its gate and run on EVERY tool call.
|
|
88
|
+
*/
|
|
89
|
+
match: z.object({
|
|
90
|
+
/** Run only if the request set this composer flag true. */
|
|
91
|
+
flag: z.string().optional(),
|
|
92
|
+
/** Run if the last user message starts with any of these prefixes. */
|
|
93
|
+
commands: z.array(z.string()).default([]),
|
|
94
|
+
/** Run if the plugin has stored state for this conversation. */
|
|
95
|
+
stateful: z.boolean().default(false),
|
|
96
|
+
/** Run if `ctx.tool_name` matches any of these `*`-wildcard patterns. */
|
|
97
|
+
tools: z.array(z.string()).default([])
|
|
98
|
+
}).optional()
|
|
99
|
+
}).refine((h) => Boolean(h.code) !== Boolean(h.code_file), {
|
|
100
|
+
message: "a turn hook must declare exactly one of 'code' (inline body) or 'code_file' (path to hooks/<name>.js)",
|
|
101
|
+
path: ["code_file"]
|
|
102
|
+
});
|
|
103
|
+
var HookEventContributionSchema = z.object({
|
|
104
|
+
/** Fully-qualified event id: `<plugin id>#<event name>`, e.g. `@acme/meetings#meeting.ended`. */
|
|
105
|
+
id: z.string().min(1),
|
|
106
|
+
/** Human-readable title for the event picker. */
|
|
107
|
+
title: z.string().min(1),
|
|
108
|
+
/** What the event means and when it fires. */
|
|
109
|
+
description: z.string().optional(),
|
|
110
|
+
/** Example of the `ctx.event` payload. Documentation, not a validated schema. */
|
|
111
|
+
payload_example: z.record(z.string(), z.unknown()).optional()
|
|
80
112
|
});
|
|
81
113
|
var DEFAULT_WIDGET_MIME = "text/html+skybridge";
|
|
82
114
|
var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
|
|
@@ -114,6 +146,11 @@ var ToolAppConfigSchema = z.object({
|
|
|
114
146
|
});
|
|
115
147
|
var ContributesSchema = z.object({
|
|
116
148
|
turn_hooks: z.array(TurnHookContributionSchema).default([]),
|
|
149
|
+
/** App events this plugin EMITS — the provider half of the hook system, whose
|
|
150
|
+
* consumer half is `turn_hooks`. Mirrors the Rust `Contributes.hook_events`;
|
|
151
|
+
* omitting it here would have `ryu pack` strip every declared event before
|
|
152
|
+
* signing, leaving an app that emits events nothing is allowed to subscribe to. */
|
|
153
|
+
hook_events: z.array(HookEventContributionSchema).default([]),
|
|
117
154
|
composer_controls: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
118
155
|
settings_tabs: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
119
156
|
slash_commands: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
@@ -121,7 +158,32 @@ var ContributesSchema = z.object({
|
|
|
121
158
|
* `ui://widget/<slug>.html` template. Mirrors the Rust-side
|
|
122
159
|
* `Contributes.widgets` field, without which the CLI's zod parse would strip
|
|
123
160
|
* every widget an app authored here declares. */
|
|
124
|
-
widgets: z.array(WidgetContributionSchema).default([])
|
|
161
|
+
widgets: z.array(WidgetContributionSchema).default([]),
|
|
162
|
+
/** App-registered sidebar sections (header + live list) and buttons (single nav
|
|
163
|
+
* rows). Loosely typed here — the shell owns the spec vocabulary — matching how
|
|
164
|
+
* `composer_controls`/`settings_tabs` are declared. Mirrors the Rust-side
|
|
165
|
+
* `Contributes.sidebar_sections` / `Contributes.sidebar_buttons`. */
|
|
166
|
+
sidebar_sections: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
167
|
+
sidebar_buttons: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
168
|
+
/** App-registered workspace dock panels (a tab in the desktop's bottom/right
|
|
169
|
+
* dock). Loosely typed for the same reason as the surfaces above — the shell
|
|
170
|
+
* owns the `panel` render-mode vocabulary and the `spec` payload. Mirrors the
|
|
171
|
+
* Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
|
|
172
|
+
* strip the dock panel an app declares here. */
|
|
173
|
+
dock_panels: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
174
|
+
/** Language servers the plugin declares, keyed by server name — the mirror of
|
|
175
|
+
* Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
|
|
176
|
+
* loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
|
|
177
|
+
* it the CLI's zod parse would strip every language server a plugin declares,
|
|
178
|
+
* before the manifest is signed.
|
|
179
|
+
*
|
|
180
|
+
* The ENTRY is deliberately a loose record and not a 13-field `z.object()`
|
|
181
|
+
* mirroring `LspServerContribution`. Claude Code owns this field vocabulary,
|
|
182
|
+
* not Ryu: a typed object here would strip a field from a newer Claude release
|
|
183
|
+
* on its way through `ryu pack` — the same silent-deletion bug this field
|
|
184
|
+
* exists to fix, one level down. Core is the layer that types it, because Core
|
|
185
|
+
* is the layer that acts on it. */
|
|
186
|
+
lsp_servers: z.record(z.string(), z.record(z.string(), z.unknown())).default({})
|
|
125
187
|
});
|
|
126
188
|
var SetupStepSchema = z.object({
|
|
127
189
|
/** Card heading (e.g. the companion app name). */
|
|
@@ -302,6 +364,25 @@ var PluginManifestSchema = z.object({
|
|
|
302
364
|
license: z.string().optional(),
|
|
303
365
|
/** Square logo/icon URL for the listing card + detail header. */
|
|
304
366
|
iconUrl: z.string().optional(),
|
|
367
|
+
/**
|
|
368
|
+
* Icon-primitive id for the listing card (Ryu extension): an Iconify/icons0
|
|
369
|
+
* `prefix:name`, a bare Hugeicons name, or a URL, resolved by the shared `Icon`
|
|
370
|
+
* primitive. A monochrome GLYPH masked with the current text colour — distinct
|
|
371
|
+
* from `iconUrl` (a raster logo). Falls back to `iconUrl` when omitted.
|
|
372
|
+
*/
|
|
373
|
+
icon: z.string().optional(),
|
|
374
|
+
/**
|
|
375
|
+
* Dithered-gradient background for the card's icon square (Ryu extension),
|
|
376
|
+
* mirroring dither-kit's `DitherGradient` props. `from`/`to` are a palette-colour
|
|
377
|
+
* name (`green`, `blue`, `purple`, `pink`, `orange`, `red`, `grey`) or a hue
|
|
378
|
+
* number (0–360); `direction` is where `to` ends up. Renders behind the glyph in
|
|
379
|
+
* place of a flat `iconBackground`; the render layer validates + falls back.
|
|
380
|
+
*/
|
|
381
|
+
iconDither: z.object({
|
|
382
|
+
from: z.union([z.string(), z.number()]),
|
|
383
|
+
to: z.union([z.string(), z.number()]).optional(),
|
|
384
|
+
direction: z.enum(["up", "down", "left", "right"]).optional()
|
|
385
|
+
}).optional(),
|
|
305
386
|
/** Ordered App-Store-style screenshot gallery URLs (Ryu extension). */
|
|
306
387
|
screenshots: z.array(z.string()).optional(),
|
|
307
388
|
/** Privacy policy URL surfaced on detail (Ryu extension). */
|
|
@@ -338,6 +419,7 @@ export {
|
|
|
338
419
|
labelImpersonatesSystemChrome,
|
|
339
420
|
CompanionSurfaceSchema,
|
|
340
421
|
TurnHookContributionSchema,
|
|
422
|
+
HookEventContributionSchema,
|
|
341
423
|
WidgetContributionSchema,
|
|
342
424
|
ToolAppConfigSchema,
|
|
343
425
|
ContributesSchema,
|
package/dist/cli.cjs
CHANGED
|
@@ -362,7 +362,39 @@ var TurnHookContributionSchema = import_zod.z.object({
|
|
|
362
362
|
/** Turn boundary this fires on. Today only `"post_assistant_turn"`. */
|
|
363
363
|
on: import_zod.z.string().min(1).default("post_assistant_turn"),
|
|
364
364
|
/** The JS hook body executed in the sandbox (returns a directive). */
|
|
365
|
-
code: import_zod.z.string().min(1)
|
|
365
|
+
code: import_zod.z.string().min(1).optional(),
|
|
366
|
+
/** Path to the hook body, relative to the plugin root (`hooks/<name>.js`). */
|
|
367
|
+
code_file: import_zod.z.string().min(1).optional(),
|
|
368
|
+
/**
|
|
369
|
+
* Cheap pre-gate mirroring Core's `HookMatch` (serde name `match` on
|
|
370
|
+
* `TurnHookContribution.run_when`). MUST round-trip through this schema:
|
|
371
|
+
* `ryu pack`/`publish` persist `safeParse(...).data`, so a field missing here
|
|
372
|
+
* is silently STRIPPED before signing — a tool-gated `pre_tool_use` hook
|
|
373
|
+
* (e.g. `tools: ["bash*"]`) would lose its gate and run on EVERY tool call.
|
|
374
|
+
*/
|
|
375
|
+
match: import_zod.z.object({
|
|
376
|
+
/** Run only if the request set this composer flag true. */
|
|
377
|
+
flag: import_zod.z.string().optional(),
|
|
378
|
+
/** Run if the last user message starts with any of these prefixes. */
|
|
379
|
+
commands: import_zod.z.array(import_zod.z.string()).default([]),
|
|
380
|
+
/** Run if the plugin has stored state for this conversation. */
|
|
381
|
+
stateful: import_zod.z.boolean().default(false),
|
|
382
|
+
/** Run if `ctx.tool_name` matches any of these `*`-wildcard patterns. */
|
|
383
|
+
tools: import_zod.z.array(import_zod.z.string()).default([])
|
|
384
|
+
}).optional()
|
|
385
|
+
}).refine((h) => Boolean(h.code) !== Boolean(h.code_file), {
|
|
386
|
+
message: "a turn hook must declare exactly one of 'code' (inline body) or 'code_file' (path to hooks/<name>.js)",
|
|
387
|
+
path: ["code_file"]
|
|
388
|
+
});
|
|
389
|
+
var HookEventContributionSchema = import_zod.z.object({
|
|
390
|
+
/** Fully-qualified event id: `<plugin id>#<event name>`, e.g. `@acme/meetings#meeting.ended`. */
|
|
391
|
+
id: import_zod.z.string().min(1),
|
|
392
|
+
/** Human-readable title for the event picker. */
|
|
393
|
+
title: import_zod.z.string().min(1),
|
|
394
|
+
/** What the event means and when it fires. */
|
|
395
|
+
description: import_zod.z.string().optional(),
|
|
396
|
+
/** Example of the `ctx.event` payload. Documentation, not a validated schema. */
|
|
397
|
+
payload_example: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
366
398
|
});
|
|
367
399
|
var DEFAULT_WIDGET_MIME = "text/html+skybridge";
|
|
368
400
|
var DEFAULT_WIDGET_DISPLAY_MODE = "inline";
|
|
@@ -400,6 +432,11 @@ var ToolAppConfigSchema = import_zod.z.object({
|
|
|
400
432
|
});
|
|
401
433
|
var ContributesSchema = import_zod.z.object({
|
|
402
434
|
turn_hooks: import_zod.z.array(TurnHookContributionSchema).default([]),
|
|
435
|
+
/** App events this plugin EMITS — the provider half of the hook system, whose
|
|
436
|
+
* consumer half is `turn_hooks`. Mirrors the Rust `Contributes.hook_events`;
|
|
437
|
+
* omitting it here would have `ryu pack` strip every declared event before
|
|
438
|
+
* signing, leaving an app that emits events nothing is allowed to subscribe to. */
|
|
439
|
+
hook_events: import_zod.z.array(HookEventContributionSchema).default([]),
|
|
403
440
|
composer_controls: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
404
441
|
settings_tabs: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
405
442
|
slash_commands: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
@@ -407,7 +444,32 @@ var ContributesSchema = import_zod.z.object({
|
|
|
407
444
|
* `ui://widget/<slug>.html` template. Mirrors the Rust-side
|
|
408
445
|
* `Contributes.widgets` field, without which the CLI's zod parse would strip
|
|
409
446
|
* every widget an app authored here declares. */
|
|
410
|
-
widgets: import_zod.z.array(WidgetContributionSchema).default([])
|
|
447
|
+
widgets: import_zod.z.array(WidgetContributionSchema).default([]),
|
|
448
|
+
/** App-registered sidebar sections (header + live list) and buttons (single nav
|
|
449
|
+
* rows). Loosely typed here — the shell owns the spec vocabulary — matching how
|
|
450
|
+
* `composer_controls`/`settings_tabs` are declared. Mirrors the Rust-side
|
|
451
|
+
* `Contributes.sidebar_sections` / `Contributes.sidebar_buttons`. */
|
|
452
|
+
sidebar_sections: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
453
|
+
sidebar_buttons: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
454
|
+
/** App-registered workspace dock panels (a tab in the desktop's bottom/right
|
|
455
|
+
* dock). Loosely typed for the same reason as the surfaces above — the shell
|
|
456
|
+
* owns the `panel` render-mode vocabulary and the `spec` payload. Mirrors the
|
|
457
|
+
* Rust-side `Contributes.dock_panels`; without it the CLI's zod parse would
|
|
458
|
+
* strip the dock panel an app declares here. */
|
|
459
|
+
dock_panels: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())).default([]),
|
|
460
|
+
/** Language servers the plugin declares, keyed by server name — the mirror of
|
|
461
|
+
* Claude Code's `.lsp.json` / `lspServers`, so a config written for either host
|
|
462
|
+
* loads in the other. Mirrors the Rust-side `Contributes.lsp_servers`; without
|
|
463
|
+
* it the CLI's zod parse would strip every language server a plugin declares,
|
|
464
|
+
* before the manifest is signed.
|
|
465
|
+
*
|
|
466
|
+
* The ENTRY is deliberately a loose record and not a 13-field `z.object()`
|
|
467
|
+
* mirroring `LspServerContribution`. Claude Code owns this field vocabulary,
|
|
468
|
+
* not Ryu: a typed object here would strip a field from a newer Claude release
|
|
469
|
+
* on its way through `ryu pack` — the same silent-deletion bug this field
|
|
470
|
+
* exists to fix, one level down. Core is the layer that types it, because Core
|
|
471
|
+
* 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({})
|
|
411
473
|
});
|
|
412
474
|
var SetupStepSchema = import_zod.z.object({
|
|
413
475
|
/** Card heading (e.g. the companion app name). */
|
|
@@ -588,6 +650,25 @@ var PluginManifestSchema = import_zod.z.object({
|
|
|
588
650
|
license: import_zod.z.string().optional(),
|
|
589
651
|
/** Square logo/icon URL for the listing card + detail header. */
|
|
590
652
|
iconUrl: import_zod.z.string().optional(),
|
|
653
|
+
/**
|
|
654
|
+
* Icon-primitive id for the listing card (Ryu extension): an Iconify/icons0
|
|
655
|
+
* `prefix:name`, a bare Hugeicons name, or a URL, resolved by the shared `Icon`
|
|
656
|
+
* primitive. A monochrome GLYPH masked with the current text colour — distinct
|
|
657
|
+
* from `iconUrl` (a raster logo). Falls back to `iconUrl` when omitted.
|
|
658
|
+
*/
|
|
659
|
+
icon: import_zod.z.string().optional(),
|
|
660
|
+
/**
|
|
661
|
+
* Dithered-gradient background for the card's icon square (Ryu extension),
|
|
662
|
+
* mirroring dither-kit's `DitherGradient` props. `from`/`to` are a palette-colour
|
|
663
|
+
* name (`green`, `blue`, `purple`, `pink`, `orange`, `red`, `grey`) or a hue
|
|
664
|
+
* number (0–360); `direction` is where `to` ends up. Renders behind the glyph in
|
|
665
|
+
* place of a flat `iconBackground`; the render layer validates + falls back.
|
|
666
|
+
*/
|
|
667
|
+
iconDither: import_zod.z.object({
|
|
668
|
+
from: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]),
|
|
669
|
+
to: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
|
|
670
|
+
direction: import_zod.z.enum(["up", "down", "left", "right"]).optional()
|
|
671
|
+
}).optional(),
|
|
591
672
|
/** Ordered App-Store-style screenshot gallery URLs (Ryu extension). */
|
|
592
673
|
screenshots: import_zod.z.array(import_zod.z.string()).optional(),
|
|
593
674
|
/** Privacy policy URL surfaced on detail (Ryu extension). */
|
|
@@ -619,8 +700,8 @@ function printUsage() {
|
|
|
619
700
|
"Ryu dev SDK",
|
|
620
701
|
"",
|
|
621
702
|
"Usage:",
|
|
622
|
-
" bunx ryu pack <dir> Validate and bundle a
|
|
623
|
-
" bunx ryu publish <dir> Validate and publish a
|
|
703
|
+
" bunx ryu pack <dir> Validate and bundle a manifest.json Plugin",
|
|
704
|
+
" bunx ryu publish <dir> Validate and publish a manifest.json Plugin to the Ryu Marketplace",
|
|
624
705
|
" bunx ryu dev <entry> Run a Runnable locally with an interactive chat loop",
|
|
625
706
|
""
|
|
626
707
|
].join("\n")
|
|
@@ -631,10 +712,17 @@ function exitError(message) {
|
|
|
631
712
|
`);
|
|
632
713
|
process.exit(1);
|
|
633
714
|
}
|
|
715
|
+
var MANIFEST_FILE_NAMES = [
|
|
716
|
+
"manifest.json",
|
|
717
|
+
"plugin.json",
|
|
718
|
+
"ryu.json"
|
|
719
|
+
];
|
|
634
720
|
function loadManifest(dir) {
|
|
635
|
-
const manifestPath = (0, import_node_path.join)(dir,
|
|
636
|
-
|
|
637
|
-
|
|
721
|
+
const manifestPath = MANIFEST_FILE_NAMES.map((name) => (0, import_node_path.join)(dir, name)).find(
|
|
722
|
+
(candidate) => (0, import_node_fs.existsSync)(candidate)
|
|
723
|
+
);
|
|
724
|
+
if (!manifestPath) {
|
|
725
|
+
exitError(`manifest.json not found in: ${dir}`);
|
|
638
726
|
}
|
|
639
727
|
let raw;
|
|
640
728
|
try {
|
|
@@ -646,16 +734,56 @@ function loadManifest(dir) {
|
|
|
646
734
|
try {
|
|
647
735
|
parsed = JSON.parse(raw);
|
|
648
736
|
} catch {
|
|
649
|
-
exitError(`
|
|
737
|
+
exitError(`manifest.json is not valid JSON: ${manifestPath}`);
|
|
650
738
|
}
|
|
651
739
|
const result = PluginManifestSchema.safeParse(parsed);
|
|
652
740
|
if (!result.success) {
|
|
653
741
|
const first = result.error.issues[0];
|
|
654
742
|
const field = first?.path.join(".") ?? "unknown";
|
|
655
743
|
const message = first?.message ?? "validation failed";
|
|
656
|
-
exitError(`
|
|
744
|
+
exitError(`manifest.json validation failed at '${field}': ${message}`);
|
|
745
|
+
}
|
|
746
|
+
return inlineCodeFiles(result.data, dir);
|
|
747
|
+
}
|
|
748
|
+
var CODE_FILE_DIRS = ["hooks", "adapters"];
|
|
749
|
+
var CODE_FILE_PATH = /^(hooks|adapters)\/[A-Za-z0-9_][A-Za-z0-9._-]*\.m?js$/;
|
|
750
|
+
function readCodeFile(dir, rel, label) {
|
|
751
|
+
if (!CODE_FILE_PATH.test(rel)) {
|
|
752
|
+
exitError(
|
|
753
|
+
`${label}: code_file '${rel}' must be exactly '<${CODE_FILE_DIRS.join("|")}>/<name>.js' with no traversal`
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
const path = (0, import_node_path.join)(dir, rel);
|
|
757
|
+
let body;
|
|
758
|
+
try {
|
|
759
|
+
body = (0, import_node_fs.readFileSync)(path, "utf8");
|
|
760
|
+
} catch (err) {
|
|
761
|
+
exitError(`${label}: could not read code_file '${rel}': ${String(err)}`);
|
|
762
|
+
}
|
|
763
|
+
if (!body.trim()) {
|
|
764
|
+
exitError(`${label}: code_file '${rel}' is empty`);
|
|
765
|
+
}
|
|
766
|
+
return body;
|
|
767
|
+
}
|
|
768
|
+
function inlineCodeFiles(manifest, dir) {
|
|
769
|
+
const out = manifest;
|
|
770
|
+
for (const hook of out.contributes?.turn_hooks ?? []) {
|
|
771
|
+
const rel = hook.code_file;
|
|
772
|
+
if (typeof rel === "string") {
|
|
773
|
+
hook.code = readCodeFile(dir, rel, `turn hook '${String(hook.id)}'`);
|
|
774
|
+
hook.code_file = void 0;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
for (const entry of out.provides ?? []) {
|
|
778
|
+
for (const [verb, binding] of Object.entries(entry.tools ?? {})) {
|
|
779
|
+
const rel = binding.adapter?.code_file;
|
|
780
|
+
if (binding.adapter && typeof rel === "string") {
|
|
781
|
+
binding.adapter.code = readCodeFile(dir, rel, `adapter '${verb}'`);
|
|
782
|
+
binding.adapter.code_file = void 0;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
657
785
|
}
|
|
658
|
-
return
|
|
786
|
+
return out;
|
|
659
787
|
}
|
|
660
788
|
function resolveUiEntry(manifest) {
|
|
661
789
|
for (const runnable of manifest.runnables) {
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import {
|
|
3
3
|
PluginManifestSchema
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SKVIJH5I.js";
|
|
5
5
|
import {
|
|
6
6
|
ModelClient,
|
|
7
7
|
resolveGatewayUrl
|
|
@@ -215,8 +215,8 @@ function printUsage() {
|
|
|
215
215
|
"Ryu dev SDK",
|
|
216
216
|
"",
|
|
217
217
|
"Usage:",
|
|
218
|
-
" bunx ryu pack <dir> Validate and bundle a
|
|
219
|
-
" bunx ryu publish <dir> Validate and publish a
|
|
218
|
+
" bunx ryu pack <dir> Validate and bundle a manifest.json Plugin",
|
|
219
|
+
" bunx ryu publish <dir> Validate and publish a manifest.json Plugin to the Ryu Marketplace",
|
|
220
220
|
" bunx ryu dev <entry> Run a Runnable locally with an interactive chat loop",
|
|
221
221
|
""
|
|
222
222
|
].join("\n")
|
|
@@ -227,10 +227,17 @@ function exitError(message) {
|
|
|
227
227
|
`);
|
|
228
228
|
process.exit(1);
|
|
229
229
|
}
|
|
230
|
+
var MANIFEST_FILE_NAMES = [
|
|
231
|
+
"manifest.json",
|
|
232
|
+
"plugin.json",
|
|
233
|
+
"ryu.json"
|
|
234
|
+
];
|
|
230
235
|
function loadManifest(dir) {
|
|
231
|
-
const manifestPath = join(dir,
|
|
232
|
-
|
|
233
|
-
|
|
236
|
+
const manifestPath = MANIFEST_FILE_NAMES.map((name) => join(dir, name)).find(
|
|
237
|
+
(candidate) => existsSync(candidate)
|
|
238
|
+
);
|
|
239
|
+
if (!manifestPath) {
|
|
240
|
+
exitError(`manifest.json not found in: ${dir}`);
|
|
234
241
|
}
|
|
235
242
|
let raw;
|
|
236
243
|
try {
|
|
@@ -242,16 +249,56 @@ function loadManifest(dir) {
|
|
|
242
249
|
try {
|
|
243
250
|
parsed = JSON.parse(raw);
|
|
244
251
|
} catch {
|
|
245
|
-
exitError(`
|
|
252
|
+
exitError(`manifest.json is not valid JSON: ${manifestPath}`);
|
|
246
253
|
}
|
|
247
254
|
const result = PluginManifestSchema.safeParse(parsed);
|
|
248
255
|
if (!result.success) {
|
|
249
256
|
const first = result.error.issues[0];
|
|
250
257
|
const field = first?.path.join(".") ?? "unknown";
|
|
251
258
|
const message = first?.message ?? "validation failed";
|
|
252
|
-
exitError(`
|
|
259
|
+
exitError(`manifest.json validation failed at '${field}': ${message}`);
|
|
260
|
+
}
|
|
261
|
+
return inlineCodeFiles(result.data, dir);
|
|
262
|
+
}
|
|
263
|
+
var CODE_FILE_DIRS = ["hooks", "adapters"];
|
|
264
|
+
var CODE_FILE_PATH = /^(hooks|adapters)\/[A-Za-z0-9_][A-Za-z0-9._-]*\.m?js$/;
|
|
265
|
+
function readCodeFile(dir, rel, label) {
|
|
266
|
+
if (!CODE_FILE_PATH.test(rel)) {
|
|
267
|
+
exitError(
|
|
268
|
+
`${label}: code_file '${rel}' must be exactly '<${CODE_FILE_DIRS.join("|")}>/<name>.js' with no traversal`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
const path = join(dir, rel);
|
|
272
|
+
let body;
|
|
273
|
+
try {
|
|
274
|
+
body = readFileSync(path, "utf8");
|
|
275
|
+
} catch (err) {
|
|
276
|
+
exitError(`${label}: could not read code_file '${rel}': ${String(err)}`);
|
|
277
|
+
}
|
|
278
|
+
if (!body.trim()) {
|
|
279
|
+
exitError(`${label}: code_file '${rel}' is empty`);
|
|
280
|
+
}
|
|
281
|
+
return body;
|
|
282
|
+
}
|
|
283
|
+
function inlineCodeFiles(manifest, dir) {
|
|
284
|
+
const out = manifest;
|
|
285
|
+
for (const hook of out.contributes?.turn_hooks ?? []) {
|
|
286
|
+
const rel = hook.code_file;
|
|
287
|
+
if (typeof rel === "string") {
|
|
288
|
+
hook.code = readCodeFile(dir, rel, `turn hook '${String(hook.id)}'`);
|
|
289
|
+
hook.code_file = void 0;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
for (const entry of out.provides ?? []) {
|
|
293
|
+
for (const [verb, binding] of Object.entries(entry.tools ?? {})) {
|
|
294
|
+
const rel = binding.adapter?.code_file;
|
|
295
|
+
if (binding.adapter && typeof rel === "string") {
|
|
296
|
+
binding.adapter.code = readCodeFile(dir, rel, `adapter '${verb}'`);
|
|
297
|
+
binding.adapter.code_file = void 0;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
253
300
|
}
|
|
254
|
-
return
|
|
301
|
+
return out;
|
|
255
302
|
}
|
|
256
303
|
function resolveUiEntry(manifest) {
|
|
257
304
|
for (const runnable of manifest.runnables) {
|
|
@@ -637,9 +637,6 @@ interface ToolOptions<TInput extends Record<string, unknown>, TOutput> {
|
|
|
637
637
|
* be forwarded verbatim to Core's MCP/ACP layer.
|
|
638
638
|
*/
|
|
639
639
|
interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown> extends Runnable<TInput, TOutput> {
|
|
640
|
-
readonly kind: "tool";
|
|
641
|
-
/** JSON Schema for this tool's input — compatible with Core's ToolInfo.schema. */
|
|
642
|
-
readonly schema: ToolSchema;
|
|
643
640
|
/**
|
|
644
641
|
* The `run` body serialized for Core's `inline_deno` tool backend — the exact
|
|
645
642
|
* same technique `defineTurnHook` uses for its `code`. This is what makes a
|
|
@@ -658,6 +655,9 @@ interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, u
|
|
|
658
655
|
* the sandbox form is the second parameter aliased to `host`.
|
|
659
656
|
*/
|
|
660
657
|
readonly code: string;
|
|
658
|
+
readonly kind: "tool";
|
|
659
|
+
/** JSON Schema for this tool's input — compatible with Core's ToolInfo.schema. */
|
|
660
|
+
readonly schema: ToolSchema;
|
|
661
661
|
}
|
|
662
662
|
/**
|
|
663
663
|
* Create a Runnable tool with input validation.
|
|
@@ -690,7 +690,7 @@ interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, u
|
|
|
690
690
|
*/
|
|
691
691
|
declare function defineTool<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown>(options: ToolOptions<TInput, TOutput>): ToolRunnable<TInput, TOutput>;
|
|
692
692
|
/**
|
|
693
|
-
* Convert a {@link ToolRunnable} into a `
|
|
693
|
+
* Convert a {@link ToolRunnable} into a `manifest.json` `kind:"tool"` runnable that
|
|
694
694
|
* ships its `run` body as Core's `inline_deno` backend. The emitted config
|
|
695
695
|
* mirrors Core's `ToolConfig` (`apps/core/src/plugin_manifest/schema.rs`):
|
|
696
696
|
* `{ slug, backend:"inline_deno", code, description?, input_schema }`. Core
|
|
@@ -637,9 +637,6 @@ interface ToolOptions<TInput extends Record<string, unknown>, TOutput> {
|
|
|
637
637
|
* be forwarded verbatim to Core's MCP/ACP layer.
|
|
638
638
|
*/
|
|
639
639
|
interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown> extends Runnable<TInput, TOutput> {
|
|
640
|
-
readonly kind: "tool";
|
|
641
|
-
/** JSON Schema for this tool's input — compatible with Core's ToolInfo.schema. */
|
|
642
|
-
readonly schema: ToolSchema;
|
|
643
640
|
/**
|
|
644
641
|
* The `run` body serialized for Core's `inline_deno` tool backend — the exact
|
|
645
642
|
* same technique `defineTurnHook` uses for its `code`. This is what makes a
|
|
@@ -658,6 +655,9 @@ interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, u
|
|
|
658
655
|
* the sandbox form is the second parameter aliased to `host`.
|
|
659
656
|
*/
|
|
660
657
|
readonly code: string;
|
|
658
|
+
readonly kind: "tool";
|
|
659
|
+
/** JSON Schema for this tool's input — compatible with Core's ToolInfo.schema. */
|
|
660
|
+
readonly schema: ToolSchema;
|
|
661
661
|
}
|
|
662
662
|
/**
|
|
663
663
|
* Create a Runnable tool with input validation.
|
|
@@ -690,7 +690,7 @@ interface ToolRunnable<TInput extends Record<string, unknown> = Record<string, u
|
|
|
690
690
|
*/
|
|
691
691
|
declare function defineTool<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = unknown>(options: ToolOptions<TInput, TOutput>): ToolRunnable<TInput, TOutput>;
|
|
692
692
|
/**
|
|
693
|
-
* Convert a {@link ToolRunnable} into a `
|
|
693
|
+
* Convert a {@link ToolRunnable} into a `manifest.json` `kind:"tool"` runnable that
|
|
694
694
|
* ships its `run` body as Core's `inline_deno` backend. The emitted config
|
|
695
695
|
* mirrors Core's `ToolConfig` (`apps/core/src/plugin_manifest/schema.rs`):
|
|
696
696
|
* `{ slug, backend:"inline_deno", code, description?, input_schema }`. Core
|