@rebon/cli-win32-x64 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +10 -0
- package/README.md +203 -144
- package/THIRD_PARTY_NOTICES.txt +1232 -128
- package/bin/rebon.js +22 -22
- package/lib/windows-managed.js +390 -390
- package/package.json +9 -2
- package/payload/compose-runtime/package.json +17 -17
- package/payload/compose-runtime/payload/compose/shims/dsh-anonymous-user-id.js +10 -10
- package/payload/compose-runtime/payload/compose/shims/dsh-credentials.js +13 -13
- package/payload/compose-runtime/payload/compose/shims/dsh-launch-environment.js +21 -21
- package/payload/compose-runtime/payload/compose/shims/dsh-llm.js +313 -313
- package/payload/compose-runtime/payload/compose/shims/dsh-settings.js +43 -43
- package/payload/compose-runtime/payload/compose/shims/dsh-tools.js +20 -20
- package/payload/compose-runtime/payload/compose/shims/dsh-web.js +10 -10
- package/payload/compose-runtime/payload/compose/shims/llm-adapter.js +32 -32
- package/payload/compose-runtime/payload/compose/shims/zod-lite.js +49 -49
- package/payload/compose-runtime/payload/vendor/cordis/LICENSE +21 -0
- package/payload/compose-runtime/payload/vendor/cordis/index.js +1530 -1530
- package/payload/compose-runtime/payload/vendor/cosmokit/LICENSE +21 -21
- package/payload/compose-runtime/payload/vendor/cosmokit/index.mjs +357 -357
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-agent +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-agent-loop +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-llm-core +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-llm-deepseek +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-logger-console +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-scope +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-session +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-system-prompt +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-timeout +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-timer +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-tool-todo +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-tool-web +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-tools-schema +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-turndown +21 -0
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-turndown-plugin-gfm +21 -0
- package/payload/compose-runtime/payload/vendor/dsh/LICENSE-web-search-exa +21 -21
- package/payload/compose-runtime/payload/vendor/dsh/agent-loop.js +1193 -1193
- package/payload/compose-runtime/payload/vendor/dsh/agent.js +714 -714
- package/payload/compose-runtime/payload/vendor/dsh/llm-core.js +269 -269
- package/payload/compose-runtime/payload/vendor/dsh/llm-deepseek.js +672 -672
- package/payload/compose-runtime/payload/vendor/dsh/logger-console.js +83 -83
- package/payload/compose-runtime/payload/vendor/dsh/scope.js +287 -287
- package/payload/compose-runtime/payload/vendor/dsh/session.js +1668 -1668
- package/payload/compose-runtime/payload/vendor/dsh/system-prompt.js +309 -309
- package/payload/compose-runtime/payload/vendor/dsh/timeout.js +100 -100
- package/payload/compose-runtime/payload/vendor/dsh/timer.js +128 -128
- package/payload/compose-runtime/payload/vendor/dsh/tool-todo.js +143 -143
- package/payload/compose-runtime/payload/vendor/dsh/tool-web.js +1527 -1527
- package/payload/compose-runtime/payload/vendor/dsh/tools-schema.js +849 -849
- package/payload/compose-runtime/payload/vendor/dsh/web-search-exa.js +122 -122
- package/payload/compose-runtime/payload/vendor/eventsource-parser/LICENSE +21 -21
- package/payload/compose-runtime/payload/vendor/eventsource-parser/index.js +177 -177
- package/payload/compose-runtime/payload/vendor/eventsource-parser/stream.js +48 -48
- package/payload/compose-runtime/payload/vendor/schemastery/LICENSE +21 -0
- package/payload/compose-runtime/payload/vendor/schemastery/index.mjs +656 -656
- package/payload/compose-runtime/payload-manifests.json +48 -48
- package/payload/compose-runtime/src/bridge.mjs +193 -195
- package/payload/compose-runtime/src/credentials-runtime.mjs +50 -50
- package/payload/compose-runtime/src/eventsource-stream.mjs +48 -48
- package/payload/compose-runtime/src/index.mjs +7 -7
- package/payload/compose-runtime/src/llm-runtime.mjs +326 -326
- package/payload/compose-runtime/src/loader.mjs +78 -78
- package/payload/compose-runtime/src/loop-assembly.mjs +237 -237
- package/payload/compose-runtime/src/payload.mjs +49 -49
- package/payload/compose-runtime/src/plugin.mjs +59 -59
- package/payload/compose-runtime/src/realm.mjs +223 -223
- package/payload/compose-runtime/src/registry.mjs +191 -191
- package/payload/compose-runtime/src/resolve.mjs +115 -115
- package/payload/compose-runtime/src/systemprompt-runtime.mjs +67 -67
- package/payload/compose-runtime/src/tools-runtime.mjs +239 -239
- package/payload/compose-runtime/src/web-runtime.mjs +209 -209
- package/payload/plugin-host/package.json +11 -11
- package/payload/plugin-host/src/bridge.mjs +64 -64
- package/payload/plugin-host/src/cli.mjs +147 -147
- package/payload/plugin-host/src/framing.mjs +113 -113
- package/payload/plugin-host/src/host.mjs +691 -691
- package/payload/plugin-host/src/json.mjs +208 -208
- package/payload/plugin-host/src/lifecycle.mjs +114 -114
- package/payload/plugin-host/src/loader.mjs +183 -183
- package/payload/plugin-host/src/methods.mjs +377 -377
- package/payload/plugin-host/src/ownership.mjs +57 -57
- package/payload/plugin-host/src/protocol.mjs +129 -129
- package/payload/plugin-host/src/sdk.mjs +7 -7
- package/payload/rebon-boa-helper.exe +0 -0
- package/payload/rebon-browser-mcp.exe +0 -0
- package/payload/rebon-computer-use.exe +0 -0
- package/payload/rebon-lsp-mcp.exe +0 -0
- package/payload/rebon.exe +2 -2
- package/payload/sandbox-win.exe +0 -0
- package/scripts/postinstall.js +112 -112
- package/payload/compose-runtime/payload/compose/credentials-runtime.js +0 -38
- package/payload/compose-runtime/payload/compose/llm-runtime.js +0 -330
- package/payload/compose-runtime/payload/compose/loop-assembly.js +0 -149
- package/payload/compose-runtime/payload/compose/serve-dispatch.js +0 -82
- package/payload/compose-runtime/payload/compose/systemprompt-runtime.js +0 -74
- package/payload/compose-runtime/payload/compose/tools-runtime.js +0 -228
- package/payload/compose-runtime/payload/compose/web-runtime.js +0 -204
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
// Which entry a stray promise belonged to.
|
|
2
|
-
//
|
|
3
|
-
// Node does not stop this process for an unhandled rejection, and
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// Measured on Node 24.19.0,
|
|
9
|
-
// `setTimeout`, `queueMicrotask` and `await`, and the `unhandledRejection`
|
|
10
|
-
// handler reads it directly — so wrapping each `plugin/load` is enough, with no
|
|
11
|
-
// per-promise `async_hooks` and therefore no standing cost.
|
|
12
|
-
//
|
|
13
|
-
// **The store follows the async chain, not the clock**, and that distinction is
|
|
14
|
-
// the whole reason `loading` exists. A timer started during `apply` that
|
|
15
|
-
// rejects ten seconds later still carries its entry, long after that load
|
|
16
|
-
// returned. So there are two questions, not one:
|
|
17
|
-
//
|
|
18
|
-
// * whose is it — `entry`, always, whenever the chain reaches back;
|
|
19
|
-
// * does it fail the load — `loading`, true only while the load is in flight,
|
|
20
|
-
// because a load that already succeeded cannot be un-failed.
|
|
21
|
-
//
|
|
22
|
-
// The flag is flipped on the same object rather than by entering a second
|
|
23
|
-
// store: a new store would not be seen by promises already rooted in the first.
|
|
24
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
25
|
-
|
|
26
|
-
const owners = new AsyncLocalStorage();
|
|
27
|
-
|
|
28
|
-
/// Runs `fn` as `entry`'s work, with the load marked in flight for its
|
|
29
|
-
/// duration. Returns whatever `fn` returns; the flag is cleared either way,
|
|
30
|
-
/// because a load that threw is over too.
|
|
31
|
-
export async function asEntryLoad(entry, fn) {
|
|
32
|
-
const store = { entry, loading: true };
|
|
33
|
-
try {
|
|
34
|
-
const value = await owners.run(store, fn);
|
|
35
|
-
// One turn of the loop before deciding. `unhandledRejection` fires after
|
|
36
|
-
// the microtask queue drains, so a task this load started that failed
|
|
37
|
-
// immediately has not been reported yet at the moment `fn` resolves —
|
|
38
|
-
// checking without this yield would call a broken load a good one.
|
|
39
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
40
|
-
if (store.rejection !== undefined) {
|
|
41
|
-
throw new Error(`[ENTRY_FAILED] ${entry} left a rejection unhandled while loading: ${store.rejection}`);
|
|
42
|
-
}
|
|
43
|
-
return value;
|
|
44
|
-
} finally {
|
|
45
|
-
store.loading = false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/// The entry whose async chain this code is on, or `undefined` outside one.
|
|
50
|
-
///
|
|
51
|
-
/// Undefined is normal and not a failure: a rejection can escape the chain
|
|
52
|
-
/// through an `EventEmitter`, a native callback or a third-party library, and
|
|
53
|
-
/// "could not be determined" is a truthful answer worth keeping distinct from a
|
|
54
|
-
/// wrong one.
|
|
55
|
-
export function currentOwner() {
|
|
56
|
-
return owners.getStore();
|
|
57
|
-
}
|
|
1
|
+
// Which entry a stray promise belonged to.
|
|
2
|
+
//
|
|
3
|
+
// Node does not stop this process for an unhandled rejection, and nothing else
|
|
4
|
+
// looks either: a plugin could start an async task, have it fail, and leave no
|
|
5
|
+
// trace anywhere rebon reads. Making it visible is not the same as attributing
|
|
6
|
+
// it — a failed promise does not carry who created it.
|
|
7
|
+
//
|
|
8
|
+
// Measured on Node 24.19.0, an `AsyncLocalStorage` store survives
|
|
9
|
+
// `setTimeout`, `queueMicrotask` and `await`, and the `unhandledRejection`
|
|
10
|
+
// handler reads it directly — so wrapping each `plugin/load` is enough, with no
|
|
11
|
+
// per-promise `async_hooks` and therefore no standing cost.
|
|
12
|
+
//
|
|
13
|
+
// **The store follows the async chain, not the clock**, and that distinction is
|
|
14
|
+
// the whole reason `loading` exists. A timer started during `apply` that
|
|
15
|
+
// rejects ten seconds later still carries its entry, long after that load
|
|
16
|
+
// returned. So there are two questions, not one:
|
|
17
|
+
//
|
|
18
|
+
// * whose is it — `entry`, always, whenever the chain reaches back;
|
|
19
|
+
// * does it fail the load — `loading`, true only while the load is in flight,
|
|
20
|
+
// because a load that already succeeded cannot be un-failed.
|
|
21
|
+
//
|
|
22
|
+
// The flag is flipped on the same object rather than by entering a second
|
|
23
|
+
// store: a new store would not be seen by promises already rooted in the first.
|
|
24
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
25
|
+
|
|
26
|
+
const owners = new AsyncLocalStorage();
|
|
27
|
+
|
|
28
|
+
/// Runs `fn` as `entry`'s work, with the load marked in flight for its
|
|
29
|
+
/// duration. Returns whatever `fn` returns; the flag is cleared either way,
|
|
30
|
+
/// because a load that threw is over too.
|
|
31
|
+
export async function asEntryLoad(entry, fn) {
|
|
32
|
+
const store = { entry, loading: true };
|
|
33
|
+
try {
|
|
34
|
+
const value = await owners.run(store, fn);
|
|
35
|
+
// One turn of the loop before deciding. `unhandledRejection` fires after
|
|
36
|
+
// the microtask queue drains, so a task this load started that failed
|
|
37
|
+
// immediately has not been reported yet at the moment `fn` resolves —
|
|
38
|
+
// checking without this yield would call a broken load a good one.
|
|
39
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
40
|
+
if (store.rejection !== undefined) {
|
|
41
|
+
throw new Error(`[ENTRY_FAILED] ${entry} left a rejection unhandled while loading: ${store.rejection}`);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
} finally {
|
|
45
|
+
store.loading = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// The entry whose async chain this code is on, or `undefined` outside one.
|
|
50
|
+
///
|
|
51
|
+
/// Undefined is normal and not a failure: a rejection can escape the chain
|
|
52
|
+
/// through an `EventEmitter`, a native callback or a third-party library, and
|
|
53
|
+
/// "could not be determined" is a truthful answer worth keeping distinct from a
|
|
54
|
+
/// wrong one.
|
|
55
|
+
export function currentOwner() {
|
|
56
|
+
return owners.getStore();
|
|
57
|
+
}
|
|
@@ -1,129 +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; }
|
|
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; }
|
|
@@ -1,7 +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
|
-
}
|
|
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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/payload/rebon.exe
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/payload/rebon.exe
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 73743360 bytes
|
|
4
|
+
sha256: 06ad89a7bfb9c17c8b5731da3069a8a08e0d05366d13a0bd4eab86a7ba526cf6
|
package/payload/sandbox-win.exe
CHANGED
|
Binary file
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const childProcess = require("child_process");
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
|
|
7
|
-
const INSTALL_SUPERVISOR_ENV = "REBON_INSTALL_SUPERVISOR";
|
|
8
|
-
|
|
9
|
-
function log(message) {
|
|
10
|
-
console.log(`[rebon postinstall] ${message}`);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function envFlagEnabled(value) {
|
|
14
|
-
return /^(1|true|yes|on)$/i.test(String(value || "").trim());
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function supervisorHintMessage() {
|
|
18
|
-
return "background supervisor scheduler was not registered; run `rebon agents service install` to keep queued jobs moving after login, or set REBON_INSTALL_SUPERVISOR=1 before install to opt in.";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function packageRootFromScript() {
|
|
22
|
-
return path.resolve(__dirname, "..");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function installWindowsPayload(packageRoot) {
|
|
26
|
-
const {
|
|
27
|
-
installManagedPackage,
|
|
28
|
-
readPackageInfo,
|
|
29
|
-
requireManagedBinDir,
|
|
30
|
-
} = require("../lib/windows-managed");
|
|
31
|
-
|
|
32
|
-
requireManagedBinDir();
|
|
33
|
-
const packageInfo = readPackageInfo(packageRoot);
|
|
34
|
-
if (!fs.existsSync(packageInfo.payloadPath)) {
|
|
35
|
-
throw new Error(`payload executable is missing: ${packageInfo.payloadPath}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const managedExe = installManagedPackage(packageInfo);
|
|
39
|
-
if (!managedExe) {
|
|
40
|
-
throw new Error("could not install payload executable into managed bin directory");
|
|
41
|
-
}
|
|
42
|
-
return managedExe;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function packageExecutable(packageRoot, platform) {
|
|
46
|
-
if (platform === "win32") {
|
|
47
|
-
return installWindowsPayload(packageRoot);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const exePath = path.join(packageRoot, "bin", "rebon");
|
|
51
|
-
if (!fs.existsSync(exePath)) {
|
|
52
|
-
throw new Error(`payload executable is missing: ${exePath}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
fs.chmodSync(exePath, 0o755);
|
|
57
|
-
} catch (_) {
|
|
58
|
-
}
|
|
59
|
-
return exePath;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function installSupervisorScheduler(exePath, spawnSyncImpl) {
|
|
63
|
-
const result = spawnSyncImpl(exePath, ["agents", "service", "install"], {
|
|
64
|
-
stdio: "inherit",
|
|
65
|
-
windowsHide: true,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
if (result.error) {
|
|
69
|
-
throw result.error;
|
|
70
|
-
}
|
|
71
|
-
if (result.status !== 0) {
|
|
72
|
-
throw new Error(`supervisor scheduler install exited with status ${result.status}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function runPostinstall(options = {}) {
|
|
77
|
-
const platform = options.platform || process.platform;
|
|
78
|
-
const env = options.env || process.env;
|
|
79
|
-
const logger = options.log || log;
|
|
80
|
-
const spawnSyncImpl = options.spawnSync || childProcess.spawnSync;
|
|
81
|
-
const packageRoot = options.packageRoot || packageRootFromScript();
|
|
82
|
-
const exePath = packageExecutable(packageRoot, platform);
|
|
83
|
-
|
|
84
|
-
logger(`installed ${exePath}`);
|
|
85
|
-
|
|
86
|
-
if (envFlagEnabled(env[INSTALL_SUPERVISOR_ENV])) {
|
|
87
|
-
logger(`registering per-user background supervisor scheduler because ${INSTALL_SUPERVISOR_ENV} is enabled`);
|
|
88
|
-
installSupervisorScheduler(exePath, spawnSyncImpl);
|
|
89
|
-
logger("registered background supervisor scheduler");
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
logger(supervisorHintMessage());
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
if (require.main === module) {
|
|
98
|
-
runPostinstall();
|
|
99
|
-
}
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error(`[rebon postinstall] ${error.message}`);
|
|
102
|
-
process.exit(1);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
module.exports = {
|
|
106
|
-
INSTALL_SUPERVISOR_ENV,
|
|
107
|
-
envFlagEnabled,
|
|
108
|
-
installSupervisorScheduler,
|
|
109
|
-
packageExecutable,
|
|
110
|
-
runPostinstall,
|
|
111
|
-
supervisorHintMessage,
|
|
112
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const childProcess = require("child_process");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const INSTALL_SUPERVISOR_ENV = "REBON_INSTALL_SUPERVISOR";
|
|
8
|
+
|
|
9
|
+
function log(message) {
|
|
10
|
+
console.log(`[rebon postinstall] ${message}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function envFlagEnabled(value) {
|
|
14
|
+
return /^(1|true|yes|on)$/i.test(String(value || "").trim());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function supervisorHintMessage() {
|
|
18
|
+
return "background supervisor scheduler was not registered; run `rebon agents service install` to keep queued jobs moving after login, or set REBON_INSTALL_SUPERVISOR=1 before install to opt in.";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function packageRootFromScript() {
|
|
22
|
+
return path.resolve(__dirname, "..");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function installWindowsPayload(packageRoot) {
|
|
26
|
+
const {
|
|
27
|
+
installManagedPackage,
|
|
28
|
+
readPackageInfo,
|
|
29
|
+
requireManagedBinDir,
|
|
30
|
+
} = require("../lib/windows-managed");
|
|
31
|
+
|
|
32
|
+
requireManagedBinDir();
|
|
33
|
+
const packageInfo = readPackageInfo(packageRoot);
|
|
34
|
+
if (!fs.existsSync(packageInfo.payloadPath)) {
|
|
35
|
+
throw new Error(`payload executable is missing: ${packageInfo.payloadPath}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const managedExe = installManagedPackage(packageInfo);
|
|
39
|
+
if (!managedExe) {
|
|
40
|
+
throw new Error("could not install payload executable into managed bin directory");
|
|
41
|
+
}
|
|
42
|
+
return managedExe;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function packageExecutable(packageRoot, platform) {
|
|
46
|
+
if (platform === "win32") {
|
|
47
|
+
return installWindowsPayload(packageRoot);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const exePath = path.join(packageRoot, "bin", "rebon");
|
|
51
|
+
if (!fs.existsSync(exePath)) {
|
|
52
|
+
throw new Error(`payload executable is missing: ${exePath}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
fs.chmodSync(exePath, 0o755);
|
|
57
|
+
} catch (_) {
|
|
58
|
+
}
|
|
59
|
+
return exePath;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function installSupervisorScheduler(exePath, spawnSyncImpl) {
|
|
63
|
+
const result = spawnSyncImpl(exePath, ["agents", "service", "install"], {
|
|
64
|
+
stdio: "inherit",
|
|
65
|
+
windowsHide: true,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (result.error) {
|
|
69
|
+
throw result.error;
|
|
70
|
+
}
|
|
71
|
+
if (result.status !== 0) {
|
|
72
|
+
throw new Error(`supervisor scheduler install exited with status ${result.status}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function runPostinstall(options = {}) {
|
|
77
|
+
const platform = options.platform || process.platform;
|
|
78
|
+
const env = options.env || process.env;
|
|
79
|
+
const logger = options.log || log;
|
|
80
|
+
const spawnSyncImpl = options.spawnSync || childProcess.spawnSync;
|
|
81
|
+
const packageRoot = options.packageRoot || packageRootFromScript();
|
|
82
|
+
const exePath = packageExecutable(packageRoot, platform);
|
|
83
|
+
|
|
84
|
+
logger(`installed ${exePath}`);
|
|
85
|
+
|
|
86
|
+
if (envFlagEnabled(env[INSTALL_SUPERVISOR_ENV])) {
|
|
87
|
+
logger(`registering per-user background supervisor scheduler because ${INSTALL_SUPERVISOR_ENV} is enabled`);
|
|
88
|
+
installSupervisorScheduler(exePath, spawnSyncImpl);
|
|
89
|
+
logger("registered background supervisor scheduler");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
logger(supervisorHintMessage());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
if (require.main === module) {
|
|
98
|
+
runPostinstall();
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`[rebon postinstall] ${error.message}`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = {
|
|
106
|
+
INSTALL_SUPERVISOR_ENV,
|
|
107
|
+
envFlagEnabled,
|
|
108
|
+
installSupervisorScheduler,
|
|
109
|
+
packageExecutable,
|
|
110
|
+
runPostinstall,
|
|
111
|
+
supervisorHintMessage,
|
|
112
|
+
};
|