blun-king-cli 9.1.470 → 9.1.471

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.
@@ -0,0 +1,198 @@
1
+ import path from 'node:path';
2
+
3
+ import { createAgentApiHttpAdapter } from './agent-api-http-adapter.mjs';
4
+ import { createAgentApiPrivateHttpServer } from './agent-api-private-http-server.mjs';
5
+ import { createAgentApiRuntime } from './agent-api-runtime.mjs';
6
+ import { createAgentApiSessionRegistry } from './agent-api-session-registry.mjs';
7
+ import { createAgentApiTurnController } from './agent-api-turn-controller.mjs';
8
+
9
+ const MIN_SECRET_BYTES = 32;
10
+
11
+ function requireSecret(value, label) {
12
+ if (typeof value !== 'string' || Buffer.byteLength(value, 'utf8') < MIN_SECRET_BYTES) {
13
+ throw new TypeError(`${label} must contain at least ${MIN_SECRET_BYTES} bytes`);
14
+ }
15
+ return value;
16
+ }
17
+
18
+ function requireAbsoluteRoot(value) {
19
+ if (typeof value !== 'string' || !path.isAbsolute(value)) {
20
+ throw new TypeError('rootDir must be an absolute path');
21
+ }
22
+ return path.resolve(value);
23
+ }
24
+
25
+ function requireObject(value, label) {
26
+ if (value === undefined) return {};
27
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
28
+ throw new TypeError(`${label} must be an object`);
29
+ }
30
+ return value;
31
+ }
32
+
33
+ function cloneHarnessConfig(value) {
34
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
35
+ throw new TypeError('harnessConfig must be an object');
36
+ }
37
+ let config;
38
+ try {
39
+ config = structuredClone(value);
40
+ } catch {
41
+ throw new TypeError('harnessConfig must be structured-cloneable');
42
+ }
43
+ if (config.defaultModel !== 'king') {
44
+ throw new TypeError('harnessConfig.defaultModel must be king');
45
+ }
46
+ if (config.models === null
47
+ || typeof config.models !== 'object'
48
+ || Array.isArray(config.models)
49
+ || config.models.king === null
50
+ || typeof config.models.king !== 'object'
51
+ || Array.isArray(config.models.king)) {
52
+ throw new TypeError('harnessConfig.models.king must be an object');
53
+ }
54
+ const provider = config.models.king.provider;
55
+ if (typeof provider !== 'string'
56
+ || provider.length === 0
57
+ || config.providers === null
58
+ || typeof config.providers !== 'object'
59
+ || Array.isArray(config.providers)
60
+ || !Object.hasOwn(config.providers, provider)) {
61
+ throw new TypeError('harnessConfig must define the provider used by models.king');
62
+ }
63
+ return config;
64
+ }
65
+
66
+ async function createBundledHarness(input) {
67
+ const { createBlunHarness } = await import('../blun.mjs');
68
+ return createBlunHarness(input);
69
+ }
70
+
71
+ function resolveHarnessPreparation(options) {
72
+ const hasConfig = options.harnessConfig !== undefined;
73
+ const hasPrepare = options.prepareHarness !== undefined;
74
+ if (hasConfig && hasPrepare) {
75
+ throw new TypeError('only one of harnessConfig or prepareHarness may be provided');
76
+ }
77
+ if (!hasConfig && !hasPrepare) {
78
+ throw new TypeError('harnessConfig or prepareHarness is required');
79
+ }
80
+ if (hasPrepare) {
81
+ if (typeof options.prepareHarness !== 'function') {
82
+ throw new TypeError('prepareHarness must be a function');
83
+ }
84
+ return options.prepareHarness;
85
+ }
86
+
87
+ const config = cloneHarnessConfig(options.harnessConfig);
88
+ return async ({ harness }) => {
89
+ if (typeof harness?.setConfig !== 'function') {
90
+ throw new TypeError('Agent API harness must provide setConfig');
91
+ }
92
+ await harness.setConfig(structuredClone(config));
93
+ };
94
+ }
95
+
96
+ function createAgentApiServiceHost(options = {}) {
97
+ const rootDir = requireAbsoluteRoot(options.rootDir);
98
+ const internalSecret = requireSecret(options.internalSecret, 'internalSecret');
99
+ const isolationSecret = requireSecret(options.isolationSecret, 'isolationSecret');
100
+ if (internalSecret === isolationSecret) {
101
+ throw new TypeError('internalSecret and isolationSecret must be different');
102
+ }
103
+ if (options.createHarness !== undefined && typeof options.createHarness !== 'function') {
104
+ throw new TypeError('createHarness must be a function');
105
+ }
106
+
107
+ const runtimeOptions = requireObject(options.runtimeOptions, 'runtimeOptions');
108
+ const controllerOptions = requireObject(options.controllerOptions, 'controllerOptions');
109
+ const prepareHarness = resolveHarnessPreparation(options);
110
+ const runtime = createAgentApiRuntime(runtimeOptions);
111
+ const controller = createAgentApiTurnController({
112
+ ...controllerOptions,
113
+ runtime,
114
+ publicModel: 'king',
115
+ });
116
+ const sessionRegistry = createAgentApiSessionRegistry({
117
+ rootDir,
118
+ isolationSecret,
119
+ createHarness: options.createHarness ?? createBundledHarness,
120
+ prepareHarness,
121
+ sessionOptions: options.sessionOptions,
122
+ maxPrincipals: options.maxPrincipals,
123
+ maxSessionsPerPrincipal: options.maxSessionsPerPrincipal,
124
+ });
125
+ const adapter = createAgentApiHttpAdapter({
126
+ controller,
127
+ resolveSession: sessionRegistry.resolveSession,
128
+ });
129
+ const privateServer = createAgentApiPrivateHttpServer({
130
+ adapter,
131
+ internalSecret,
132
+ maxBodyBytes: options.maxBodyBytes,
133
+ });
134
+
135
+ let state = 'idle';
136
+ let listenPromise;
137
+ let closePromise;
138
+
139
+ async function listen(input = {}) {
140
+ if (state === 'closed' || state === 'closing') {
141
+ throw new Error('Agent API service host is closed');
142
+ }
143
+ if (state !== 'idle') {
144
+ throw new Error('Agent API service host is already listening');
145
+ }
146
+ state = 'starting';
147
+ try {
148
+ listenPromise = privateServer.listen(input);
149
+ const address = await listenPromise;
150
+ if (state === 'closing' || state === 'closed') {
151
+ await privateServer.close();
152
+ throw new Error('Agent API service host is closed');
153
+ }
154
+ state = 'listening';
155
+ return address;
156
+ } catch (error) {
157
+ if (state === 'starting') state = 'idle';
158
+ throw error;
159
+ } finally {
160
+ listenPromise = undefined;
161
+ }
162
+ }
163
+
164
+ function close() {
165
+ if (closePromise !== undefined) return closePromise;
166
+ state = 'closing';
167
+ closePromise = (async () => {
168
+ let firstError;
169
+ if (listenPromise !== undefined) {
170
+ try {
171
+ await listenPromise;
172
+ } catch {
173
+ // The listen failure remains with its caller; cleanup continues here.
174
+ }
175
+ }
176
+ try {
177
+ await privateServer.close();
178
+ } catch (error) {
179
+ firstError = error;
180
+ }
181
+ try {
182
+ await sessionRegistry.close();
183
+ } catch (error) {
184
+ firstError ??= error;
185
+ }
186
+ state = 'closed';
187
+ if (firstError !== undefined) throw firstError;
188
+ })();
189
+ return closePromise;
190
+ }
191
+
192
+ return Object.freeze({ close, listen });
193
+ }
194
+
195
+ export {
196
+ MIN_SECRET_BYTES,
197
+ createAgentApiServiceHost,
198
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.470",
3
+ "version": "9.1.471",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {