blun-king-cli 9.1.571 → 9.1.573
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
|
+
## 9.1.573
|
|
2
|
+
|
|
3
|
+
- Keep the input field visible and focused after the startup goal choice,
|
|
4
|
+
including Escape and a failed goal-resume request. Preserve unsent drafts.
|
|
5
|
+
- Show a compact with/without-goal choice instead of the entire objective.
|
|
6
|
+
- Ctrl+S leaves that choice without approving the goal and reaches the normal
|
|
7
|
+
queue shortcut. It also handles pending input while the session is idle.
|
|
8
|
+
- Preserve existing Escape behavior: queued work is cleared before a subsequent
|
|
9
|
+
Escape cancels the active turn. No automatic deletion of session history.
|
|
10
|
+
|
|
11
|
+
The release does not reconstruct acknowledgements lost by an older process
|
|
12
|
+
before upgrading. Genuinely pending messages remain attached to their session.
|
|
13
|
+
|
|
1
14
|
# Changelog
|
|
2
15
|
|
|
16
|
+
## 9.1.572
|
|
17
|
+
|
|
18
|
+
- Research evidence rejects source URLs containing recognized credential parameter
|
|
19
|
+
names in queries or parameter-shaped fragments, including nested names and
|
|
20
|
+
fragment routes. Rejection applies to failed sources as well as usable sources.
|
|
21
|
+
- Previously saved reports with these URLs are withheld during recall with the
|
|
22
|
+
existing `partial` / `report-invalid` result. Original snapshots are not modified
|
|
23
|
+
or deleted, and source addresses are never silently stripped or rewritten.
|
|
24
|
+
- Ordinary documentation queries and anchors retain their exact bytes. This is
|
|
25
|
+
a credential-name boundary, not general secret detection or a history cleanup.
|
|
26
|
+
|
|
27
|
+
|
|
3
28
|
## 9.1.571
|
|
4
29
|
|
|
5
30
|
- Bind Telegram queue ranges to their owning session. A new session no longer automatically consumes work left pending by an older session; resuming a known session restores only its own pending ranges.
|
package/blun.mjs
CHANGED
|
@@ -417373,6 +417373,10 @@ var ChoicePickerComponent = class extends Container {
|
|
|
417373
417373
|
}
|
|
417374
417374
|
handleInput(data) {
|
|
417375
417375
|
if (isKeyRelease(data)) return;
|
|
417376
|
+
if (matchesKey(normalizeCapsLockedCtrl(data), Key.ctrl("s")) && this.opts.onCtrlS !== void 0) {
|
|
417377
|
+
this.opts.onCtrlS();
|
|
417378
|
+
return;
|
|
417379
|
+
}
|
|
417376
417380
|
if (matchesKey(data, Key.ctrl("c")) && this.opts.onCtrlC !== void 0) {
|
|
417377
417381
|
this.opts.onCtrlC();
|
|
417378
417382
|
return;
|
|
@@ -508611,7 +508615,7 @@ var EditorKeyboardController = class {
|
|
|
508611
508615
|
};
|
|
508612
508616
|
editor.onCtrlS = () => {
|
|
508613
508617
|
const draft = (editor.getExpandedText?.() ?? editor.getText()).trim();
|
|
508614
|
-
if (!host.streamingUI.hasActiveTurn() && host.state.appState.streamingPhase === "idle" &&
|
|
508618
|
+
if (!host.streamingUI.hasActiveTurn() && host.state.appState.streamingPhase === "idle" && draft.length === 0 && host.state.queuedMessages.length === 0) return;
|
|
508615
508619
|
host.flushQueuedMessages(draft, editor.inputMode);
|
|
508616
508620
|
host.updateQueueDisplay();
|
|
508617
508621
|
host.state.ui.requestRender();
|
|
@@ -519807,13 +519811,14 @@ function startupResumeGoalPromptCopy() {
|
|
|
519807
519811
|
withGoal: uiText("startupResumeGoal.resume")
|
|
519808
519812
|
};
|
|
519809
519813
|
}
|
|
519810
|
-
function promptStartupResumeGoal(host, objective, copy) {
|
|
519814
|
+
function promptStartupResumeGoal(host, objective, copy, onFlushQueue) {
|
|
519811
519815
|
return new Promise((resolve) => {
|
|
519812
519816
|
let done = false;
|
|
519813
519817
|
const finish = (choice) => {
|
|
519814
519818
|
if (done) return;
|
|
519815
519819
|
done = true;
|
|
519816
|
-
host.dismissEditorReplacement();
|
|
519820
|
+
if (choice === "exit") host.dismissEditorReplacement();
|
|
519821
|
+
else host.restoreEditor();
|
|
519817
519822
|
resolve(choice);
|
|
519818
519823
|
};
|
|
519819
519824
|
host.mountEditorReplacement(new ChoicePickerComponent({
|
|
@@ -519824,8 +519829,7 @@ function promptStartupResumeGoal(host, objective, copy) {
|
|
|
519824
519829
|
label: copy.withoutGoal
|
|
519825
519830
|
}, {
|
|
519826
519831
|
value: "resume",
|
|
519827
|
-
label: copy.withGoal
|
|
519828
|
-
description: objective
|
|
519832
|
+
label: copy.withGoal
|
|
519829
519833
|
}],
|
|
519830
519834
|
onSelect: (value) => {
|
|
519831
519835
|
finish(value);
|
|
@@ -519838,6 +519842,11 @@ function promptStartupResumeGoal(host, objective, copy) {
|
|
|
519838
519842
|
},
|
|
519839
519843
|
onCtrlD: () => {
|
|
519840
519844
|
finish("exit");
|
|
519845
|
+
},
|
|
519846
|
+
onCtrlS: () => {
|
|
519847
|
+
if (done) return;
|
|
519848
|
+
finish("without");
|
|
519849
|
+
onFlushQueue?.();
|
|
519841
519850
|
}
|
|
519842
519851
|
}));
|
|
519843
519852
|
});
|
|
@@ -521194,13 +521203,15 @@ const telegramBoundary = this.captureTelegramQueueBoundary();
|
|
|
521194
521203
|
this.syncGoalTimeTrigger();
|
|
521195
521204
|
if (goal.status !== "paused" && goal.status !== "blocked") return;
|
|
521196
521205
|
this.startupGoalPromptedSessionId = sessionId;
|
|
521197
|
-
|
|
521198
|
-
|
|
521199
|
-
|
|
521200
|
-
|
|
521201
|
-
|
|
521202
|
-
|
|
521203
|
-
|
|
521206
|
+
await this.withTelegramQueuePaused(async () => {
|
|
521207
|
+
const choice = await promptStartupResumeGoal(this, goal.objective, startupResumeGoalPromptCopy(), () => { this.state.editor.handleInput("\u0013"); });
|
|
521208
|
+
if (this.aborted || this.session?.id !== sessionId) return;
|
|
521209
|
+
if (choice === "exit") {
|
|
521210
|
+
await this.stop(0);
|
|
521211
|
+
return;
|
|
521212
|
+
}
|
|
521213
|
+
if (choice === "resume") await handleGoalCommand(this, "resume");
|
|
521214
|
+
});
|
|
521204
521215
|
}
|
|
521205
521216
|
async stop(exitCode) {
|
|
521206
521217
|
if (this.isShuttingDown) return;
|
package/package.json
CHANGED
|
@@ -80,3 +80,25 @@ unread global corpus. `limited:true` also flags result-count or output truncatio
|
|
|
80
80
|
continuation is for the read budget, not pagination of every lexical hit. Narrow
|
|
81
81
|
the query when necessary. Unavailable or partial results must remain visible as
|
|
82
82
|
limitations; they do not prevent ordinary authorized research.
|
|
83
|
+
|
|
84
|
+
## Source URL Credential Boundary
|
|
85
|
+
|
|
86
|
+
All source statuses reject URL userinfo and recognized credential parameter names
|
|
87
|
+
in queries and parameter-shaped fragments, including fragment routes. Names are
|
|
88
|
+
decoded with URLSearchParams, compared case-insensitively, and checked within
|
|
89
|
+
bracket/dot nesting. Examples include access_token, refresh_token, api_key,
|
|
90
|
+
client_secret, authorization, session_id and signed-URL credential/signature fields.
|
|
91
|
+
|
|
92
|
+
The source address is never silently stripped or rewritten. Normal queries such
|
|
93
|
+
as `q=access_token`, bare `key` / `code` parameters, and ordinary documentation
|
|
94
|
+
anchors are not classified as credentials. This is not a general secret detector:
|
|
95
|
+
custom keys, opaque path values, nested encoded URLs and report text still require
|
|
96
|
+
the explicit review described above. Passing validation does not prove public
|
|
97
|
+
reachability, permission to share, or absence of secrets.
|
|
98
|
+
|
|
99
|
+
New invalid reports return `saved:false` / `report-invalid` before collection
|
|
100
|
+
creation. Reading an older invalid report withholds that snapshot and reports
|
|
101
|
+
`partial` / `report-invalid`, while leaving its original bytes on disk. Other valid
|
|
102
|
+
snapshots may still be returned. This is not automatic cleanup or credential
|
|
103
|
+
revocation. Do not remove history or rewrite URLs to make validation pass.
|
|
104
|
+
|
|
@@ -5,6 +5,25 @@ const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,99}$/;
|
|
|
5
5
|
const HASH = /^[a-f0-9]{64}$/;
|
|
6
6
|
const STATES = new Set(['usable', 'failed', 'challenge', 'irrelevant', 'stale']);
|
|
7
7
|
const VERDICTS = new Set(['supported', 'unsupported', 'uncertain']);
|
|
8
|
+
const CREDENTIAL_PARAMETER = /^(?:(?:access|refresh|id|oauth)[_-]?token|token|api[_-]?key|client[_-]?secret|password|passwd|authorization|bearer|jwt|session[_-]?(?:id|token)|secret|signature|sig|x-(?:amz|goog)-(?:signature|credential|security-token))$/i;
|
|
9
|
+
function validateSourceUrl(value) {
|
|
10
|
+
let url;
|
|
11
|
+
try { url = new URL(value); } catch { assert.fail('source URL'); }
|
|
12
|
+
assert(['http:', 'https:'].includes(url.protocol) && !url.username && !url.password, 'source URL');
|
|
13
|
+
const parameters = [url.searchParams];
|
|
14
|
+
// OAuth-style fragments can hold credentials; ordinary documentation anchors stay intact.
|
|
15
|
+
const fragment = url.hash.slice(1);
|
|
16
|
+
if (fragment.includes('=')) {
|
|
17
|
+
parameters.push(new URLSearchParams(fragment));
|
|
18
|
+
const queryStart = fragment.indexOf('?');
|
|
19
|
+
if (queryStart !== -1) parameters.push(new URLSearchParams(fragment.slice(queryStart + 1)));
|
|
20
|
+
}
|
|
21
|
+
for (const params of parameters) {
|
|
22
|
+
for (const key of params.keys()) {
|
|
23
|
+
assert(!key.split(/[\[\].]+/).some(part => CREDENTIAL_PARAMETER.test(part)), 'source URL');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
function boundedArray(value, maximum, label) {
|
|
9
28
|
assert(Array.isArray(value) && value.length <= maximum, label);
|
|
10
29
|
return value;
|
|
@@ -38,8 +57,7 @@ function score(report) {
|
|
|
38
57
|
const sourceMap = new Map();
|
|
39
58
|
const sourceCounts = Object.fromEntries([...STATES].map(status => [status, 0]));
|
|
40
59
|
for (const source of sources) {
|
|
41
|
-
|
|
42
|
-
assert(['http:', 'https:'].includes(url.protocol) && !url.username && !url.password, 'source URL');
|
|
60
|
+
validateSourceUrl(source.url);
|
|
43
61
|
assert(STATES.has(source.status), 'source status');
|
|
44
62
|
assert(source.retrievedAt === null || timestamp(source.retrievedAt), 'source time');
|
|
45
63
|
assert(source.contentSha256 === null || HASH.test(source.contentSha256), 'source hash');
|