@rebon/cli-linux-x64 0.17.3 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/bin/compose-runtime/package.json +17 -0
  2. package/bin/compose-runtime/payload/compose/credentials-runtime.js +38 -0
  3. package/bin/compose-runtime/payload/compose/llm-runtime.js +330 -0
  4. package/bin/compose-runtime/payload/compose/loop-assembly.js +149 -0
  5. package/bin/compose-runtime/payload/compose/serve-dispatch.js +82 -0
  6. package/bin/compose-runtime/payload/compose/shims/dsh-anonymous-user-id.js +10 -0
  7. package/bin/compose-runtime/payload/compose/shims/dsh-credentials.js +13 -0
  8. package/bin/compose-runtime/payload/compose/shims/dsh-launch-environment.js +21 -0
  9. package/bin/compose-runtime/payload/compose/shims/dsh-llm.js +313 -0
  10. package/bin/compose-runtime/payload/compose/shims/dsh-settings.js +43 -0
  11. package/bin/compose-runtime/payload/compose/shims/dsh-tools.js +20 -0
  12. package/bin/compose-runtime/payload/compose/shims/dsh-web.js +10 -0
  13. package/bin/compose-runtime/payload/compose/shims/llm-adapter.js +32 -0
  14. package/bin/compose-runtime/payload/compose/shims/zod-lite.js +49 -0
  15. package/bin/compose-runtime/payload/compose/systemprompt-runtime.js +74 -0
  16. package/bin/compose-runtime/payload/compose/tools-runtime.js +228 -0
  17. package/bin/compose-runtime/payload/compose/web-runtime.js +204 -0
  18. package/bin/compose-runtime/payload/vendor/cordis/index.js +1530 -0
  19. package/bin/compose-runtime/payload/vendor/cosmokit/LICENSE +21 -0
  20. package/bin/compose-runtime/payload/vendor/cosmokit/index.mjs +357 -0
  21. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent +21 -0
  22. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent-loop +21 -0
  23. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-core +21 -0
  24. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-deepseek +21 -0
  25. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-logger-console +21 -0
  26. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-scope +21 -0
  27. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-session +21 -0
  28. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-system-prompt +21 -0
  29. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timeout +21 -0
  30. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timer +21 -0
  31. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-todo +21 -0
  32. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-web +21 -0
  33. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tools-schema +21 -0
  34. package/bin/compose-runtime/payload/vendor/dsh/LICENSE-web-search-exa +21 -0
  35. package/bin/compose-runtime/payload/vendor/dsh/agent-loop.js +1193 -0
  36. package/bin/compose-runtime/payload/vendor/dsh/agent.js +714 -0
  37. package/bin/compose-runtime/payload/vendor/dsh/llm-core.js +269 -0
  38. package/bin/compose-runtime/payload/vendor/dsh/llm-deepseek.js +672 -0
  39. package/bin/compose-runtime/payload/vendor/dsh/logger-console.js +83 -0
  40. package/bin/compose-runtime/payload/vendor/dsh/scope.js +287 -0
  41. package/bin/compose-runtime/payload/vendor/dsh/session.js +1668 -0
  42. package/bin/compose-runtime/payload/vendor/dsh/system-prompt.js +309 -0
  43. package/bin/compose-runtime/payload/vendor/dsh/timeout.js +100 -0
  44. package/bin/compose-runtime/payload/vendor/dsh/timer.js +128 -0
  45. package/bin/compose-runtime/payload/vendor/dsh/tool-todo.js +143 -0
  46. package/bin/compose-runtime/payload/vendor/dsh/tool-web.js +1527 -0
  47. package/bin/compose-runtime/payload/vendor/dsh/tools-schema.js +849 -0
  48. package/bin/compose-runtime/payload/vendor/dsh/web-search-exa.js +122 -0
  49. package/bin/compose-runtime/payload/vendor/eventsource-parser/LICENSE +21 -0
  50. package/bin/compose-runtime/payload/vendor/eventsource-parser/index.js +177 -0
  51. package/bin/compose-runtime/payload/vendor/eventsource-parser/stream.js +48 -0
  52. package/bin/compose-runtime/payload/vendor/schemastery/index.mjs +656 -0
  53. package/bin/compose-runtime/payload-manifests.json +48 -0
  54. package/bin/compose-runtime/src/bridge.mjs +142 -0
  55. package/bin/compose-runtime/src/credentials-runtime.mjs +50 -0
  56. package/bin/compose-runtime/src/eventsource-stream.mjs +48 -0
  57. package/bin/compose-runtime/src/index.mjs +7 -0
  58. package/bin/compose-runtime/src/llm-runtime.mjs +326 -0
  59. package/bin/compose-runtime/src/loader.mjs +78 -0
  60. package/bin/compose-runtime/src/loop-assembly.mjs +237 -0
  61. package/bin/compose-runtime/src/payload.mjs +49 -0
  62. package/bin/compose-runtime/src/plugin.mjs +59 -0
  63. package/bin/compose-runtime/src/realm.mjs +223 -0
  64. package/bin/compose-runtime/src/registry.mjs +191 -0
  65. package/bin/compose-runtime/src/resolve.mjs +115 -0
  66. package/bin/compose-runtime/src/systemprompt-runtime.mjs +67 -0
  67. package/bin/compose-runtime/src/tools-runtime.mjs +239 -0
  68. package/bin/compose-runtime/src/web-runtime.mjs +209 -0
  69. package/bin/plugin-host/package.json +11 -0
  70. package/bin/plugin-host/src/bridge.mjs +64 -0
  71. package/bin/plugin-host/src/cli.mjs +105 -0
  72. package/bin/plugin-host/src/framing.mjs +113 -0
  73. package/bin/plugin-host/src/host.mjs +624 -0
  74. package/bin/plugin-host/src/json.mjs +196 -0
  75. package/bin/plugin-host/src/lifecycle.mjs +114 -0
  76. package/bin/plugin-host/src/loader.mjs +142 -0
  77. package/bin/plugin-host/src/methods.mjs +256 -0
  78. package/bin/plugin-host/src/protocol.mjs +129 -0
  79. package/bin/plugin-host/src/sdk.mjs +7 -0
  80. package/bin/rebon +0 -0
  81. package/bin/rebon-boa-helper +0 -0
  82. package/package.json +4 -1
@@ -0,0 +1,256 @@
1
+ // Payload schemas for the methods that carry one. Mirrors
2
+ // `crates/rebon-plugin-protocol/src/methods.rs`; the shared corpus at
3
+ // `crates/rebon-plugin-protocol/tests/fixtures/v1/methods.json` is what keeps
4
+ // the two honest. Codes are the bracketed tokens that corpus pins, which is a
5
+ // different vocabulary from `protocol.mjs`'s framing codes on purpose: they
6
+ // answer different questions and are pinned by different corpora.
7
+ //
8
+ // Validation order is part of the contract. A payload with two problems reports
9
+ // the first one in declaration order, and both languages must pick the same one.
10
+ import { ProtocolError, PLATFORM_PLUGIN_ID, plain } from './protocol.mjs';
11
+
12
+ export const PLUGIN_LOAD_METHOD = 'plugin/load';
13
+ export const PLUGIN_UNLOAD_METHOD = 'plugin/unload';
14
+ export const SERVICE_CALL_METHOD = 'service/call';
15
+ export const EVENT_DELIVER_METHOD = 'event/deliver';
16
+ export const EVENT_SUBSCRIBE_METHOD = 'event/subscribe';
17
+ export const EVENT_UNSUBSCRIBE_METHOD = 'event/unsubscribe';
18
+ export const EVENT_EMIT_METHOD = 'event/emit';
19
+ export const TOOL_INVOKE_METHOD = 'tool/invoke';
20
+ export const LLM_STREAM_METHOD = 'llm/stream';
21
+ export const TOOL_CALL_METHOD = 'tool/call';
22
+ export const SEAT_CALL_METHOD = 'seat/call';
23
+
24
+ export const MAX_NAME_BYTES = 128;
25
+ export const MAX_DECLARATIONS = 256;
26
+ // A tool description is read by a model, so it is spent out of every turn's
27
+ // context budget for as long as the tool is offered. The bound is part of the
28
+ // contract, not a defensive habit.
29
+ export const MAX_DESCRIPTION_BYTES = 4096;
30
+
31
+ // Rust bounds a name by its UTF-8 byte length. Measuring UTF-16 units here
32
+ // would accept names Rust rejects, which is exactly the kind of drift a plugin
33
+ // author discovers only on the other host.
34
+ const byteLength = (value) => Buffer.byteLength(value, 'utf8');
35
+ // `\p{Cc}` is the Unicode category Rust's `char::is_control` tests.
36
+ const CONTROL = /\p{Cc}/u;
37
+
38
+ function shape(value, required, optional, where) {
39
+ if (!plain(value)) throw new ProtocolError('[WRONG_SHAPE]', `${where} must be a plain object`);
40
+ const known = new Set([...required, ...optional]);
41
+ for (const key of Object.keys(value)) {
42
+ if (!known.has(key)) throw new ProtocolError('[UNKNOWN_FIELD]', `${where} has unknown field ${JSON.stringify(key)}`);
43
+ }
44
+ for (const key of required) {
45
+ if (!Object.prototype.hasOwnProperty.call(value, key)) {
46
+ throw new ProtocolError('[MISSING_FIELD]', `${where} is missing field ${JSON.stringify(key)}`);
47
+ }
48
+ }
49
+ }
50
+
51
+ function string(value, where) {
52
+ if (typeof value !== 'string') throw new ProtocolError('[WRONG_SHAPE]', `${where} must be a string`);
53
+ return value;
54
+ }
55
+
56
+ export function validateName(kind, name) {
57
+ if (name.length === 0) throw new ProtocolError('[EMPTY_NAME]', `${kind} name must not be empty`);
58
+ if (byteLength(name) > MAX_NAME_BYTES) throw new ProtocolError('[NAME_TOO_LONG]', `${kind} name is longer than ${MAX_NAME_BYTES} bytes`);
59
+ if (CONTROL.test(name)) throw new ProtocolError('[CONTROL_CHARACTER]', `${kind} name contains a control character`);
60
+ return name;
61
+ }
62
+
63
+ export const MAX_PATH_BYTES = 4096;
64
+
65
+ // `path.isAbsolute` answers for the platform it runs on, which is right for
66
+ // using a path and wrong for agreeing about one: this rule is read by both
67
+ // languages on whichever machine runs CI. A supervisor and its host always run
68
+ // on the same machine, so both spellings count.
69
+ const looksAbsolute = (value) => /^[/\\]/.test(value) || /^[A-Za-z]:[/\\]/.test(value);
70
+
71
+ function pathShape(kind, value) {
72
+ if (value.length === 0) throw new ProtocolError('[EMPTY_NAME]', `${kind} path must not be empty`);
73
+ if (byteLength(value) > MAX_PATH_BYTES) throw new ProtocolError('[PATH_TOO_LONG]', `${kind} path is longer than ${MAX_PATH_BYTES} bytes`);
74
+ if (CONTROL.test(value)) throw new ProtocolError('[CONTROL_CHARACTER]', `${kind} path contains a control character`);
75
+ return value;
76
+ }
77
+
78
+ export function validateAbsolutePath(kind, value) {
79
+ pathShape(kind, value);
80
+ if (!looksAbsolute(value)) throw new ProtocolError('[PATH_NOT_ABSOLUTE]', `${kind} path must be absolute`);
81
+ return value;
82
+ }
83
+
84
+ export function validateRelativePath(kind, value) {
85
+ pathShape(kind, value);
86
+ if (looksAbsolute(value)) throw new ProtocolError('[PATH_NOT_RELATIVE]', `${kind} path must be relative to the package root`);
87
+ if (value.includes('\\') || value.split('/').some((part) => part === '..' || part === '')) {
88
+ throw new ProtocolError('[PATH_ESCAPES]', `${kind} path leaves the package root`);
89
+ }
90
+ return value;
91
+ }
92
+
93
+ export function validatePluginId(pluginId) {
94
+ validateName('plugin', pluginId);
95
+ if (pluginId === PLATFORM_PLUGIN_ID) throw new ProtocolError('[RESERVED_PLUGIN_ID]', `plugin id ${PLATFORM_PLUGIN_ID} is reserved for platform control traffic`);
96
+ return pluginId;
97
+ }
98
+
99
+ // Absent lists default to empty, matching serde's `#[serde(default)]`. The
100
+ // length bound is checked before the entries, and each entry's own validity
101
+ // before the duplicate check — same order as Rust.
102
+ function declarations(value, kind, where) {
103
+ if (value === undefined) return [];
104
+ if (!Array.isArray(value)) throw new ProtocolError('[WRONG_SHAPE]', `${where} must be an array`);
105
+ if (value.length > MAX_DECLARATIONS) throw new ProtocolError('[TOO_MANY_DECLARATIONS]', `${where} declares ${value.length} entries, over the ${MAX_DECLARATIONS} limit`);
106
+ const seen = new Set();
107
+ for (const entry of value) {
108
+ validateName(kind, string(entry, `${where} entry`));
109
+ if (seen.has(entry)) throw new ProtocolError('[DUPLICATE_DECLARATION]', `${where} declares ${JSON.stringify(entry)} twice`);
110
+ seen.add(entry);
111
+ }
112
+ return Object.freeze([...value]);
113
+ }
114
+
115
+ export function pluginLoadRequest(input) {
116
+ shape(input, ['pluginId', 'root', 'entry'], ['services', 'eventTopics', 'publishedTopics', 'llmProviders', 'tools', 'invokableTools', 'seats', 'config'], 'plugin/load payload');
117
+ return Object.freeze({
118
+ pluginId: validatePluginId(string(input.pluginId, 'pluginId')),
119
+ root: validateAbsolutePath('root', string(input.root, 'root')),
120
+ entry: validateRelativePath('entry', string(input.entry, 'entry')),
121
+ services: declarations(input.services, 'service', 'services'),
122
+ eventTopics: declarations(input.eventTopics, 'topic', 'eventTopics'),
123
+ // What it may publish, as opposed to what it listens to. Two powers, two
124
+ // lists — the same separation `tools` and `invokableTools` keep.
125
+ publishedTopics: declarations(input.publishedTopics, 'topic', 'publishedTopics'),
126
+ llmProviders: declarations(input.llmProviders, 'provider', 'llmProviders'),
127
+ tools: declarations(input.tools, 'tool', 'tools'),
128
+ // The one declared list with no registered counterpart: a plugin calls
129
+ // these rather than providing them, which is why its name says so.
130
+ invokableTools: declarations(input.invokableTools, 'tool', 'invokableTools'),
131
+ seats: declarations(input.seats, 'seat', 'seats'),
132
+ // Opaque: what a plugin's configuration means is the plugin's contract, and
133
+ // an absent one is `null` rather than a missing field — the same rule every
134
+ // other payload slot follows.
135
+ config: input.config === undefined ? null : input.config,
136
+ });
137
+ }
138
+
139
+ export function pluginToolDefinition(input) {
140
+ shape(input, ['name', 'description', 'inputSchema'], [], 'tool definition');
141
+ const name = validateName('tool', string(input.name, 'name'));
142
+ const description = string(input.description, 'description');
143
+ if (description.length === 0) throw new ProtocolError('[EMPTY_DESCRIPTION]', `tool ${JSON.stringify(name)} has no description, so a model cannot know when to use it`);
144
+ if (byteLength(description) > MAX_DESCRIPTION_BYTES) throw new ProtocolError('[DESCRIPTION_TOO_LONG]', `tool ${JSON.stringify(name)} has a description over the ${MAX_DESCRIPTION_BYTES} byte limit`);
145
+ return Object.freeze({ name, description, inputSchema: input.inputSchema });
146
+ }
147
+
148
+ function toolDefinitions(value, where) {
149
+ if (value === undefined) return [];
150
+ if (!Array.isArray(value)) throw new ProtocolError('[WRONG_SHAPE]', `${where} must be an array`);
151
+ if (value.length > MAX_DECLARATIONS) throw new ProtocolError('[TOO_MANY_DECLARATIONS]', `${where} declares ${value.length} entries, over the ${MAX_DECLARATIONS} limit`);
152
+ const seen = new Set();
153
+ const out = [];
154
+ for (const entry of value) {
155
+ const tool = pluginToolDefinition(entry);
156
+ if (seen.has(tool.name)) throw new ProtocolError('[DUPLICATE_DECLARATION]', `${where} declares ${JSON.stringify(tool.name)} twice`);
157
+ seen.add(tool.name);
158
+ out.push(tool);
159
+ }
160
+ return Object.freeze(out);
161
+ }
162
+
163
+ export function pluginReadyReport(input) {
164
+ shape(input, ['pluginId'], ['services', 'eventTopics', 'llmProviders', 'tools'], 'plugin/load terminal payload');
165
+ return Object.freeze({
166
+ pluginId: validatePluginId(string(input.pluginId, 'pluginId')),
167
+ services: declarations(input.services, 'service', 'services'),
168
+ eventTopics: declarations(input.eventTopics, 'topic', 'eventTopics'),
169
+ llmProviders: declarations(input.llmProviders, 'provider', 'llmProviders'),
170
+ tools: toolDefinitions(input.tools, 'tools'),
171
+ });
172
+ }
173
+
174
+ export function pluginUnloadRequest(input) {
175
+ shape(input, ['pluginId'], [], 'plugin/unload payload');
176
+ return Object.freeze({ pluginId: validatePluginId(string(input.pluginId, 'pluginId')) });
177
+ }
178
+
179
+ export function pluginDrainReport(input) {
180
+ shape(input, ['pluginId'], ['outstandingCalls', 'revokedSubscriptions'], 'plugin/unload terminal payload');
181
+ return Object.freeze({
182
+ pluginId: validatePluginId(string(input.pluginId, 'pluginId')),
183
+ outstandingCalls: declarations(input.outstandingCalls, 'call', 'outstandingCalls'),
184
+ revokedSubscriptions: declarations(input.revokedSubscriptions, 'subscription', 'revokedSubscriptions'),
185
+ });
186
+ }
187
+
188
+ export function serviceCallRequest(input) {
189
+ shape(input, ['service', 'request'], [], 'service/call payload');
190
+ return Object.freeze({ service: validateName('service', string(input.service, 'service')), request: input.request });
191
+ }
192
+
193
+ export function eventSubscribeRequest(input) {
194
+ shape(input, ['subscription', 'topic'], [], 'event/subscribe payload');
195
+ return Object.freeze({
196
+ subscription: validateName('subscription', string(input.subscription, 'subscription')),
197
+ topic: validateName('topic', string(input.topic, 'topic')),
198
+ });
199
+ }
200
+
201
+ export function eventUnsubscribeRequest(input) {
202
+ shape(input, ['subscription'], [], 'event/unsubscribe payload');
203
+ return Object.freeze({ subscription: validateName('subscription', string(input.subscription, 'subscription')) });
204
+ }
205
+
206
+ export function eventDelivery(input) {
207
+ shape(input, ['subscription', 'topic', 'event'], [], 'event/deliver payload');
208
+ return Object.freeze({
209
+ subscription: validateName('subscription', string(input.subscription, 'subscription')),
210
+ topic: validateName('topic', string(input.topic, 'topic')),
211
+ event: input.event,
212
+ });
213
+ }
214
+
215
+ export function eventEmitRequest(input) {
216
+ shape(input, ['topic', 'event'], [], 'event/emit payload');
217
+ return Object.freeze({
218
+ topic: validateName('topic', string(input.topic, 'topic')),
219
+ event: input.event,
220
+ });
221
+ }
222
+
223
+ export function toolInvokeRequest(input) {
224
+ shape(input, ['tool', 'input'], [], 'tool/invoke payload');
225
+ return Object.freeze({ tool: validateName('tool', string(input.tool, 'tool')), input: input.input });
226
+ }
227
+
228
+ export function llmStreamRequest(input) {
229
+ shape(input, ['provider', 'request'], [], 'llm/stream payload');
230
+ return Object.freeze({ provider: validateName('provider', string(input.provider, 'provider')), request: input.request });
231
+ }
232
+
233
+ export function seatCallRequest(input) {
234
+ shape(input, ['seat', 'method', 'params'], [], 'seat/call payload');
235
+ return Object.freeze({
236
+ seat: validateName('seat', string(input.seat, 'seat')),
237
+ method: validateName('method', string(input.method, 'method')),
238
+ params: input.params,
239
+ });
240
+ }
241
+
242
+ /// The corpus's `kind` names, so a shared case table can dispatch by name.
243
+ export const VALIDATORS = Object.freeze({
244
+ plugin_load: pluginLoadRequest,
245
+ plugin_ready: pluginReadyReport,
246
+ plugin_unload: pluginUnloadRequest,
247
+ plugin_drain: pluginDrainReport,
248
+ service_call: serviceCallRequest,
249
+ event_subscribe: eventSubscribeRequest,
250
+ event_unsubscribe: eventUnsubscribeRequest,
251
+ event_deliver: eventDelivery,
252
+ event_emit: eventEmitRequest,
253
+ tool_invoke: toolInvokeRequest,
254
+ llm_stream: llmStreamRequest,
255
+ seat_call: seatCallRequest,
256
+ });
@@ -0,0 +1,129 @@
1
+ export const PROTOCOL_VERSION = 1;
2
+ export const MAX_FRAME_BYTES = 8 * 1024 * 1024;
3
+ export const MAX_SAFE_WIRE_INTEGER = Number.MAX_SAFE_INTEGER;
4
+ export const PLATFORM_PLUGIN_ID = '$rebon/platform';
5
+ export const PLATFORM_CONTROL_SCOPE_ID = '$rebon/control';
6
+ export const PLATFORM_CONTROL_GENERATION = 0;
7
+ export const CALL_CANCEL_METHOD = 'call/cancel';
8
+
9
+ export class ProtocolError extends Error {
10
+ constructor(code, message) { super(message); this.name = 'ProtocolError'; this.code = code; }
11
+ }
12
+
13
+ export class RawJsonNumber {
14
+ constructor(raw) { this.raw = raw; Object.freeze(this); }
15
+ valueOf() { return Number(this.raw); }
16
+ toJSON() { throw new ProtocolError('raw_number', 'raw JSON number cannot be emitted without an explicit schema conversion'); }
17
+ }
18
+
19
+ const own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
20
+ export const plain = (value) => value !== null && typeof value === 'object' && !Array.isArray(value)
21
+ && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
22
+
23
+ function exact(value, keys, where) {
24
+ if (!plain(value)) throw new ProtocolError('wrong_shape', `${where} must be a plain object`);
25
+ const actual = Object.keys(value).sort();
26
+ const expected = [...keys].sort();
27
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
28
+ throw new ProtocolError('unknown_field', `${where} must have exactly: ${expected.join(', ')}`);
29
+ }
30
+ for (const key of expected) if (!own(value, key)) throw new ProtocolError('wrong_shape', `${where}.${key} is required`);
31
+ }
32
+
33
+ function wireInteger(value, field) {
34
+ if (!Number.isSafeInteger(value) || value < 0) throw new ProtocolError('unsafe_integer', `${field} must be a safe non-negative integer`);
35
+ return Object.is(value, -0) ? 0 : value;
36
+ }
37
+ function string(value, field) {
38
+ if (typeof value !== 'string') throw new ProtocolError('wrong_shape', `${field} must be a string`);
39
+ return value;
40
+ }
41
+ function cloneJson(value, seen = new Set()) {
42
+ if (value instanceof RawJsonNumber) return value;
43
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
44
+ if (typeof value === 'number') {
45
+ if (!Number.isFinite(value)) throw new ProtocolError('invalid_payload', 'payload numbers must be finite JSON numbers');
46
+ return Object.is(value, -0) ? 0 : value;
47
+ }
48
+ if (typeof value !== 'object') throw new ProtocolError('invalid_payload', 'payload must be JSON-compatible');
49
+ if (seen.has(value)) throw new ProtocolError('invalid_payload', 'payload must not be cyclic');
50
+ seen.add(value);
51
+ let copy;
52
+ if (Array.isArray(value)) copy = value.map((item) => cloneJson(item, seen));
53
+ else {
54
+ if (!plain(value)) throw new ProtocolError('invalid_payload', 'payload objects must have a plain prototype');
55
+ copy = Object.create(null);
56
+ for (const key of Object.keys(value)) copy[key] = cloneJson(value[key], seen);
57
+ }
58
+ seen.delete(value);
59
+ return Object.freeze(copy);
60
+ }
61
+
62
+ /// Every name a message object may carry, whatever its variant.
63
+ const MESSAGE_FIELDS = ['type', 'method', 'status', 'payload'];
64
+
65
+ /// Checks a message object in the order the other language does.
66
+ ///
67
+ /// The order is the contract, not an implementation detail. A name nobody knows
68
+ /// is refused before the variant is even decided; then the fields every variant
69
+ /// needs; only then a name that is real but belongs to a different variant.
70
+ /// Collapsing these into one set comparison would report "you left out payload"
71
+ /// as "unknown field", which sends a sender to the wrong place — and would say
72
+ /// something different from the Rust side for the same bytes.
73
+ function messageShape(message, fields) {
74
+ if (!plain(message)) throw new ProtocolError('wrong_shape', 'message must be a plain object');
75
+ for (const key of Object.keys(message)) {
76
+ if (!MESSAGE_FIELDS.includes(key)) throw new ProtocolError('unknown_field', `message has unknown field ${JSON.stringify(key)}`);
77
+ }
78
+ for (const key of ['type', 'payload']) {
79
+ if (!own(message, key)) throw new ProtocolError('wrong_shape', `message.${key} is required`);
80
+ }
81
+ for (const key of Object.keys(message)) {
82
+ if (!fields.includes(key)) throw new ProtocolError('unknown_field', `message must have exactly: ${[...fields].sort().join(', ')}`);
83
+ }
84
+ for (const key of fields) {
85
+ if (!own(message, key)) throw new ProtocolError('wrong_shape', `message.${key} is required`);
86
+ }
87
+ }
88
+
89
+ export function validateEnvelope(input) {
90
+ exact(input, ['protocol_version','host_epoch','plugin_id','scope_id','scope_generation','call_id','message'], 'envelope');
91
+ if (input.protocol_version !== PROTOCOL_VERSION) throw new ProtocolError('bad_version', `protocol_version must be ${PROTOCOL_VERSION}`);
92
+ const type = plain(input.message) ? input.message.type : undefined;
93
+ // A chunk carries no method: it belongs to the call whose identity it rides,
94
+ // and that call's method already said what it means.
95
+ const fields = type === 'terminal' ? ['type','status','payload']
96
+ : type === 'chunk' ? ['type','payload']
97
+ : ['type','method','payload'];
98
+ if (!['request','terminal','notification','chunk'].includes(type)) throw new ProtocolError('wrong_shape', 'message.type is invalid');
99
+ messageShape(input.message, fields);
100
+ const message = type === 'terminal'
101
+ ? Object.freeze({ type, status: terminalStatus(input.message.status), payload: cloneJson(input.message.payload) })
102
+ : type === 'chunk'
103
+ ? Object.freeze({ type, payload: cloneJson(input.message.payload) })
104
+ : Object.freeze({ type, method: string(input.message.method, 'message.method'), payload: cloneJson(input.message.payload) });
105
+ return Object.freeze({
106
+ protocol_version: PROTOCOL_VERSION,
107
+ host_epoch: wireInteger(input.host_epoch, 'host_epoch'),
108
+ plugin_id: string(input.plugin_id, 'plugin_id'),
109
+ scope_id: string(input.scope_id, 'scope_id'),
110
+ scope_generation: wireInteger(input.scope_generation, 'scope_generation'),
111
+ call_id: string(input.call_id, 'call_id'),
112
+ message,
113
+ });
114
+ }
115
+ function terminalStatus(status) {
116
+ if (!['success','error','cancelled'].includes(status)) throw new ProtocolError('bad_status', 'terminal status is invalid');
117
+ return status;
118
+ }
119
+ export function identityOf(envelope) {
120
+ return Object.freeze({ host_epoch: envelope.host_epoch, plugin_id: envelope.plugin_id, scope_id: envelope.scope_id,
121
+ scope_generation: envelope.scope_generation, call_id: envelope.call_id });
122
+ }
123
+ export function envelope(identity, message) { return validateEnvelope({ protocol_version: PROTOCOL_VERSION, ...identity, message }); }
124
+ export function terminal(identity, status, payload) { return envelope(identity, { type: 'terminal', status, payload }); }
125
+ export function request(identity, method, payload) { return envelope(identity, { type: 'request', method, payload }); }
126
+ export function notification(identity, method, payload) { return envelope(identity, { type: 'notification', method, payload }); }
127
+ export function chunk(identity, payload) { return envelope(identity, { type: 'chunk', payload }); }
128
+ export function cancel(target) { return notification(target, CALL_CANCEL_METHOD, null); }
129
+ export function isPlatformControl(value) { return value.plugin_id === PLATFORM_PLUGIN_ID && value.scope_id === PLATFORM_CONTROL_SCOPE_ID && value.scope_generation === 0; }
@@ -0,0 +1,7 @@
1
+ export function createPluginApi(boundBridge) {
2
+ if (!boundBridge || typeof boundBridge.call !== 'function' || typeof boundBridge.notify !== 'function') throw new TypeError('boundBridge is invalid');
3
+ return Object.freeze({
4
+ call(method, payload, options = {}) { return boundBridge.call(method, payload, options); },
5
+ notify(method, payload) { return boundBridge.notify(method, payload); },
6
+ });
7
+ }
package/bin/rebon CHANGED
Binary file
Binary file
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@rebon/cli-linux-x64",
3
- "version": "0.17.3",
3
+ "version": "0.18.0",
4
4
  "description": "Agent cli for coding and more.",
5
5
  "license": "MIT OR Apache-2.0",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
6
9
  "bin": {
7
10
  "rebon": "bin/rebon"
8
11
  },