@mjasnikovs/pi-task 0.18.47 → 0.18.48
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/dist/config/register.d.ts +62 -1
- package/dist/config/register.js +150 -87
- package/dist/shared/pkg-version.d.ts +8 -0
- package/dist/shared/pkg-version.js +20 -0
- package/dist/workers/html-clean.js +2 -16
- package/package.json +1 -1
|
@@ -1,5 +1,42 @@
|
|
|
1
|
-
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
1
|
+
import type { ExtensionAPI, ExtensionCommandContext } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import type { Component } from '@earendil-works/pi-tui';
|
|
3
|
+
import { type PiTaskConfig } from './config.js';
|
|
2
4
|
import { type InstalledExtension } from './extension-list.js';
|
|
5
|
+
type Theme = ExtensionCommandContext['ui']['theme'];
|
|
6
|
+
/**
|
|
7
|
+
* Frames a child component (the settings list) in a rounded border with a title
|
|
8
|
+
* woven into the top edge. Without this the overlay's content sits flush against
|
|
9
|
+
* the chat scrollback and reads as just more console text; the border gives it a
|
|
10
|
+
* distinct panel. Layout mirrors {@link renderQuestionBox}'s box chrome: render
|
|
11
|
+
* the child at the inner width, pad each line so the right edge lines up, then
|
|
12
|
+
* frame. Input and invalidation are forwarded straight through to the child.
|
|
13
|
+
*/
|
|
14
|
+
declare class BorderedBox implements Component {
|
|
15
|
+
private readonly child;
|
|
16
|
+
private readonly title;
|
|
17
|
+
private readonly border;
|
|
18
|
+
private readonly titleColor;
|
|
19
|
+
/**
|
|
20
|
+
* Body lines to pad out to. The settings list grows and shrinks with the
|
|
21
|
+
* selected item's description, so without a floor the whole panel jumps
|
|
22
|
+
* a few rows every time the cursor moves. Padding to the tallest layout
|
|
23
|
+
* keeps the border still while the contents change.
|
|
24
|
+
*/
|
|
25
|
+
private readonly minBodyLines;
|
|
26
|
+
constructor(child: Component & {
|
|
27
|
+
handleInput(data: string): void;
|
|
28
|
+
}, title: string, border: (s: string) => string, titleColor: (s: string) => string,
|
|
29
|
+
/**
|
|
30
|
+
* Body lines to pad out to. The settings list grows and shrinks with the
|
|
31
|
+
* selected item's description, so without a floor the whole panel jumps
|
|
32
|
+
* a few rows every time the cursor moves. Padding to the tallest layout
|
|
33
|
+
* keeps the border still while the contents change.
|
|
34
|
+
*/
|
|
35
|
+
minBodyLines?: number);
|
|
36
|
+
render(width: number): string[];
|
|
37
|
+
invalidate(): void;
|
|
38
|
+
handleInput(data: string): void;
|
|
39
|
+
}
|
|
3
40
|
export declare function extensionItems(extensions: InstalledExtension[], whitelist: readonly string[]): {
|
|
4
41
|
id: string;
|
|
5
42
|
label: string;
|
|
@@ -9,4 +46,28 @@ export declare function extensionItems(extensions: InstalledExtension[], whiteli
|
|
|
9
46
|
}[];
|
|
10
47
|
/** Apply an extension toggle to the config's whitelist (idempotent both ways). */
|
|
11
48
|
export declare function applyExtensionToggle(whitelist: readonly string[], entryPath: string, on: boolean): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Tallest body the settings list can render, so {@link BorderedBox} can pad
|
|
51
|
+
* every frame to it and hold the border still. Mirrors SettingsList's own
|
|
52
|
+
* layout: one pad row, the visible rows, the scroll counter, a blank, the
|
|
53
|
+
* selected item's wrapped description, a blank, the key hint.
|
|
54
|
+
*/
|
|
55
|
+
export declare function settingsBodyHeight(descriptions: string[], maxVisible: number, wrapWidth: number): number;
|
|
56
|
+
/** A settings row as {@link SettingsList} wants it. */
|
|
57
|
+
export type PanelItem = {
|
|
58
|
+
id: string;
|
|
59
|
+
label: string;
|
|
60
|
+
description: string;
|
|
61
|
+
currentValue: string;
|
|
62
|
+
values: string[];
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Builds the framed settings panel. Split out of the command handler so the
|
|
66
|
+
* exact component the overlay shows can be rendered to a string in a test or a
|
|
67
|
+
* preview script, rather than only being inspectable by opening the TUI.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createSettingsPanel(items: PanelItem[], theme: Theme, onChange: (id: string, newValue: string) => void, onCancel: () => void): BorderedBox;
|
|
70
|
+
/** The full settings row list for the current config, in menu order. */
|
|
71
|
+
export declare function panelItems(cfg: PiTaskConfig, installed: InstalledExtension[]): PanelItem[];
|
|
12
72
|
export declare function registerConfig(pi: ExtensionAPI): void;
|
|
73
|
+
export {};
|
package/dist/config/register.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { SettingsList, visibleWidth } from '@earendil-works/pi-tui';
|
|
1
|
+
import { SettingsList, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
2
2
|
import { registerBridgeCommand } from '../remote/bridge.js';
|
|
3
|
+
import { readPkgVersion } from '../shared/pkg-version.js';
|
|
3
4
|
import { SEARCH_PROVIDERS, SEARCH_PROVIDER_LABELS, providerForLabel } from '../workers/search-types.js';
|
|
4
5
|
import { COMMAND_TIMEOUT_OPTIONS, getConfig, saveConfig, STREAM_INACTIVITY_OPTIONS } from './config.js';
|
|
5
6
|
import { listInstalledExtensions } from './extension-list.js';
|
|
6
|
-
|
|
7
|
+
// Version in the title so a bug report or screenshot says which build it came
|
|
8
|
+
// from without anyone having to go look it up.
|
|
9
|
+
const CONFIG_TITLE = `pi-task ${readPkgVersion()} settings`;
|
|
7
10
|
/**
|
|
8
11
|
* Frames a child component (the settings list) in a rounded border with a title
|
|
9
12
|
* woven into the top edge. Without this the overlay's content sits flush against
|
|
@@ -17,11 +20,20 @@ class BorderedBox {
|
|
|
17
20
|
title;
|
|
18
21
|
border;
|
|
19
22
|
titleColor;
|
|
20
|
-
|
|
23
|
+
minBodyLines;
|
|
24
|
+
constructor(child, title, border, titleColor,
|
|
25
|
+
/**
|
|
26
|
+
* Body lines to pad out to. The settings list grows and shrinks with the
|
|
27
|
+
* selected item's description, so without a floor the whole panel jumps
|
|
28
|
+
* a few rows every time the cursor moves. Padding to the tallest layout
|
|
29
|
+
* keeps the border still while the contents change.
|
|
30
|
+
*/
|
|
31
|
+
minBodyLines = 0) {
|
|
21
32
|
this.child = child;
|
|
22
33
|
this.title = title;
|
|
23
34
|
this.border = border;
|
|
24
35
|
this.titleColor = titleColor;
|
|
36
|
+
this.minBodyLines = minBodyLines;
|
|
25
37
|
}
|
|
26
38
|
render(width) {
|
|
27
39
|
const innerWidth = Math.max(1, width - 4); // "│ " + content + " │"
|
|
@@ -33,7 +45,13 @@ class BorderedBox {
|
|
|
33
45
|
const top = rest >= 0 ?
|
|
34
46
|
this.border(`╭${dash(lead)}`) + this.titleColor(tag) + this.border(`${dash(rest)}╮`)
|
|
35
47
|
: this.border(`╭${dash(width - 2)}╮`);
|
|
36
|
-
|
|
48
|
+
// One blank row under the title so the first setting is not welded to
|
|
49
|
+
// the border, then the child, then padding up to the stable height.
|
|
50
|
+
const childLines = this.child.render(innerWidth);
|
|
51
|
+
const raw = ['', ...childLines];
|
|
52
|
+
while (raw.length < this.minBodyLines)
|
|
53
|
+
raw.push('');
|
|
54
|
+
const body = raw.map(line => {
|
|
37
55
|
const pad = innerWidth - visibleWidth(line);
|
|
38
56
|
const padded = pad > 0 ? line + ' '.repeat(pad) : line;
|
|
39
57
|
return this.border('│ ') + padded + this.border(' │');
|
|
@@ -53,83 +71,95 @@ class BorderedBox {
|
|
|
53
71
|
* toggle on/off; enum settings list their values and cycle through them.
|
|
54
72
|
*/
|
|
55
73
|
const ITEMS = [
|
|
56
|
-
{
|
|
74
|
+
{
|
|
75
|
+
id: 'remote',
|
|
76
|
+
label: 'remote control',
|
|
77
|
+
description: 'Serve the task UI on your local network so you can follow and steer a run from '
|
|
78
|
+
+ 'your phone. Prints a QR code to scan when it starts'
|
|
79
|
+
},
|
|
57
80
|
{
|
|
58
81
|
id: 'compressReasoning',
|
|
59
|
-
label: 'compress
|
|
60
|
-
description: '
|
|
82
|
+
label: 'compress thinking',
|
|
83
|
+
description: "Shrink the model's thinking blocks once it has moved on, so a long run keeps more "
|
|
84
|
+
+ 'room for the work itself'
|
|
61
85
|
},
|
|
62
86
|
{
|
|
63
87
|
id: 'autoCommit',
|
|
64
88
|
label: 'auto-commit',
|
|
65
|
-
description: 'git commit
|
|
66
|
-
|
|
67
|
-
{
|
|
68
|
-
id: 'orientation',
|
|
69
|
-
label: 'orientation',
|
|
70
|
-
description: 'Pre-supply the project core (manifest, types, schema…) to the read-heavy research workers'
|
|
89
|
+
description: 'Make a git commit before and after every sub-task, so each step is a checkpoint '
|
|
90
|
+
+ 'you can read back or roll back to'
|
|
71
91
|
},
|
|
72
92
|
{
|
|
73
93
|
id: 'verifyWork',
|
|
74
94
|
label: 'verify work',
|
|
75
|
-
description:
|
|
95
|
+
description: 'When a task says it is done, actually run the checks its spec asks for and report '
|
|
96
|
+
+ "PASS or FAIL instead of taking the model's word for it. This is also what lets "
|
|
97
|
+
+ '"enforce guidelines" fix things safely. /task waits for the work to finish'
|
|
76
98
|
},
|
|
77
99
|
{
|
|
78
100
|
id: 'enforceGuidelines',
|
|
79
101
|
label: 'enforce guidelines',
|
|
80
|
-
description:
|
|
102
|
+
description: 'Check what each task committed against your AGENTS.md / CLAUDE.md rules. With '
|
|
103
|
+
+ '"verify work" on it also fixes what it finds, undoing any fix that breaks the '
|
|
104
|
+
+ 'checks; on its own it only reports. /task waits for the work to finish'
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'orientation',
|
|
108
|
+
label: 'project tour',
|
|
109
|
+
description: 'Show the research workers the shape of the project first — package manifest, '
|
|
110
|
+
+ 'types, schema — so they spend their steps on the question instead of on finding '
|
|
111
|
+
+ 'their way around'
|
|
81
112
|
},
|
|
82
113
|
{
|
|
83
114
|
id: 'parallelResearchWorkers',
|
|
84
115
|
label: 'parallel research',
|
|
85
|
-
description: 'Run the 4 research workers
|
|
116
|
+
description: 'Run the 4 research workers at once instead of one after another. Only faster if '
|
|
117
|
+
+ 'your model backend can answer several requests at the same time — on a single '
|
|
118
|
+
+ 'local GPU it is measurably slower, so leave it off there'
|
|
86
119
|
},
|
|
87
120
|
{
|
|
88
121
|
id: 'researchCache',
|
|
89
122
|
label: 'research cache',
|
|
90
|
-
description: '
|
|
123
|
+
description: 'Remember docs and web pages for the length of one run, so later tasks reuse what '
|
|
124
|
+
+ 'the first one already fetched instead of downloading it again. Only external '
|
|
125
|
+
+ 'sources, only successful fetches, and it is dropped when the run ends'
|
|
91
126
|
},
|
|
92
127
|
{
|
|
93
128
|
id: 'searchProvider',
|
|
94
|
-
label: 'search
|
|
95
|
-
description: '
|
|
129
|
+
label: 'search engine',
|
|
130
|
+
description: 'Which engine backs web search. Exa and DuckDuckGo work with no setup; Brave needs '
|
|
131
|
+
+ 'a BRAVE_SEARCH_API_KEY in your environment',
|
|
96
132
|
// Display full engine names; the stored config value stays the short id.
|
|
97
133
|
values: SEARCH_PROVIDERS.map(p => SEARCH_PROVIDER_LABELS[p])
|
|
98
134
|
},
|
|
99
135
|
{
|
|
100
136
|
id: 'requestTimeoutMs',
|
|
101
137
|
label: 'command timeout',
|
|
102
|
-
description: '
|
|
103
|
-
+ '
|
|
104
|
-
+ '
|
|
105
|
-
+ '
|
|
106
|
-
+ 'the verify/fix gate children. off disables it everywhere — gates can then hang '
|
|
107
|
-
+ 'unbounded',
|
|
138
|
+
description: 'Give up on any single command that runs this long, and tell the model to set its '
|
|
139
|
+
+ 'own timeout next time. Stops a run from waiting forever on a dev server or a '
|
|
140
|
+
+ 'hung build. Covers the main session and the checking steps; "off" lets a stuck '
|
|
141
|
+
+ 'command hang the run until you notice',
|
|
108
142
|
// Display human labels; the stored config value stays the ms number.
|
|
109
143
|
values: COMMAND_TIMEOUT_OPTIONS.map(o => o.label)
|
|
110
144
|
},
|
|
111
145
|
{
|
|
112
146
|
id: 'streamInactivityMs',
|
|
113
|
-
label: '
|
|
114
|
-
description: '
|
|
115
|
-
+ '
|
|
116
|
-
+ '
|
|
117
|
-
+ '
|
|
118
|
-
+ '
|
|
119
|
-
+ 'generous on local backends: prompt processing on a large context legitimately '
|
|
120
|
-
+ 'emits nothing for minutes. off disables it on both the main session and children',
|
|
147
|
+
label: 'stuck reply retry',
|
|
148
|
+
description: 'Give up on a model reply that has sent nothing for this long and ask again. A '
|
|
149
|
+
+ 'dropped connection looks exactly like a model thinking hard and reports no '
|
|
150
|
+
+ 'error, so a run can sit dead for hours. Only total silence counts, and the clock '
|
|
151
|
+
+ 'pauses while a command runs — local models go quiet for minutes on long prompts, '
|
|
152
|
+
+ 'so leave room',
|
|
121
153
|
// Display human labels; the stored config value stays the ms number.
|
|
122
154
|
values: STREAM_INACTIVITY_OPTIONS.map(o => o.label)
|
|
123
155
|
},
|
|
124
156
|
{
|
|
125
157
|
id: 'yoloMode',
|
|
126
158
|
label: 'yolo mode',
|
|
127
|
-
description: '
|
|
128
|
-
+ 'and
|
|
129
|
-
+ '
|
|
130
|
-
+ '
|
|
131
|
-
+ '(YOLO) in the task file and debt ledger. For THROWAWAY/TEST projects you are '
|
|
132
|
-
+ 'not watching — a real run should decide these itself'
|
|
159
|
+
description: 'Stop asking you anything: every question takes the option pi recommends, a failed '
|
|
160
|
+
+ 'check is accepted and written down as debt, and a failed final check is retried '
|
|
161
|
+
+ 'until the budget runs out. Each auto-answer is marked (YOLO) in the task file. '
|
|
162
|
+
+ 'For throwaway projects you are not watching'
|
|
133
163
|
}
|
|
134
164
|
];
|
|
135
165
|
/** Human label for the stored command-timeout ms (falls back to the raw ms). */
|
|
@@ -162,10 +192,10 @@ export function extensionItems(extensions, whitelist) {
|
|
|
162
192
|
return extensions.map(e => ({
|
|
163
193
|
id: EXT_ID_PREFIX + e.path,
|
|
164
194
|
label: `ext: ${e.label}`,
|
|
165
|
-
description: `
|
|
166
|
-
+ '
|
|
167
|
-
+ '
|
|
168
|
-
+ `
|
|
195
|
+
description: `Load this ${e.origin} extension in the helper sessions pi-task spawns. They run `
|
|
196
|
+
+ 'with extensions off by default, so turn this on when the extension provides the '
|
|
197
|
+
+ 'model they need (pi-lmstudio, for example). They also inherit its tools and '
|
|
198
|
+
+ `hooks, so only enable ones you trust. ${e.path}`,
|
|
169
199
|
currentValue: whitelist.includes(e.path) ? 'on' : 'off',
|
|
170
200
|
values: ['on', 'off']
|
|
171
201
|
}));
|
|
@@ -175,15 +205,62 @@ export function applyExtensionToggle(whitelist, entryPath, on) {
|
|
|
175
205
|
const rest = whitelist.filter(p => p !== entryPath);
|
|
176
206
|
return on ? [...rest, entryPath] : rest;
|
|
177
207
|
}
|
|
208
|
+
/** Overlay width; the list gets `- 4` of it, the description `- 4` again. */
|
|
209
|
+
const OVERLAY_WIDTH = 68;
|
|
210
|
+
/** Settings rows shown at once before the list scrolls. */
|
|
211
|
+
const MAX_VISIBLE = 9;
|
|
212
|
+
/**
|
|
213
|
+
* Tallest body the settings list can render, so {@link BorderedBox} can pad
|
|
214
|
+
* every frame to it and hold the border still. Mirrors SettingsList's own
|
|
215
|
+
* layout: one pad row, the visible rows, the scroll counter, a blank, the
|
|
216
|
+
* selected item's wrapped description, a blank, the key hint.
|
|
217
|
+
*/
|
|
218
|
+
export function settingsBodyHeight(descriptions, maxVisible, wrapWidth) {
|
|
219
|
+
const tallestDescription = Math.max(0, ...descriptions.map(d => wrapTextWithAnsi(d, wrapWidth).length));
|
|
220
|
+
return 1 + maxVisible + 1 + 1 + tallestDescription + 1 + 1;
|
|
221
|
+
}
|
|
178
222
|
function makeTheme(theme) {
|
|
179
223
|
return {
|
|
180
|
-
label: (text, selected) =>
|
|
181
|
-
|
|
224
|
+
label: (text, selected) => selected ? theme.fg('accent', theme.bold(text)) : theme.fg('text', text),
|
|
225
|
+
// A filled/hollow dot makes the on/off column scannable at a glance
|
|
226
|
+
// without reading a word on every row. Enum values (an engine name, a
|
|
227
|
+
// duration) are real content, so they stay readable rather than muted.
|
|
228
|
+
value: (text, selected) => {
|
|
229
|
+
if (text === 'on')
|
|
230
|
+
return theme.fg('success', '● on');
|
|
231
|
+
if (text === 'off')
|
|
232
|
+
return theme.fg('dim', '○ off');
|
|
233
|
+
return theme.fg(selected ? 'accent' : 'muted', text);
|
|
234
|
+
},
|
|
182
235
|
description: text => theme.fg('muted', text),
|
|
183
|
-
|
|
236
|
+
// Two cells wide to match the unselected row's " " indent — a
|
|
237
|
+
// single-cell cursor shifts the selected label one column left.
|
|
238
|
+
cursor: theme.fg('accent', '❯') + ' ',
|
|
184
239
|
hint: text => theme.fg('dim', text)
|
|
185
240
|
};
|
|
186
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Builds the framed settings panel. Split out of the command handler so the
|
|
244
|
+
* exact component the overlay shows can be rendered to a string in a test or a
|
|
245
|
+
* preview script, rather than only being inspectable by opening the TUI.
|
|
246
|
+
*/
|
|
247
|
+
export function createSettingsPanel(items, theme, onChange, onCancel) {
|
|
248
|
+
const list = new SettingsList(items, MAX_VISIBLE, makeTheme(theme), onChange, onCancel);
|
|
249
|
+
return new BorderedBox(list, CONFIG_TITLE, s => theme.fg('borderMuted', s), s => theme.fg('accent', theme.bold(s)), settingsBodyHeight(items.map(i => i.description), MAX_VISIBLE, OVERLAY_WIDTH - 8));
|
|
250
|
+
}
|
|
251
|
+
/** The full settings row list for the current config, in menu order. */
|
|
252
|
+
export function panelItems(cfg, installed) {
|
|
253
|
+
return [
|
|
254
|
+
...ITEMS.map(({ id, label, description, values }) => ({
|
|
255
|
+
id: id,
|
|
256
|
+
label,
|
|
257
|
+
description,
|
|
258
|
+
currentValue: displayValue(cfg, id, Boolean(values)),
|
|
259
|
+
values: values ?? ['on', 'off']
|
|
260
|
+
})),
|
|
261
|
+
...extensionItems(installed, cfg.extensionWhitelist)
|
|
262
|
+
];
|
|
263
|
+
}
|
|
187
264
|
async function handleTaskConfig(_args, ctx) {
|
|
188
265
|
const cfg = { ...getConfig(), extensionWhitelist: [...getConfig().extensionWhitelist] };
|
|
189
266
|
// Enumerated live at open so an installed extension appears and an
|
|
@@ -199,50 +276,36 @@ async function handleTaskConfig(_args, ctx) {
|
|
|
199
276
|
ctx.ui.notify(lines.join(' | '), 'info');
|
|
200
277
|
return;
|
|
201
278
|
}
|
|
202
|
-
await ctx.ui.custom((_tui, theme, _kb, done) => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
else if (id === 'streamInactivityMs') {
|
|
229
|
-
const opt = STREAM_INACTIVITY_OPTIONS.find(o => o.label === newValue);
|
|
230
|
-
if (opt)
|
|
231
|
-
cfg.streamInactivityMs = opt.ms;
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
;
|
|
235
|
-
cfg[id] = newValue === 'on';
|
|
236
|
-
}
|
|
237
|
-
saveConfig(cfg).catch(() => { });
|
|
238
|
-
}, () => done(undefined));
|
|
239
|
-
return new BorderedBox(list, CONFIG_TITLE, s => theme.fg('borderMuted', s), s => theme.fg('accent', theme.bold(s)));
|
|
240
|
-
}, { overlay: true, overlayOptions: { width: 58 } });
|
|
279
|
+
await ctx.ui.custom((_tui, theme, _kb, done) => createSettingsPanel(panelItems(cfg, installed), theme, (id, newValue) => {
|
|
280
|
+
if (id.startsWith(EXT_ID_PREFIX)) {
|
|
281
|
+
cfg.extensionWhitelist = applyExtensionToggle(cfg.extensionWhitelist, id.slice(EXT_ID_PREFIX.length), newValue === 'on');
|
|
282
|
+
}
|
|
283
|
+
else if (id === 'searchProvider') {
|
|
284
|
+
const provider = providerForLabel(newValue);
|
|
285
|
+
if (provider)
|
|
286
|
+
cfg.searchProvider = provider;
|
|
287
|
+
}
|
|
288
|
+
else if (id === 'requestTimeoutMs') {
|
|
289
|
+
const opt = COMMAND_TIMEOUT_OPTIONS.find(o => o.label === newValue);
|
|
290
|
+
if (opt)
|
|
291
|
+
cfg.requestTimeoutMs = opt.ms;
|
|
292
|
+
}
|
|
293
|
+
else if (id === 'streamInactivityMs') {
|
|
294
|
+
const opt = STREAM_INACTIVITY_OPTIONS.find(o => o.label === newValue);
|
|
295
|
+
if (opt)
|
|
296
|
+
cfg.streamInactivityMs = opt.ms;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
;
|
|
300
|
+
cfg[id] = newValue === 'on';
|
|
301
|
+
}
|
|
302
|
+
saveConfig(cfg).catch(() => { });
|
|
303
|
+
}, () => done(undefined)), { overlay: true, overlayOptions: { width: OVERLAY_WIDTH } });
|
|
241
304
|
}
|
|
242
305
|
export function registerConfig(pi) {
|
|
243
306
|
registerBridgeCommand(pi, 'task-config', {
|
|
244
|
-
description: 'Configure pi-task settings (remote
|
|
245
|
-
+ '
|
|
307
|
+
description: 'Configure pi-task settings (remote control, auto-commit, verify work, enforce '
|
|
308
|
+
+ 'guidelines, research, timeouts, extensions for helper sessions).',
|
|
246
309
|
handler: handleTaskConfig
|
|
247
310
|
});
|
|
248
311
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The installed pi-task version, read from package.json at runtime so nothing
|
|
3
|
+
* has to be regenerated on release. Two levels up holds for both src/<dir>
|
|
4
|
+
* (tests) and dist/<dir> (build), since tsc preserves the layout under rootDir.
|
|
5
|
+
* Falls back to '0.0.0' rather than throwing: every caller here is cosmetic
|
|
6
|
+
* (a User-Agent, a title bar) and none is worth failing over.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readPkgVersion(): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
/**
|
|
5
|
+
* The installed pi-task version, read from package.json at runtime so nothing
|
|
6
|
+
* has to be regenerated on release. Two levels up holds for both src/<dir>
|
|
7
|
+
* (tests) and dist/<dir> (build), since tsc preserves the layout under rootDir.
|
|
8
|
+
* Falls back to '0.0.0' rather than throwing: every caller here is cosmetic
|
|
9
|
+
* (a User-Agent, a title bar) and none is worth failing over.
|
|
10
|
+
*/
|
|
11
|
+
export function readPkgVersion() {
|
|
12
|
+
try {
|
|
13
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(here, '..', '..', 'package.json'), 'utf8'));
|
|
15
|
+
return typeof pkg.version === 'string' ? pkg.version : '0.0.0';
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return '0.0.0';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { dirname, join } from 'node:path';
|
|
4
1
|
import { parseHTML } from 'linkedom';
|
|
5
2
|
import { Readability } from '@mozilla/readability';
|
|
6
3
|
import TurndownService from 'turndown';
|
|
4
|
+
import { readPkgVersion } from '../shared/pkg-version.js';
|
|
7
5
|
const turndown = new TurndownService({
|
|
8
6
|
codeBlockStyle: 'fenced',
|
|
9
7
|
headingStyle: 'atx',
|
|
@@ -33,21 +31,9 @@ export function cleanHtml(html, baseUrl) {
|
|
|
33
31
|
}
|
|
34
32
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
35
33
|
const DEFAULT_MAX_BYTES = 2 * 1024 * 1024; // 2 MB
|
|
34
|
+
// Read at runtime so the User-Agent never drifts out of sync with releases.
|
|
36
35
|
const PKG_VERSION = readPkgVersion();
|
|
37
36
|
const USER_AGENT = `pi-worker/${PKG_VERSION} (+https://npmjs.com/package/@mjasnikovs/pi-worker)`;
|
|
38
|
-
// Read the version from package.json at runtime so the User-Agent never drifts
|
|
39
|
-
// out of sync with releases. Two levels up holds for both src/workers (tests)
|
|
40
|
-
// and dist/workers (build) since tsc preserves the layout under rootDir.
|
|
41
|
-
function readPkgVersion() {
|
|
42
|
-
try {
|
|
43
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
44
|
-
const pkg = JSON.parse(readFileSync(join(here, '..', '..', 'package.json'), 'utf8'));
|
|
45
|
-
return typeof pkg.version === 'string' ? pkg.version : '0.0.0';
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return '0.0.0';
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
37
|
// Decide how to handle a response based on its content-type. HTML is run through
|
|
52
38
|
// the readability/turndown pipeline; text-ish formats (markdown, plain text,
|
|
53
39
|
// JSON, XML/feeds) are already clean and pass through verbatim; binary formats
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.48",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|