blun-king-cli 9.1.628 → 9.1.630
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/bin/blun.js +0 -0
- package/bin/core-bootstrap.js +0 -0
- package/bin/king.js +0 -0
- package/bin/launcher-mode.js +0 -0
- package/bin/launcher-runtime.js +183 -9
- package/bin/managed-node.js +0 -0
- package/bin/native-command-runner.js +0 -0
- package/bin/native-module-repair.js +0 -0
- package/bin/node-runtime.js +0 -0
- package/bin/node-version.js +0 -0
- package/bin/plugin-bootstrap.js +0 -0
- package/bin/private-paths.js +0 -0
- package/bin/profile-handoff.cjs +152 -0
- package/bin/profile-launch.cjs +120 -0
- package/bin/standard-tools-bootstrap.js +0 -0
- package/bin/update-copy.js +0 -0
- package/bin/update-lease.js +0 -0
- package/bin/update-notice.js +0 -0
- package/blun.mjs +25 -34
- package/package.json +1 -1
- package/worker-host.mjs +17 -26
package/bin/blun.js
CHANGED
|
File without changes
|
package/bin/core-bootstrap.js
CHANGED
|
File without changes
|
package/bin/king.js
CHANGED
|
File without changes
|
package/bin/launcher-mode.js
CHANGED
|
File without changes
|
package/bin/launcher-runtime.js
CHANGED
|
@@ -40,9 +40,142 @@ const {
|
|
|
40
40
|
writePrivateFile,
|
|
41
41
|
} = require('./private-paths');
|
|
42
42
|
|
|
43
|
-
const { writeTelegramTemplate } = require('./rename-private-staging.cjs');
|
|
43
|
+
const { writeTelegramTemplate } = require('./rename-private-staging.cjs');
|
|
44
44
|
const PKG = path.resolve(__dirname, '..');
|
|
45
|
-
const
|
|
45
|
+
const { parseProfileLaunchArgs, resolveSelectedProfile } = require('./profile-launch.cjs');
|
|
46
|
+
|
|
47
|
+
// Generated from actual 628 Commander registrations; see GRAMMAR-628.json.
|
|
48
|
+
// No subcommand option arity table: subcommand tails belong to Core.
|
|
49
|
+
const ROOT_OPTION_ARITY = Object.freeze({
|
|
50
|
+
"-V": "flag",
|
|
51
|
+
"--version": "flag",
|
|
52
|
+
"--local": "flag",
|
|
53
|
+
"--message": "required",
|
|
54
|
+
"--history-limit": "required",
|
|
55
|
+
"--timeout-ms": "required",
|
|
56
|
+
"--thinking": "required",
|
|
57
|
+
"--url": "required",
|
|
58
|
+
"--token": "required",
|
|
59
|
+
"--password": "required",
|
|
60
|
+
"--tls-fingerprint": "required",
|
|
61
|
+
"-S": "optional",
|
|
62
|
+
"--session": "optional",
|
|
63
|
+
"-r": "optional",
|
|
64
|
+
"--resume": "optional",
|
|
65
|
+
"-c": "flag",
|
|
66
|
+
"--continue": "flag",
|
|
67
|
+
"-C": "flag",
|
|
68
|
+
"-y": "flag",
|
|
69
|
+
"--yolo": "flag",
|
|
70
|
+
"--auto": "flag",
|
|
71
|
+
"-p": "required",
|
|
72
|
+
"--prompt": "required",
|
|
73
|
+
"--output-format": "required",
|
|
74
|
+
"--skills-dir": "required",
|
|
75
|
+
"--add-dir": "required",
|
|
76
|
+
"--effort": "required",
|
|
77
|
+
"--permission-mode": "required",
|
|
78
|
+
"--fork-session": "required",
|
|
79
|
+
"--allowed-tools": "required",
|
|
80
|
+
"--settings": "required",
|
|
81
|
+
"--yes": "flag",
|
|
82
|
+
"--auto-approve": "flag",
|
|
83
|
+
"--plan": "flag",
|
|
84
|
+
"-h": "flag",
|
|
85
|
+
"--help": "flag"
|
|
86
|
+
});
|
|
87
|
+
const ROOT_COMMAND_NAMES = new Set(["export","proof","brief","handoff","replay","guard","demo","workspace","acp","server","web","login","mistakes","memory","doctor","config","agents","sessions","status","models","logs","vis","setup","docs","upgrade","update","__plugin_run_node","tui","chat","terminal"]);
|
|
88
|
+
|
|
89
|
+
function parseExplicitProfileArgs(args, mode) {
|
|
90
|
+
if (mode !== LAUNCHER_MODES.KING) return { args: [...args], name: undefined };
|
|
91
|
+
const remaining = [];
|
|
92
|
+
let name;
|
|
93
|
+
let firstOperandSeen = false;
|
|
94
|
+
let unknownDestination = false;
|
|
95
|
+
for (let i = 0; i < args.length; i++) {
|
|
96
|
+
const arg = args[i];
|
|
97
|
+
if (arg === '--') {
|
|
98
|
+
remaining.push(...args.slice(i));
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (arg === '--profile' || arg.startsWith('--profile=')) {
|
|
102
|
+
if (name !== undefined) throw new Error('--profile darf nur einmal angegeben werden.');
|
|
103
|
+
if (arg !== '--profile') throw new Error('--profile braucht einen getrennten Profilnamen.');
|
|
104
|
+
const pair = i + 1 < args.length ? [arg, args[++i]] : [arg];
|
|
105
|
+
name = parseProfileLaunchArgs(pair, mode).name;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
remaining.push(arg);
|
|
109
|
+
let token = arg;
|
|
110
|
+
let arity;
|
|
111
|
+
let recognized = false;
|
|
112
|
+
let attached = false;
|
|
113
|
+
let separator = false;
|
|
114
|
+
// Replay Commander's virtual tokens after each BOOLEAN short option.
|
|
115
|
+
// The remainder can itself become --, a long option, or a value option:
|
|
116
|
+
// -cy- -> -c -y --; -cy-prompt -> -c -y --prompt.
|
|
117
|
+
for (;;) {
|
|
118
|
+
if (token === '--') { separator = true; break; }
|
|
119
|
+
arity = Object.hasOwn(ROOT_OPTION_ARITY, token) ? ROOT_OPTION_ARITY[token] : undefined;
|
|
120
|
+
recognized = arity !== undefined;
|
|
121
|
+
if (recognized) break;
|
|
122
|
+
if (token.length > 2 && token[0] === '-' && token[1] !== '-') {
|
|
123
|
+
const short = '-' + token[1];
|
|
124
|
+
arity = ROOT_OPTION_ARITY[short];
|
|
125
|
+
if (arity !== undefined) {
|
|
126
|
+
if (arity !== 'flag') { recognized = true; attached = true; break; }
|
|
127
|
+
token = '-' + token.slice(2);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (/^--[^=]+=/u.test(token)) {
|
|
132
|
+
arity = ROOT_OPTION_ARITY[token.slice(0, token.indexOf('='))];
|
|
133
|
+
recognized = arity === 'required' || arity === 'optional';
|
|
134
|
+
attached = recognized;
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
if (separator) {
|
|
139
|
+
remaining.push(...args.slice(i + 1));
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
if (recognized) {
|
|
143
|
+
if (!attached && i + 1 < args.length
|
|
144
|
+
&& (arity === 'required'
|
|
145
|
+
|| (arity === 'optional' && !(args[i + 1].length > 1 && args[i + 1][0] === '-')))) {
|
|
146
|
+
remaining.push(args[++i]);
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const maybeOption = token.length > 1 && token[0] === '-';
|
|
151
|
+
if (maybeOption) unknownDestination = true;
|
|
152
|
+
// Match Commander's first operand/unknown split. A command-looking value
|
|
153
|
+
// behind -p/-S, a previous positional target or an unknown option is NOT
|
|
154
|
+
// a new command boundary.
|
|
155
|
+
if (!unknownDestination && !firstOperandSeen) {
|
|
156
|
+
if (ROOT_COMMAND_NAMES.has(arg)) {
|
|
157
|
+
remaining.push(...args.slice(i + 1));
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
firstOperandSeen = true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return { args: remaining, name };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const PROFILE = parseExplicitProfileArgs(process.argv.slice(2), launcherModeFromArgv(process.argv));
|
|
167
|
+
const ARGS = PROFILE.args;
|
|
168
|
+
|
|
169
|
+
function applyProfileEnvironment(env, paths) {
|
|
170
|
+
env.BLUN_HOME = paths.blunHome;
|
|
171
|
+
if (PROFILE.name !== undefined) {
|
|
172
|
+
env.BLUN_SHARED_HOME = paths.sharedHome;
|
|
173
|
+
env.BLUN_PROFILE = PROFILE.name;
|
|
174
|
+
} else if (String(env.BLUN_STATE_DIR || '').trim()) {
|
|
175
|
+
env.BLUN_SHARED_HOME = paths.blunHome;
|
|
176
|
+
}
|
|
177
|
+
return env;
|
|
178
|
+
}
|
|
46
179
|
|
|
47
180
|
function normalizeWindowsPath(value) {
|
|
48
181
|
return path.win32.normalize(value).replace(/\\+$/u, '').toLowerCase();
|
|
@@ -52,6 +185,7 @@ function resolveLauncherPrivatePaths(options = {}) {
|
|
|
52
185
|
const env = options.env || process.env;
|
|
53
186
|
const platform = options.platform || process.platform;
|
|
54
187
|
const configuredHome = String(env.BLUN_HOME || '').trim();
|
|
188
|
+
const selectedProfile = options.profileName ?? PROFILE.name;
|
|
55
189
|
const stateDir = String(env.BLUN_STATE_DIR || '').trim();
|
|
56
190
|
if (stateDir) {
|
|
57
191
|
const paths = platform === 'win32' ? path.win32 : path.posix;
|
|
@@ -78,10 +212,35 @@ function resolveLauncherPrivatePaths(options = {}) {
|
|
|
78
212
|
ancestor = parent;
|
|
79
213
|
}
|
|
80
214
|
}
|
|
215
|
+
if (selectedProfile !== undefined) {
|
|
216
|
+
// A state override cannot bypass the canonical OS-home/profile guards.
|
|
217
|
+
const profileEnv = { ...env };
|
|
218
|
+
delete profileEnv.BLUN_STATE_DIR;
|
|
219
|
+
const selected = resolveLauncherPrivatePaths({ ...options, env: profileEnv, profileName: selectedProfile });
|
|
220
|
+
const normalize = platform === 'win32' ? normalizeWindowsPath : paths.normalize;
|
|
221
|
+
if (normalize(blunHome) !== normalize(selected.blunHome)) {
|
|
222
|
+
throw new Error('BLUN_STATE_DIR widerspricht dem ausgewaehlten Profil.');
|
|
223
|
+
}
|
|
224
|
+
return selected;
|
|
225
|
+
}
|
|
81
226
|
return { blunHome, userHome: (options.homedir || os.homedir)() };
|
|
82
227
|
}
|
|
83
228
|
if (platform !== 'win32') {
|
|
229
|
+
const posix = path.posix;
|
|
84
230
|
const userHome = (options.homedir || os.homedir)();
|
|
231
|
+
if (selectedProfile !== undefined) {
|
|
232
|
+
const configuredShared = String(env.BLUN_SHARED_HOME || '').trim();
|
|
233
|
+
const sharedHome = configuredShared
|
|
234
|
+
? posix.resolve(configuredShared)
|
|
235
|
+
: configuredHome && posix.basename(posix.dirname(configuredHome)) === 'profile'
|
|
236
|
+
? posix.resolve(configuredHome, '..', '..')
|
|
237
|
+
: configuredHome ? posix.resolve(configuredHome) : posix.join(userHome, '.blun');
|
|
238
|
+
const blunHome = resolveSelectedProfile(sharedHome, selectedProfile, posix);
|
|
239
|
+
if (configuredShared && configuredHome && posix.resolve(configuredHome) !== blunHome) {
|
|
240
|
+
throw new Error(`BLUN_HOME passt nicht zum Profil ${selectedProfile}: ${blunHome}`);
|
|
241
|
+
}
|
|
242
|
+
return { blunHome, sharedHome, userHome };
|
|
243
|
+
}
|
|
85
244
|
return {
|
|
86
245
|
blunHome: configuredHome ? path.resolve(configuredHome) : path.join(userHome, '.blun'),
|
|
87
246
|
userHome,
|
|
@@ -108,6 +267,19 @@ function resolveLauncherPrivatePaths(options = {}) {
|
|
|
108
267
|
}
|
|
109
268
|
|
|
110
269
|
const blunHome = path.win32.join(userHome, '.blun');
|
|
270
|
+
if (selectedProfile !== undefined) {
|
|
271
|
+
const profileHome = resolveSelectedProfile(blunHome, selectedProfile, path.win32);
|
|
272
|
+
const configuredShared = String(env.BLUN_SHARED_HOME || '').trim();
|
|
273
|
+
if (configuredShared && normalizeWindowsPath(path.win32.resolve(configuredShared)) !== normalizeWindowsPath(blunHome)) {
|
|
274
|
+
throw new Error(`BLUN_SHARED_HOME muss unter Windows exakt im Windows-Benutzerprofil liegen: ${blunHome}`);
|
|
275
|
+
}
|
|
276
|
+
if (configuredHome && normalizeWindowsPath(path.win32.resolve(configuredHome)) !== normalizeWindowsPath(profileHome)
|
|
277
|
+
&& normalizeWindowsPath(path.win32.resolve(configuredHome)) !== normalizeWindowsPath(blunHome)) {
|
|
278
|
+
throw new Error(`BLUN_HOME passt nicht zum Profil ${selectedProfile}: ${profileHome}`);
|
|
279
|
+
}
|
|
280
|
+
return { blunHome: profileHome, sharedHome: blunHome, userHome };
|
|
281
|
+
}
|
|
282
|
+
|
|
111
283
|
if (configuredHome
|
|
112
284
|
&& normalizeWindowsPath(path.win32.resolve(configuredHome)) !== normalizeWindowsPath(blunHome)) {
|
|
113
285
|
throw new Error(`BLUN_HOME muss unter Windows exakt im Windows-Benutzerprofil liegen: ${blunHome}`);
|
|
@@ -290,6 +462,9 @@ function spawnManagedLauncher(binary, cwd) {
|
|
|
290
462
|
async function runLauncher(options = {}) {
|
|
291
463
|
const callerCwd = process.cwd();
|
|
292
464
|
const mode = options.mode || launcherModeFromArgv(process.argv);
|
|
465
|
+
if (PROFILE.name !== undefined) {
|
|
466
|
+
applyProfileEnvironment(process.env, resolveLauncherPrivatePaths());
|
|
467
|
+
}
|
|
293
468
|
const explicitUpdateRequest = isNpmPackageUpdateRequest(ARGS);
|
|
294
469
|
const explicitToolsRequest = String(ARGS[0] || '').toLowerCase() === 'tools';
|
|
295
470
|
const noticeResult = options.skipUpdateNotice === true
|
|
@@ -394,7 +569,7 @@ async function runLauncher(options = {}) {
|
|
|
394
569
|
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
395
570
|
const informationalEnv = { ...process.env };
|
|
396
571
|
delete informationalEnv.BLUN_HOME;
|
|
397
|
-
env
|
|
572
|
+
applyProfileEnvironment(env, resolveLauncherPrivatePaths({ env: informationalEnv }));
|
|
398
573
|
process.exitCode = await runInformationalCore(ARGS, env, callerCwd, releaseNotice);
|
|
399
574
|
return;
|
|
400
575
|
}
|
|
@@ -406,11 +581,11 @@ async function runLauncher(options = {}) {
|
|
|
406
581
|
process.exitCode = 1;
|
|
407
582
|
return;
|
|
408
583
|
}
|
|
409
|
-
const
|
|
584
|
+
const inspectionPaths = resolveLauncherPrivatePaths();
|
|
585
|
+
const blunDir = inspectionPaths.blunHome;
|
|
410
586
|
require('./verify-bundled-agent-sources.cjs').verifyBundledAgentSources(PKG);
|
|
411
587
|
const env = createLauncherEnvironment(process.env, LAUNCHER_MODES.BLUN, readPackageVersion(), blunDir);
|
|
412
|
-
env
|
|
413
|
-
if (String(process.env.BLUN_STATE_DIR || '').trim()) env.BLUN_SHARED_HOME = blunDir;
|
|
588
|
+
applyProfileEnvironment(env, inspectionPaths);
|
|
414
589
|
process.exitCode = await runInformationalCore(ARGS, env, callerCwd, releaseNotice);
|
|
415
590
|
return;
|
|
416
591
|
}
|
|
@@ -440,7 +615,7 @@ async function runLauncher(options = {}) {
|
|
|
440
615
|
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
441
616
|
const informationalEnv = { ...process.env };
|
|
442
617
|
delete informationalEnv.BLUN_HOME;
|
|
443
|
-
env
|
|
618
|
+
applyProfileEnvironment(env, resolveLauncherPrivatePaths({ env: informationalEnv }));
|
|
444
619
|
process.exitCode = await superviseProtectedCore(ARGS, env, callerCwd, releaseNotice);
|
|
445
620
|
return;
|
|
446
621
|
}
|
|
@@ -530,8 +705,7 @@ async function runLauncher(options = {}) {
|
|
|
530
705
|
|
|
531
706
|
// --- 6. Start -----------------------------------------------------------
|
|
532
707
|
const env = createLauncherEnvironment(process.env, mode, readPackageVersion(), blunDir);
|
|
533
|
-
env
|
|
534
|
-
if (String(process.env.BLUN_STATE_DIR || '').trim()) env.BLUN_SHARED_HOME = blunDir;
|
|
708
|
+
applyProfileEnvironment(env, getPrivatePaths());
|
|
535
709
|
process.exitCode = await superviseProtectedCore(
|
|
536
710
|
ARGS,
|
|
537
711
|
env,
|
package/bin/managed-node.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/bin/node-runtime.js
CHANGED
|
File without changes
|
package/bin/node-version.js
CHANGED
|
File without changes
|
package/bin/plugin-bootstrap.js
CHANGED
|
File without changes
|
package/bin/private-paths.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { randomUUID } = require('node:crypto');
|
|
6
|
+
const { listExistingProfiles, resolveSelectedProfile } = require('./profile-launch.cjs');
|
|
7
|
+
|
|
8
|
+
const PROFILE_HANDOFF_REQUEST = 'blun-profile-handoff-request';
|
|
9
|
+
const PROFILE_HANDOFF_REPLY = 'blun-profile-handoff-reply';
|
|
10
|
+
const PROFILE_HANDOFF_CANCEL = 'blun-profile-handoff-cancel';
|
|
11
|
+
const PROFILE_HANDOFF_READY = 'blun-profile-handoff-ready';
|
|
12
|
+
const PROFILE_HANDOFF_EXIT = 76;
|
|
13
|
+
const REQUEST_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
14
|
+
const SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/u;
|
|
15
|
+
const samePath = (a, b) => process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
16
|
+
const unavailable = () => Object.assign(new Error('PROFILE_HANDOFF_UNAVAILABLE'), { code: 'PROFILE_HANDOFF_UNAVAILABLE' });
|
|
17
|
+
|
|
18
|
+
function createProfileHandoff(env, isBusy = () => false) {
|
|
19
|
+
let pending;
|
|
20
|
+
const validate = (name) => {
|
|
21
|
+
if (typeof env.BLUN_HOME !== 'string' || !path.isAbsolute(env.BLUN_HOME)
|
|
22
|
+
|| (env.BLUN_SHARED_HOME !== undefined && !path.isAbsolute(env.BLUN_SHARED_HOME))) throw unavailable();
|
|
23
|
+
const sharedHome = path.resolve(env.BLUN_SHARED_HOME || env.BLUN_HOME);
|
|
24
|
+
const homeDir = path.resolve(env.BLUN_HOME);
|
|
25
|
+
const current = env.BLUN_PROFILE;
|
|
26
|
+
if (current !== undefined) {
|
|
27
|
+
if (!env.BLUN_SHARED_HOME || path.basename(resolveSelectedProfile(sharedHome, current)) !== current
|
|
28
|
+
|| !samePath(homeDir, resolveSelectedProfile(sharedHome, current))) throw unavailable();
|
|
29
|
+
} else if (!samePath(homeDir, sharedHome)) throw unavailable();
|
|
30
|
+
const names = listExistingProfiles(sharedHome);
|
|
31
|
+
if (typeof name !== 'string' || !names.includes(name) || name === current
|
|
32
|
+
|| (current !== undefined && !names.includes(current))) throw unavailable();
|
|
33
|
+
const target = resolveSelectedProfile(sharedHome, name);
|
|
34
|
+
if (isBusy(target)) throw unavailable();
|
|
35
|
+
const identities = [sharedHome, path.join(sharedHome, 'profile'), homeDir, target]
|
|
36
|
+
.map(file => ({ file, stat: fs.lstatSync(file, { bigint: true }) }));
|
|
37
|
+
if (identities.some(({ stat }) => !stat.isDirectory() || stat.isSymbolicLink())) throw unavailable();
|
|
38
|
+
return { name, sharedHome, homeDir: target, identities };
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
hasPending: () => pending !== undefined,
|
|
42
|
+
getSourceSessionId: () => pending?.sourceSessionId,
|
|
43
|
+
prepare(message) {
|
|
44
|
+
if (message?.type === PROFILE_HANDOFF_CANCEL && typeof message.requestId === 'string'
|
|
45
|
+
&& REQUEST_ID.test(message.requestId) && Object.keys(message).sort().join(',') === 'requestId,type') {
|
|
46
|
+
if (pending?.requestId === message.requestId) pending = undefined;
|
|
47
|
+
return { type: PROFILE_HANDOFF_REPLY, requestId: message.requestId, status: 'cancelled' };
|
|
48
|
+
}
|
|
49
|
+
if (!message || message.type !== PROFILE_HANDOFF_REQUEST || typeof message.requestId !== 'string' || !REQUEST_ID.test(message.requestId)) return undefined;
|
|
50
|
+
const shape = Object.keys(message).sort().join(',');
|
|
51
|
+
if (shape !== 'name,requestId,type' && shape !== 'name,requestId,sourceSessionId,type') return undefined;
|
|
52
|
+
if (shape === 'name,requestId,sourceSessionId,type'
|
|
53
|
+
&& (typeof message.sourceSessionId !== 'string' || !SESSION_ID.test(message.sourceSessionId))) return undefined;
|
|
54
|
+
let status = 'rejected';
|
|
55
|
+
try {
|
|
56
|
+
if (pending) {
|
|
57
|
+
if (pending.requestId === message.requestId && pending.name === message.name
|
|
58
|
+
&& pending.sourceSessionId === message.sourceSessionId) status = 'prepared';
|
|
59
|
+
} else {
|
|
60
|
+
pending = { ...validate(message.name), requestId: message.requestId, sourceSessionId: message.sourceSessionId };
|
|
61
|
+
status = 'prepared';
|
|
62
|
+
}
|
|
63
|
+
} catch {}
|
|
64
|
+
return { type: PROFILE_HANDOFF_REPLY, requestId: message.requestId, status };
|
|
65
|
+
},
|
|
66
|
+
commit() {
|
|
67
|
+
try {
|
|
68
|
+
if (!pending) throw unavailable();
|
|
69
|
+
const fresh = validate(pending.name);
|
|
70
|
+
for (let index = 0; index < pending.identities.length; index++) {
|
|
71
|
+
const before = pending.identities[index];
|
|
72
|
+
const after = fresh.identities[index];
|
|
73
|
+
if (before.file !== after.file || before.stat.dev !== after.stat.dev || before.stat.ino !== after.stat.ino) throw unavailable();
|
|
74
|
+
}
|
|
75
|
+
return Object.freeze({ name: fresh.name, sharedHome: fresh.sharedHome, homeDir: fresh.homeDir,
|
|
76
|
+
...(pending.sourceSessionId === undefined ? {} : { sourceSessionId: pending.sourceSessionId }) });
|
|
77
|
+
} catch {
|
|
78
|
+
throw unavailable();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function createProfileHandoffClient(channel = process) {
|
|
85
|
+
const enabled = channel.env?.BLUN_PROFILE_HANDOFF === '1';
|
|
86
|
+
let pending;
|
|
87
|
+
let disposed = false;
|
|
88
|
+
const available = () => !disposed && enabled && channel.connected === true
|
|
89
|
+
&& typeof channel.send === 'function';
|
|
90
|
+
const cancel = () => {
|
|
91
|
+
const request = pending;
|
|
92
|
+
if (!request) return;
|
|
93
|
+
pending = undefined;
|
|
94
|
+
request.finish(false);
|
|
95
|
+
if (channel.connected === true) {
|
|
96
|
+
try { channel.send({ type: PROFILE_HANDOFF_CANCEL, requestId: request.id }, () => {}); } catch {}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return Object.freeze({
|
|
100
|
+
isPrepared: () => available() && pending?.prepared === true,
|
|
101
|
+
cancel,
|
|
102
|
+
dispose() { cancel(); disposed = true; },
|
|
103
|
+
/** @param {string} name @param {{ signal?: AbortSignal, timeoutMs?: number, sourceSessionId?: string }} [options] */
|
|
104
|
+
prepare(name, { signal, timeoutMs = 5000, sourceSessionId } = {}) {
|
|
105
|
+
try {
|
|
106
|
+
if (!available() || pending || signal?.aborted || typeof name !== 'string'
|
|
107
|
+
|| path.basename(resolveSelectedProfile(path.parse(__dirname).root, name)) !== name
|
|
108
|
+
|| !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30000
|
|
109
|
+
|| (sourceSessionId !== undefined && (typeof sourceSessionId !== 'string' || !SESSION_ID.test(sourceSessionId)))) return Promise.resolve(false);
|
|
110
|
+
} catch { return Promise.resolve(false); }
|
|
111
|
+
return new Promise(resolve => {
|
|
112
|
+
const request = { id: randomUUID(), prepared: false, finish: undefined };
|
|
113
|
+
let settled = false;
|
|
114
|
+
let timer;
|
|
115
|
+
const aborted = () => cancel();
|
|
116
|
+
const disconnected = () => cancel();
|
|
117
|
+
const message = reply => {
|
|
118
|
+
if (pending !== request || reply?.type !== PROFILE_HANDOFF_REPLY || reply.requestId !== request.id
|
|
119
|
+
|| Object.keys(reply).sort().join(',') !== 'requestId,status,type') return;
|
|
120
|
+
if (reply.status === 'prepared') request.finish(true);
|
|
121
|
+
else if (reply.status === 'rejected' || reply.status === 'cancelled') cancel();
|
|
122
|
+
};
|
|
123
|
+
request.finish = prepared => {
|
|
124
|
+
if (!prepared) signal?.removeEventListener('abort', aborted);
|
|
125
|
+
if (settled) return;
|
|
126
|
+
settled = true;
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
channel.removeListener('message', message);
|
|
129
|
+
channel.removeListener('disconnect', disconnected);
|
|
130
|
+
try { channel.channel?.unref(); } catch {}
|
|
131
|
+
request.prepared = prepared;
|
|
132
|
+
resolve(prepared);
|
|
133
|
+
};
|
|
134
|
+
pending = request;
|
|
135
|
+
channel.on('message', message);
|
|
136
|
+
channel.once('disconnect', disconnected);
|
|
137
|
+
signal?.addEventListener('abort', aborted, { once: true });
|
|
138
|
+
timer = setTimeout(aborted, timeoutMs);
|
|
139
|
+
try {
|
|
140
|
+
channel.channel?.ref();
|
|
141
|
+
channel.send({ type: PROFILE_HANDOFF_REQUEST, requestId: request.id, name,
|
|
142
|
+
...(sourceSessionId === undefined ? {} : { sourceSessionId }) }, error => {
|
|
143
|
+
if (error && pending === request) cancel();
|
|
144
|
+
});
|
|
145
|
+
} catch { cancel(); }
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = { createProfileHandoff, createProfileHandoffClient, PROFILE_HANDOFF_CANCEL, PROFILE_HANDOFF_REQUEST, PROFILE_HANDOFF_REPLY,
|
|
152
|
+
PROFILE_HANDOFF_READY, PROFILE_HANDOFF_EXIT };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
|
|
6
|
+
const PROFILE_NAME = /^[a-z0-9][a-z0-9_-]{0,31}$/u;
|
|
7
|
+
const RESERVED = new Set(['doctor', 'export', 'help', 'login', 'mcp', 'server', 'tools', 'update', 'upgrade', 'version']);
|
|
8
|
+
|
|
9
|
+
function profileName(value) {
|
|
10
|
+
const name = String(value || '').trim().toLowerCase();
|
|
11
|
+
if (!PROFILE_NAME.test(name)) {
|
|
12
|
+
throw new Error('Profilnamen duerfen nur Buchstaben, Zahlen, Unterstriche und Bindestriche enthalten.');
|
|
13
|
+
}
|
|
14
|
+
return name;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseProfileLaunchArgs(args, mode) {
|
|
18
|
+
const remaining = [...args];
|
|
19
|
+
if (mode !== 'king') return { args: remaining, name: undefined };
|
|
20
|
+
|
|
21
|
+
const flag = remaining.indexOf('--profile');
|
|
22
|
+
if (flag >= 0) {
|
|
23
|
+
if (flag + 1 >= remaining.length) throw new Error('--profile braucht einen Profilnamen.');
|
|
24
|
+
const name = profileName(remaining[flag + 1]);
|
|
25
|
+
remaining.splice(flag, 2);
|
|
26
|
+
return { args: remaining, name };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (remaining.length === 1 && !remaining[0].startsWith('-') && !RESERVED.has(remaining[0].toLowerCase())) {
|
|
30
|
+
return { args: [], name: profileName(remaining[0]), implicit: true };
|
|
31
|
+
}
|
|
32
|
+
if (remaining.length === 2 && remaining.includes('-c')) {
|
|
33
|
+
const candidate = remaining.find(value => value !== '-c');
|
|
34
|
+
if (candidate && !candidate.startsWith('-')) return { args: ['-c'], name: profileName(candidate), implicit: true };
|
|
35
|
+
}
|
|
36
|
+
return { args: remaining, name: undefined };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function resolveSelectedProfile(sharedHome, name, pathImpl = path) {
|
|
40
|
+
return pathImpl.join(sharedHome, 'profile', profileName(name));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function catalogUnavailable() {
|
|
44
|
+
return Object.assign(new Error('PROFILE_CATALOG_UNAVAILABLE'), { code: 'PROFILE_CATALOG_UNAVAILABLE' });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function directoryIdentity(target, fsImpl) {
|
|
48
|
+
let stat;
|
|
49
|
+
try { stat = fsImpl.lstatSync(target, { bigint: true }); }
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error.code === 'ENOENT') return undefined;
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) throw catalogUnavailable();
|
|
55
|
+
return stat;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function homeIdentity(home, fsImpl) {
|
|
59
|
+
const root = path.parse(home).root;
|
|
60
|
+
let current = root;
|
|
61
|
+
let stat = directoryIdentity(current, fsImpl);
|
|
62
|
+
for (const part of home.slice(root.length).split(path.sep).filter(Boolean)) {
|
|
63
|
+
if (stat === undefined) return undefined;
|
|
64
|
+
current = path.join(current, part);
|
|
65
|
+
stat = directoryIdentity(current, fsImpl);
|
|
66
|
+
}
|
|
67
|
+
return stat;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function sameDirectory(before, after) {
|
|
71
|
+
return before !== undefined && after !== undefined && before.dev === after.dev && before.ino === after.ino;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Directory names only; selection must revalidate them before changing owner. */
|
|
75
|
+
function listExistingProfiles(sharedHome, options = {}) {
|
|
76
|
+
const fsImpl = options.fsImpl || fs;
|
|
77
|
+
try {
|
|
78
|
+
if (typeof sharedHome !== 'string' || !path.isAbsolute(sharedHome)) throw catalogUnavailable();
|
|
79
|
+
const home = path.resolve(sharedHome);
|
|
80
|
+
if (process.platform === 'win32' && home.startsWith('\\\\')) throw catalogUnavailable();
|
|
81
|
+
const beforeHome = homeIdentity(home, fsImpl);
|
|
82
|
+
if (beforeHome === undefined) return [];
|
|
83
|
+
const profileRoot = path.join(home, 'profile');
|
|
84
|
+
const beforeProfiles = directoryIdentity(profileRoot, fsImpl);
|
|
85
|
+
if (beforeProfiles === undefined) {
|
|
86
|
+
if (!sameDirectory(beforeHome, homeIdentity(home, fsImpl))) throw catalogUnavailable();
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
const rows = [];
|
|
90
|
+
const dir = fsImpl.opendirSync(profileRoot);
|
|
91
|
+
try {
|
|
92
|
+
let count = 0;
|
|
93
|
+
for (let entry; (entry = dir.readSync()) !== null;) {
|
|
94
|
+
if (++count > 4096) throw catalogUnavailable();
|
|
95
|
+
const name = entry.name;
|
|
96
|
+
if (!PROFILE_NAME.test(name)) continue;
|
|
97
|
+
const target = resolveSelectedProfile(home, name);
|
|
98
|
+
let stat;
|
|
99
|
+
try { stat = fsImpl.lstatSync(target, { bigint: true }); }
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error.code === 'ENOENT') continue;
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
if (stat.isDirectory() && !stat.isSymbolicLink()) rows.push({ name, target, stat });
|
|
105
|
+
}
|
|
106
|
+
} finally { dir.closeSync(); }
|
|
107
|
+
if (!sameDirectory(beforeHome, homeIdentity(home, fsImpl))
|
|
108
|
+
|| !sameDirectory(beforeProfiles, directoryIdentity(profileRoot, fsImpl))) throw catalogUnavailable();
|
|
109
|
+
for (const row of rows) {
|
|
110
|
+
if (!sameDirectory(row.stat, directoryIdentity(row.target, fsImpl))) throw catalogUnavailable();
|
|
111
|
+
}
|
|
112
|
+
if (!sameDirectory(beforeHome, homeIdentity(home, fsImpl))
|
|
113
|
+
|| !sameDirectory(beforeProfiles, directoryIdentity(profileRoot, fsImpl))) throw catalogUnavailable();
|
|
114
|
+
return rows.map(row => row.name).sort();
|
|
115
|
+
} catch {
|
|
116
|
+
throw catalogUnavailable();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { parseProfileLaunchArgs, resolveSelectedProfile, listExistingProfiles };
|
|
File without changes
|
package/bin/update-copy.js
CHANGED
|
File without changes
|
package/bin/update-lease.js
CHANGED
|
File without changes
|
package/bin/update-notice.js
CHANGED
|
File without changes
|
package/blun.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// BLUN_BUILD_INPUT_SHA256:
|
|
2
|
+
// BLUN_BUILD_INPUT_SHA256:295bff16bb3a40df46f164599652a975e64b232ee91e1b245fa87962a4b61885
|
|
3
3
|
/*! wasm-webp 0.1.0
|
|
4
4
|
MIT License
|
|
5
5
|
|
|
@@ -13347,23 +13347,15 @@ var init_managed_usage = __esmMin((() => {
|
|
|
13347
13347
|
init_utils$2();
|
|
13348
13348
|
DEFAULT_BLUN_BASE_URL = "https://api.blun.ai/v1";
|
|
13349
13349
|
DEFAULT_BLUN_AUXILIARY_BASE_URL = "https://api.blun.ai/coding/v1";
|
|
13350
|
-
ACCOUNT_USAGE_WINDOWS = [
|
|
13351
|
-
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
"7d"
|
|
13360
|
-
],
|
|
13361
|
-
[
|
|
13362
|
-
"monat",
|
|
13363
|
-
"month",
|
|
13364
|
-
"month"
|
|
13365
|
-
]
|
|
13366
|
-
];
|
|
13350
|
+
ACCOUNT_USAGE_WINDOWS = [[
|
|
13351
|
+
"sieben_tage",
|
|
13352
|
+
"seven_days",
|
|
13353
|
+
"7d"
|
|
13354
|
+
], [
|
|
13355
|
+
"monat",
|
|
13356
|
+
"month",
|
|
13357
|
+
"month"
|
|
13358
|
+
]];
|
|
13367
13359
|
}));
|
|
13368
13360
|
function cloneOverrides(overrides) {
|
|
13369
13361
|
if (overrides === void 0) return void 0;
|
|
@@ -13864,9 +13856,11 @@ function parseManagedQuotaPayload(payload) {
|
|
|
13864
13856
|
const billingKind = nonEmptyString$6(payload["art"]);
|
|
13865
13857
|
const globalUnlimited = payload["unlimited"];
|
|
13866
13858
|
if (plan === void 0 || typeof paid !== "boolean" || !Number.isSafeInteger(creditCents) || creditCents < 0 || billingKind === void 0 || globalUnlimited !== void 0 && typeof globalUnlimited !== "boolean" || !isRecord$27(payload["stand"])) return;
|
|
13867
|
-
|
|
13859
|
+
const stand = payload["stand"];
|
|
13860
|
+
if (!hasStrictQuotaStand(stand, globalUnlimited === true)) return void 0;
|
|
13861
|
+
const presentWindows = QUOTA_WINDOWS.filter(([sourceKey]) => sourceKey in stand);
|
|
13868
13862
|
const limits = parseManagedUsagePayload(payload).limits;
|
|
13869
|
-
if (limits.length !==
|
|
13863
|
+
if (limits.length !== presentWindows.length || presentWindows.some(([, id]) => limits.filter((row) => row.id === id).length !== 1)) return;
|
|
13870
13864
|
return {
|
|
13871
13865
|
plan,
|
|
13872
13866
|
paid,
|
|
@@ -13931,7 +13925,8 @@ function nonEmptyString$6(value) {
|
|
|
13931
13925
|
return normalized.length === 0 ? void 0 : normalized;
|
|
13932
13926
|
}
|
|
13933
13927
|
function hasStrictQuotaStand(stand, globalUnlimited) {
|
|
13934
|
-
return
|
|
13928
|
+
return QUOTA_WINDOWS.every(([sourceKey]) => {
|
|
13929
|
+
if (!(sourceKey in stand)) return true;
|
|
13935
13930
|
const row = stand[sourceKey];
|
|
13936
13931
|
if (!isRecord$27(row)) return false;
|
|
13937
13932
|
const used = row["verbraucht"];
|
|
@@ -13954,18 +13949,14 @@ function hasValidResetAt(value) {
|
|
|
13954
13949
|
if (typeof value !== "string") return false;
|
|
13955
13950
|
return /(?:Z|[+-]\d{2}:\d{2})$/i.test(value) && Number.isFinite(Date.parse(value));
|
|
13956
13951
|
}
|
|
13957
|
-
var MANAGED_QUOTA_PATH,
|
|
13952
|
+
var MANAGED_QUOTA_PATH, QUOTA_WINDOWS;
|
|
13958
13953
|
var init_managed_quota$1 = __esmMin((() => {
|
|
13959
13954
|
init_constants$6();
|
|
13960
13955
|
init_api_error();
|
|
13961
13956
|
init_managed_usage();
|
|
13962
13957
|
init_utils$2();
|
|
13963
13958
|
MANAGED_QUOTA_PATH = "/api/verbrauch/mein-stand";
|
|
13964
|
-
|
|
13965
|
-
["fuenf_stunden", "five_hours"],
|
|
13966
|
-
["sieben_tage", "seven_days"],
|
|
13967
|
-
["monat", "month"]
|
|
13968
|
-
];
|
|
13959
|
+
QUOTA_WINDOWS = [["sieben_tage", "seven_days"], ["monat", "month"]];
|
|
13969
13960
|
}));
|
|
13970
13961
|
function blunCodeFeedbackUrl(baseUrl) {
|
|
13971
13962
|
return `${blunCodeAuxiliaryBaseUrl(baseUrl)}/feedback`;
|
|
@@ -529129,8 +529120,11 @@ function normalizeManagedQuotaWindows(rows) {
|
|
|
529129
529120
|
unlimited: row.unlimited === true ? true : void 0
|
|
529130
529121
|
});
|
|
529131
529122
|
}
|
|
529132
|
-
if (byId.size
|
|
529133
|
-
return WINDOW_IDS.
|
|
529123
|
+
if (byId.size === 0 && rows.some((row) => row.id !== void 0 && WINDOW_IDS.includes(row.id))) return;
|
|
529124
|
+
return WINDOW_IDS.flatMap((id) => {
|
|
529125
|
+
const window = byId.get(id);
|
|
529126
|
+
return window === void 0 ? [] : [window];
|
|
529127
|
+
});
|
|
529134
529128
|
}
|
|
529135
529129
|
function selectHighestManagedQuotaWindow(windows) {
|
|
529136
529130
|
return windows?.filter((window) => window.unlimited !== true).reduce((highest, window) => highest === void 0 || window.fraction > highest.fraction ? window : highest, void 0);
|
|
@@ -529148,11 +529142,7 @@ function nextManagedQuotaWarning(windows, highestShown) {
|
|
|
529148
529142
|
var MANAGED_QUOTA_WARNING_METADATA_KEY, WINDOW_IDS;
|
|
529149
529143
|
var init_managed_quota = __esmMin((() => {
|
|
529150
529144
|
MANAGED_QUOTA_WARNING_METADATA_KEY = "managedQuotaWarningThreshold";
|
|
529151
|
-
WINDOW_IDS = [
|
|
529152
|
-
"five_hours",
|
|
529153
|
-
"seven_days",
|
|
529154
|
-
"month"
|
|
529155
|
-
];
|
|
529145
|
+
WINDOW_IDS = ["seven_days", "month"];
|
|
529156
529146
|
}));
|
|
529157
529147
|
function managedProviderOnly(providers) {
|
|
529158
529148
|
const managed = providers[DEFAULT_OAUTH_PROVIDER_NAME];
|
|
@@ -537127,6 +537117,7 @@ function centerQuotaLine(line, width) {
|
|
|
537127
537117
|
return `${" ".repeat(leftPadding)}${line}`;
|
|
537128
537118
|
}
|
|
537129
537119
|
function formatManagedQuotaFooterLines(windows, colors, width, options = {}) {
|
|
537120
|
+
windows = windows.filter((window) => window.id !== "five_hours");
|
|
537130
537121
|
if (windows.length === 0 || width <= 0) return [];
|
|
537131
537122
|
const locale = options.locale ?? getCurrentUiLocale();
|
|
537132
537123
|
const variants = [
|
package/package.json
CHANGED
package/worker-host.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// BLUN_BUILD_INPUT_SHA256:
|
|
2
|
+
// BLUN_BUILD_INPUT_SHA256:295bff16bb3a40df46f164599652a975e64b232ee91e1b245fa87962a4b61885
|
|
3
3
|
/*! wasm-webp 0.1.0
|
|
4
4
|
MIT License
|
|
5
5
|
|
|
@@ -13011,23 +13011,15 @@ function managedContextWindowFrom(payload) {
|
|
|
13011
13011
|
const [first] = values;
|
|
13012
13012
|
return values.every((value) => value === first) ? first : void 0;
|
|
13013
13013
|
}
|
|
13014
|
-
const ACCOUNT_USAGE_WINDOWS = [
|
|
13015
|
-
|
|
13016
|
-
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
"7d"
|
|
13024
|
-
],
|
|
13025
|
-
[
|
|
13026
|
-
"monat",
|
|
13027
|
-
"month",
|
|
13028
|
-
"month"
|
|
13029
|
-
]
|
|
13030
|
-
];
|
|
13014
|
+
const ACCOUNT_USAGE_WINDOWS = [[
|
|
13015
|
+
"sieben_tage",
|
|
13016
|
+
"seven_days",
|
|
13017
|
+
"7d"
|
|
13018
|
+
], [
|
|
13019
|
+
"monat",
|
|
13020
|
+
"month",
|
|
13021
|
+
"month"
|
|
13022
|
+
]];
|
|
13031
13023
|
function toAccountUsageRow(raw, id, label, accountUnlimited) {
|
|
13032
13024
|
if (!isRecord$9(raw)) return null;
|
|
13033
13025
|
const used = toInt(raw["verbraucht"]);
|
|
@@ -13738,11 +13730,7 @@ async function provisionManagedBlunCodeConfig(options) {
|
|
|
13738
13730
|
};
|
|
13739
13731
|
}
|
|
13740
13732
|
const MANAGED_QUOTA_PATH = "/api/verbrauch/mein-stand";
|
|
13741
|
-
const
|
|
13742
|
-
["fuenf_stunden", "five_hours"],
|
|
13743
|
-
["sieben_tage", "seven_days"],
|
|
13744
|
-
["monat", "month"]
|
|
13745
|
-
];
|
|
13733
|
+
const QUOTA_WINDOWS = [["sieben_tage", "seven_days"], ["monat", "month"]];
|
|
13746
13734
|
function blunManagedQuotaUrl(oauthHost) {
|
|
13747
13735
|
return `${(oauthHost ?? process.env["BLUN_OAUTH_HOST"] ?? "https://account.blun.ai").replace(/\/+$/, "")}${MANAGED_QUOTA_PATH}`;
|
|
13748
13736
|
}
|
|
@@ -13754,9 +13742,11 @@ function parseManagedQuotaPayload(payload) {
|
|
|
13754
13742
|
const billingKind = nonEmptyString$5(payload["art"]);
|
|
13755
13743
|
const globalUnlimited = payload["unlimited"];
|
|
13756
13744
|
if (plan === void 0 || typeof paid !== "boolean" || !Number.isSafeInteger(creditCents) || creditCents < 0 || billingKind === void 0 || globalUnlimited !== void 0 && typeof globalUnlimited !== "boolean" || !isRecord$9(payload["stand"])) return;
|
|
13757
|
-
|
|
13745
|
+
const stand = payload["stand"];
|
|
13746
|
+
if (!hasStrictQuotaStand(stand, globalUnlimited === true)) return void 0;
|
|
13747
|
+
const presentWindows = QUOTA_WINDOWS.filter(([sourceKey]) => sourceKey in stand);
|
|
13758
13748
|
const limits = parseManagedUsagePayload(payload).limits;
|
|
13759
|
-
if (limits.length !==
|
|
13749
|
+
if (limits.length !== presentWindows.length || presentWindows.some(([, id]) => limits.filter((row) => row.id === id).length !== 1)) return;
|
|
13760
13750
|
return {
|
|
13761
13751
|
plan,
|
|
13762
13752
|
paid,
|
|
@@ -13821,7 +13811,8 @@ function nonEmptyString$5(value) {
|
|
|
13821
13811
|
return normalized.length === 0 ? void 0 : normalized;
|
|
13822
13812
|
}
|
|
13823
13813
|
function hasStrictQuotaStand(stand, globalUnlimited) {
|
|
13824
|
-
return
|
|
13814
|
+
return QUOTA_WINDOWS.every(([sourceKey]) => {
|
|
13815
|
+
if (!(sourceKey in stand)) return true;
|
|
13825
13816
|
const row = stand[sourceKey];
|
|
13826
13817
|
if (!isRecord$9(row)) return false;
|
|
13827
13818
|
const used = row["verbraucht"];
|