@pasko70/pibo 3.2.3 → 3.2.4
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/agent-runtime/auth.js +3 -5
- package/dist/agent-runtime/portable-history.js +1 -1
- package/dist/agent-runtimes/codex-native/client.js +2 -5
- package/dist/agent-runtimes/codex-native/redaction.js +3 -27
- package/dist/agent-runtimes/omp/client.js +2 -4
- package/dist/agent-runtimes/pi/adapter.js +1 -0
- package/dist/agent-runtimes/pi/runtime.js +1 -1
- package/dist/apps/chat/agent-store.js +1 -1
- package/dist/apps/chat/chat-files.js +3 -1
- package/dist/apps/chat/chat-request-normalizers.js +1 -1
- package/dist/apps/chat/chat-user-skill-routes.js +7 -0
- package/dist/apps/chat/data/project-service.js +49 -1
- package/dist/apps/chat/data/session-query-service.js +46 -6
- package/dist/apps/chat/loop-api.js +10 -1
- package/dist/apps/chat/ralph-api.js +10 -1
- package/dist/apps/chat/web-app.js +22 -5
- package/dist/apps/chat-ui/assets/{dist-CTsyojIh.js → dist-CTC-_3l0.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BHio-Mzx.js → dist-CftLEFd5.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DJ6Bhcuk.js → dist-Dq27Wt2q.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CPKEoSPK.js → dist-Y4fHV5-Z.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DNHFRMCk.js → dist-dGF-PDGY.js} +1 -1
- package/dist/apps/chat-ui/assets/index-C_P0gXvJ.js +228 -0
- package/dist/apps/chat-ui/assets/{index-Dc-6Xupt.css → index-DUGVw259.css} +1 -1
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/{index-B1TuQNY5.js → index-B5X0NaRo.js} +4 -4
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/InkSessionApp.js +38 -28
- package/dist/auth/cli.js +4 -6
- package/dist/auth/machine-keys.js +20 -3
- package/dist/cli-session/localSessionSource.js +2 -4
- package/dist/compute/cli.js +16 -13
- package/dist/compute/docker.js +96 -48
- package/dist/core/base-prompt.js +27 -5
- package/dist/core/context-build.js +5 -15
- package/dist/core/sensitive-data-redaction.js +2 -2
- package/dist/core/session-router.js +15 -4
- package/dist/cron/cli.js +42 -10
- package/dist/cron/store.js +1 -1
- package/dist/data/cli.js +15 -7
- package/dist/data/payload-store.js +38 -0
- package/dist/data/schema.js +9 -1
- package/dist/debug/index.js +5 -0
- package/dist/debug/pty.js +45 -10
- package/dist/gateway/cli.js +18 -2
- package/dist/gateway/server.js +1 -0
- package/dist/loops/service.js +32 -16
- package/dist/mcp/runtime-session.js +2 -1
- package/dist/pi-packages/cli.js +5 -0
- package/dist/previews/cli.js +2 -6
- package/dist/previews/config.js +10 -0
- package/dist/previews/manager.js +2 -9
- package/dist/ralph/service.js +21 -10
- package/dist/session-ui/statusViewModel.js +2 -4
- package/dist/sessions/pibo-data-store.js +18 -0
- package/dist/sessions/store.js +9 -0
- package/dist/setup/cli.js +14 -7
- package/dist/setup/installation-profiles.js +20 -7
- package/dist/shared/trace-event-projection.js +32 -1
- package/dist/subagents/tool.js +3 -3
- package/dist/tools/browser-use-leases.js +11 -3
- package/dist/tools/index.js +0 -1
- package/dist/tools/runtime/node-worker-source.js +39 -5
- package/dist/tools/runtime/python-backend.js +6 -2
- package/dist/tools/runtime/registry.js +38 -6
- package/dist/vscode/cli.js +1 -2
- package/dist/vscode/install.js +21 -14
- package/npm-shrinkwrap.json +2 -2
- package/package.json +3 -1
- package/dist/apps/chat-ui/assets/index-Y6jYzsaC.js +0 -228
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta name="theme-color" content="#101d22" />
|
|
7
7
|
<title>Pibo</title>
|
|
8
|
-
<script type="module" crossorigin src="/apps/chat-vscode/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/apps/chat-vscode/assets/index-B5X0NaRo.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-b18ZkEo0.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { Box, Text, useApp, useInput } from "ink";
|
|
2
|
+
import { Box, Text, useApp, useInput, usePaste } from "ink";
|
|
3
3
|
import { isCliSourceError } from "../../cli-session/index.js";
|
|
4
|
-
import { buildCompactTerminalRows, buildSlashCommandCatalog, buildTerminalStatusViewModel, commandSupportLabel, filterSlashCommands, formatSlashCommand, groupSlashCommandsForHelp, normalizeCommandErrorDescriptor, progressBarText, } from "../../session-ui/index.js";
|
|
4
|
+
import { buildCompactTerminalRows, buildSlashCommandCatalog, buildTerminalStatusViewModel, commandSupportLabel, filterSlashCommands, formatSlashCommand, groupSlashCommandsForHelp, normalizeCommandErrorDescriptor, progressBarText, redactTerminalSecret, } from "../../session-ui/index.js";
|
|
5
5
|
import { InkTerminalView } from "./InkTerminalView.js";
|
|
6
6
|
import { isExpandableTerminalRow } from "./InkTerminalRow.js";
|
|
7
7
|
const IDENTIFIER_PATTERN = /^(?:user|room|ps|pi|entry|evt|run|create|provider|model|agent)[_:]/i;
|
|
@@ -32,6 +32,12 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
|
|
|
32
32
|
const closedRef = useRef(false);
|
|
33
33
|
const stateRef = useRef(state);
|
|
34
34
|
const pickerActivationRef = useRef({ inFlight: false });
|
|
35
|
+
const commitInputState = useCallback((update) => {
|
|
36
|
+
const next = update(stateRef.current);
|
|
37
|
+
stateRef.current = next;
|
|
38
|
+
setState(next);
|
|
39
|
+
return next;
|
|
40
|
+
}, []);
|
|
35
41
|
useEffect(() => {
|
|
36
42
|
stateRef.current = state;
|
|
37
43
|
}, [state]);
|
|
@@ -220,82 +226,89 @@ export function InkSessionApp({ source, initialSessionId, maxRows, maxLineChars,
|
|
|
220
226
|
cleanup();
|
|
221
227
|
};
|
|
222
228
|
}, [cleanup, initialSessionId, openRoomPicker, openSession, source]);
|
|
229
|
+
usePaste((input) => {
|
|
230
|
+
if (closedRef.current || input.length === 0)
|
|
231
|
+
return;
|
|
232
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "text", value: input }));
|
|
233
|
+
});
|
|
223
234
|
useInput((input, key) => {
|
|
224
235
|
if (closedRef.current)
|
|
225
236
|
return;
|
|
237
|
+
const returnTerminatedInput = !key.ctrl && !key.meta && !key.return && (input.endsWith("\r") || input.endsWith("\n"));
|
|
238
|
+
if (returnTerminatedInput) {
|
|
239
|
+
const text = input.slice(0, -1);
|
|
240
|
+
if (text.length > 0)
|
|
241
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "text", value: text }));
|
|
242
|
+
}
|
|
226
243
|
if (key.ctrl && input === "c") {
|
|
227
244
|
requestExit();
|
|
228
245
|
return;
|
|
229
246
|
}
|
|
230
247
|
if (key.escape) {
|
|
231
|
-
|
|
248
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "escape" }));
|
|
232
249
|
return;
|
|
233
250
|
}
|
|
234
251
|
if (key.upArrow) {
|
|
235
|
-
|
|
252
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "up" }));
|
|
236
253
|
return;
|
|
237
254
|
}
|
|
238
255
|
if (key.downArrow) {
|
|
239
|
-
|
|
256
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "down" }));
|
|
240
257
|
return;
|
|
241
258
|
}
|
|
242
259
|
if (key.leftArrow) {
|
|
243
|
-
|
|
260
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "left" }));
|
|
244
261
|
return;
|
|
245
262
|
}
|
|
246
263
|
if (key.rightArrow) {
|
|
247
|
-
|
|
264
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "right" }));
|
|
248
265
|
return;
|
|
249
266
|
}
|
|
250
267
|
if (key.home) {
|
|
251
|
-
|
|
268
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "home" }));
|
|
252
269
|
return;
|
|
253
270
|
}
|
|
254
271
|
if (key.end) {
|
|
255
|
-
|
|
272
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "end" }));
|
|
256
273
|
return;
|
|
257
274
|
}
|
|
258
|
-
if (key.return) {
|
|
275
|
+
if (key.return || returnTerminatedInput) {
|
|
259
276
|
const submitted = stateRef.current.input;
|
|
260
277
|
if (submitted.length === 0 && !stateRef.current.picker && !stateRef.current.slashSuggestions && canToggleSelectedRowDetails(stateRef.current)) {
|
|
261
|
-
|
|
278
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "toggle-details" }));
|
|
262
279
|
return;
|
|
263
280
|
}
|
|
264
281
|
if (stateRef.current.slashSuggestions && !stateRef.current.picker) {
|
|
265
282
|
const accepted = acceptSlashSuggestion(stateRef.current);
|
|
266
283
|
if (accepted.runInput) {
|
|
267
|
-
|
|
284
|
+
commitInputState((current) => ({ ...current, input: "", inputCursor: 0, slashSuggestions: undefined, message: undefined, error: undefined }));
|
|
268
285
|
void submitCommandOrMessage(accepted.runInput);
|
|
269
286
|
}
|
|
270
287
|
else {
|
|
271
|
-
|
|
288
|
+
commitInputState((current) => ({ ...current, input: accepted.input, inputCursor: accepted.input.length, slashSuggestions: undefined, message: `Accepted ${accepted.input.trim()}. Press Enter to run or add arguments.`, error: undefined }));
|
|
272
289
|
}
|
|
273
290
|
return;
|
|
274
291
|
}
|
|
275
292
|
if (submitted.trimStart().startsWith("/")) {
|
|
276
|
-
|
|
293
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "enter" }));
|
|
277
294
|
void submitCommandOrMessage(submitted);
|
|
278
295
|
return;
|
|
279
296
|
}
|
|
280
297
|
if (stateRef.current.picker) {
|
|
281
|
-
|
|
298
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "enter" }));
|
|
282
299
|
void selectPickerItem();
|
|
283
300
|
return;
|
|
284
301
|
}
|
|
285
|
-
|
|
302
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "enter" }));
|
|
286
303
|
void submitCommandOrMessage(submitted);
|
|
287
304
|
return;
|
|
288
305
|
}
|
|
289
306
|
if (key.backspace || key.delete) {
|
|
290
|
-
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
if (input === "d" && !key.ctrl && !key.meta && stateRef.current.input.length === 0 && !stateRef.current.picker && !stateRef.current.slashSuggestions && canToggleSelectedRowDetails(stateRef.current)) {
|
|
294
|
-
setState((current) => reduceInkSessionInputState(current, { type: "toggle-details" }));
|
|
307
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "backspace" }));
|
|
295
308
|
return;
|
|
296
309
|
}
|
|
297
|
-
if (input && !key.ctrl && !key.meta) {
|
|
298
|
-
|
|
310
|
+
if (input && !returnTerminatedInput && !key.ctrl && !key.meta) {
|
|
311
|
+
commitInputState((current) => reduceInkSessionInputState(current, { type: "text", value: input }));
|
|
299
312
|
}
|
|
300
313
|
});
|
|
301
314
|
return React.createElement(InkSessionAppView, { state, maxRows, maxLineChars });
|
|
@@ -540,7 +553,7 @@ export function cliCommandSummaryText(catalog = buildSlashCommandCatalog()) {
|
|
|
540
553
|
return `Commands: ${commands.join(" ")} (type / for suggestions, /help for catalog)`;
|
|
541
554
|
}
|
|
542
555
|
export function cliCommandHintText() {
|
|
543
|
-
return "commands: / opens palette · /status runtime · /room /session navigate · /help catalog · ↑↓ focus rows ·
|
|
556
|
+
return "commands: / opens palette · /status runtime · /room /session navigate · /help catalog · ↑↓ focus rows · enter details · ctrl-c exit";
|
|
544
557
|
}
|
|
545
558
|
export function cliSessionSlashHelpText(catalog = buildSlashCommandCatalog()) {
|
|
546
559
|
const grouped = groupSlashCommandsForHelp(catalog);
|
|
@@ -1307,10 +1320,7 @@ export function formatCliSessionError(error) {
|
|
|
1307
1320
|
return `${message}. Recovery: use /status for source state or restart the CLI if the problem persists.`;
|
|
1308
1321
|
}
|
|
1309
1322
|
function redactCliSessionStatusText(text) {
|
|
1310
|
-
return text
|
|
1311
|
-
.replace(/\b([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD)[A-Z0-9_]*)\s*=\s*([^\s]+)/gi, "$1=[redacted]")
|
|
1312
|
-
.replace(/\b(api[_-]?key|token|secret|password)\s*[:=]\s*([^\s]+)/gi, "$1=[redacted]")
|
|
1313
|
-
.replace(/\b(?:sk|pk|pibo|ghp|github_pat)_[A-Za-z0-9_\-]{8,}\b/g, "[redacted]");
|
|
1323
|
+
return redactTerminalSecret(text);
|
|
1314
1324
|
}
|
|
1315
1325
|
function errorMessage(error) {
|
|
1316
1326
|
return error instanceof Error ? error.message : String(error);
|
package/dist/auth/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
import { loadPiboConfig } from "../config/config.js";
|
|
6
6
|
import { piboHomePath } from "../core/pibo-home.js";
|
|
7
7
|
import { protectPrivateFileSync } from "../core/private-path.js";
|
|
8
|
-
import { generateMachineKey, getDefaultMachineKeyStorePath, importMachineKeyRecord, listMachineKeys, parseMachineKeyRecord, revokeMachineKey, } from "./machine-keys.js";
|
|
8
|
+
import { generateMachineKey, getDefaultMachineKeyStorePath, importMachineKeyRecord, listMachineKeys, parseIsoTimestamp, parseMachineKeyRecord, revokeMachineKey, } from "./machine-keys.js";
|
|
9
9
|
function printJson(value) {
|
|
10
10
|
console.log(JSON.stringify(value, null, 2));
|
|
11
11
|
}
|
|
@@ -117,12 +117,10 @@ function writePrivateNewFile(path, content) {
|
|
|
117
117
|
function parseExpiresAt(value) {
|
|
118
118
|
if (value === undefined)
|
|
119
119
|
return undefined;
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
throw new Error("--expires-at must be an ISO timestamp");
|
|
123
|
-
if (timestamp <= Date.now())
|
|
120
|
+
const expiresAt = parseIsoTimestamp(value, "--expires-at");
|
|
121
|
+
if (Date.parse(expiresAt) <= Date.now())
|
|
124
122
|
throw new Error("--expires-at must be in the future");
|
|
125
|
-
return
|
|
123
|
+
return expiresAt;
|
|
126
124
|
}
|
|
127
125
|
export async function runAuthCli(argv = process.argv) {
|
|
128
126
|
if (argv.length <= 2 || argv[2] === "--help" || argv[2] === "-h") {
|
|
@@ -15,11 +15,28 @@ export function getDefaultMachineKeyStorePath() {
|
|
|
15
15
|
function hashMachineKeyToken(token) {
|
|
16
16
|
return createHash("sha256").update(token, "utf8").digest();
|
|
17
17
|
}
|
|
18
|
-
function parseIsoTimestamp(value, field) {
|
|
19
|
-
if (typeof value !== "string"
|
|
18
|
+
export function parseIsoTimestamp(value, field) {
|
|
19
|
+
if (typeof value !== "string")
|
|
20
|
+
throw new Error(`${field} must be an ISO timestamp`);
|
|
21
|
+
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/);
|
|
22
|
+
if (!match)
|
|
23
|
+
throw new Error(`${field} must be an ISO timestamp`);
|
|
24
|
+
const [, yearText, monthText, dayText, hourText, minuteText, secondText] = match;
|
|
25
|
+
const year = Number(yearText);
|
|
26
|
+
const month = Number(monthText);
|
|
27
|
+
const day = Number(dayText);
|
|
28
|
+
const hour = Number(hourText);
|
|
29
|
+
const minute = Number(minuteText);
|
|
30
|
+
const second = Number(secondText);
|
|
31
|
+
const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
32
|
+
const daysByMonth = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
33
|
+
if (month < 1 || month > 12 || day < 1 || day > daysByMonth[month - 1] || hour > 23 || minute > 59 || second > 59) {
|
|
20
34
|
throw new Error(`${field} must be an ISO timestamp`);
|
|
21
35
|
}
|
|
22
|
-
|
|
36
|
+
const timestamp = Date.parse(value);
|
|
37
|
+
if (Number.isNaN(timestamp))
|
|
38
|
+
throw new Error(`${field} must be an ISO timestamp`);
|
|
39
|
+
return new Date(timestamp).toISOString();
|
|
23
40
|
}
|
|
24
41
|
function optionalString(value, field) {
|
|
25
42
|
if (value === undefined)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { redactSensitiveText } from "../core/sensitive-data-redaction.js";
|
|
2
3
|
import { DEFAULT_PIBO_PROFILE_NAME, resolvePiboProfileNameFromRegistryOrDefault, } from "../plugins/builtin.js";
|
|
3
4
|
import { createDefaultPiboUserProfileRegistry } from "../plugins/user-profile-resources.js";
|
|
4
5
|
import { createDefaultPiboDataSessionStore } from "../sessions/pibo-data-store.js";
|
|
@@ -863,10 +864,7 @@ function parseCliOutputPersistenceState(value) {
|
|
|
863
864
|
return { version: 1, piboSessionId: candidate.piboSessionId, deliveries };
|
|
864
865
|
}
|
|
865
866
|
export function redactCliSecretText(text) {
|
|
866
|
-
return text
|
|
867
|
-
.replace(/\b([A-Z0-9_]*(?:API[_-]?KEY|TOKEN|SECRET|PASSWORD)[A-Z0-9_]*)\s*=\s*([^\s]+)/gi, "$1=[redacted]")
|
|
868
|
-
.replace(/\b(api[_-]?key|token|secret|password)\s*[:=]\s*([^\s]+)/gi, "$1=[redacted]")
|
|
869
|
-
.replace(/\b(?:sk|pk|pibo|ghp|github_pat)_[A-Za-z0-9_\-]{8,}\b/g, "[redacted]");
|
|
867
|
+
return redactSensitiveText(text);
|
|
870
868
|
}
|
|
871
869
|
function sessionToSummary(session, defaultRoomId) {
|
|
872
870
|
return {
|
package/dist/compute/cli.js
CHANGED
|
@@ -13,11 +13,14 @@ function computeImageBuildConfig() {
|
|
|
13
13
|
function printJson(value) {
|
|
14
14
|
console.log(JSON.stringify(value, null, 2));
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
function positiveIntegerOption(optionName) {
|
|
17
|
+
return (value) => {
|
|
18
|
+
const parsed = Number(value);
|
|
19
|
+
if (value.trim() === "" || !Number.isInteger(parsed) || parsed <= 0) {
|
|
20
|
+
throw new InvalidArgumentError(`${optionName} must be a positive integer`);
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
};
|
|
21
24
|
}
|
|
22
25
|
function parseNonNegativeNumber(value) {
|
|
23
26
|
const parsed = Number(value);
|
|
@@ -233,8 +236,8 @@ Next:
|
|
|
233
236
|
.description("Create a one-time worker from the current Docker image")
|
|
234
237
|
.option("--name <name>", "Set the container name")
|
|
235
238
|
.option("--holder <holder>", "Tag the container holder")
|
|
236
|
-
.option("--ttl-seconds <n>", "Tag the worker TTL in seconds")
|
|
237
|
-
.option("--idle-seconds <n>", "Tag the worker idle retention in seconds")
|
|
239
|
+
.option("--ttl-seconds <n>", "Tag the worker TTL in seconds", positiveIntegerOption("--ttl-seconds"))
|
|
240
|
+
.option("--idle-seconds <n>", "Tag the worker idle retention in seconds", positiveIntegerOption("--idle-seconds"))
|
|
238
241
|
.option("--ralph-job-id <id>", "Tag the Ralph job id when this worker is Ralph-owned")
|
|
239
242
|
.option("--ralph-run-id <id>", "Tag the Ralph run id when this worker is Ralph-owned")
|
|
240
243
|
.addHelpText("after", `
|
|
@@ -262,8 +265,8 @@ Environment:
|
|
|
262
265
|
imageName: image.imageName,
|
|
263
266
|
name: options.name,
|
|
264
267
|
holder: options.holder,
|
|
265
|
-
ttlSeconds:
|
|
266
|
-
idleSeconds:
|
|
268
|
+
ttlSeconds: options.ttlSeconds,
|
|
269
|
+
idleSeconds: options.idleSeconds,
|
|
267
270
|
ralphJobId: options.ralphJobId,
|
|
268
271
|
ralphRunId: options.ralphRunId,
|
|
269
272
|
});
|
|
@@ -285,8 +288,8 @@ Next:
|
|
|
285
288
|
.requiredOption("--worktree <name>", "Name the Git worktree and branch")
|
|
286
289
|
.option("--repo <path>", "Use this repository", WORKSPACE_DIR)
|
|
287
290
|
.option("--holder <holder>", "Tag the container holder")
|
|
288
|
-
.option("--ttl-seconds <n>", "Tag the worker TTL in seconds")
|
|
289
|
-
.option("--idle-seconds <n>", "Tag the worker idle retention in seconds")
|
|
291
|
+
.option("--ttl-seconds <n>", "Tag the worker TTL in seconds", positiveIntegerOption("--ttl-seconds"))
|
|
292
|
+
.option("--idle-seconds <n>", "Tag the worker idle retention in seconds", positiveIntegerOption("--idle-seconds"))
|
|
290
293
|
.option("--ralph-job-id <id>", "Tag the Ralph job id when this worker is Ralph-owned")
|
|
291
294
|
.option("--ralph-run-id <id>", "Tag the Ralph run id when this worker is Ralph-owned")
|
|
292
295
|
.addHelpText("after", `
|
|
@@ -316,8 +319,8 @@ Example:
|
|
|
316
319
|
worktreeName: options.worktree,
|
|
317
320
|
imageName: image.imageName,
|
|
318
321
|
holder: options.holder,
|
|
319
|
-
ttlSeconds:
|
|
320
|
-
idleSeconds:
|
|
322
|
+
ttlSeconds: options.ttlSeconds,
|
|
323
|
+
idleSeconds: options.idleSeconds,
|
|
321
324
|
ralphJobId: options.ralphJobId,
|
|
322
325
|
ralphRunId: options.ralphRunId,
|
|
323
326
|
});
|
package/dist/compute/docker.js
CHANGED
|
@@ -401,6 +401,7 @@ function buildComputeWorkerMetadataLabels(options) {
|
|
|
401
401
|
`${LABEL_PORT_BLOCK}=${options.portBlock}`,
|
|
402
402
|
`${LABEL_TTL_SECONDS}=${options.ttlSeconds}`,
|
|
403
403
|
`${LABEL_IDLE_SECONDS}=${options.idleSeconds}`,
|
|
404
|
+
`${LABEL_LAST_USED_AT}=${options.createdAt}`,
|
|
404
405
|
...(options.holder ? [`${LABEL_HOLDER}=${options.holder}`] : []),
|
|
405
406
|
...(options.worktree ? [`${LABEL_WORKTREE}=${options.worktree}`] : []),
|
|
406
407
|
...(options.worktreePath ? [`${LABEL_WORKTREE_PATH}=${options.worktreePath}`] : []),
|
|
@@ -488,60 +489,100 @@ export function buildDevWorkerDockerRunArgs(options) {
|
|
|
488
489
|
"ln -sf /workspace/dist/bin/pibo.js /usr/local/bin/pibo && exec tail -f /dev/null",
|
|
489
490
|
];
|
|
490
491
|
}
|
|
492
|
+
export function validateDevWorktreeName(name) {
|
|
493
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(name)) {
|
|
494
|
+
throw new Error("Dev worktree name must use only letters, numbers, dots, underscores, and hyphens, and must start with a letter or number");
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
async function localBranchExists(repoDir, name) {
|
|
498
|
+
try {
|
|
499
|
+
await execFileAsync("git", ["show-ref", "--verify", "--quiet", `refs/heads/${name}`], { cwd: repoDir });
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
if (error instanceof Error && "code" in error && error.code === 1)
|
|
504
|
+
return false;
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
491
508
|
export async function spawnDevWorker(options) {
|
|
509
|
+
validateDevWorktreeName(options.worktreeName);
|
|
510
|
+
const branchExisted = await localBranchExists(options.repoDir, options.worktreeName);
|
|
492
511
|
const worktreePath = path.join(options.repoDir, ".worktrees", options.worktreeName);
|
|
493
512
|
await createWorktree(options.repoDir, options.worktreeName);
|
|
494
|
-
const id = `pibo-dev-${options.worktreeName}`;
|
|
495
|
-
const createdAt = new Date().toISOString();
|
|
496
|
-
// Mount host node_modules into the container so dependencies are immediately available
|
|
497
|
-
const hostNodeModules = path.join(options.repoDir, "node_modules");
|
|
498
|
-
let nodeModulesMount;
|
|
499
513
|
try {
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
514
|
+
const id = `pibo-dev-${options.worktreeName}`;
|
|
515
|
+
const createdAt = new Date().toISOString();
|
|
516
|
+
// Mount host node_modules into the container so dependencies are immediately available
|
|
517
|
+
const hostNodeModules = path.join(options.repoDir, "node_modules");
|
|
518
|
+
let nodeModulesMount;
|
|
519
|
+
try {
|
|
520
|
+
const { stdout } = await execFileAsync("ls", ["-d", hostNodeModules]);
|
|
521
|
+
if (stdout.trim())
|
|
522
|
+
nodeModulesMount = hostNodeModules;
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
// host node_modules does not exist; skip mount
|
|
526
|
+
}
|
|
527
|
+
const ports = await withDevPortAllocationLock(async () => {
|
|
528
|
+
const block = await findNextPortBlock();
|
|
529
|
+
const base = DEV_PORT_BASE + block * DEV_PORT_BLOCK_SIZE;
|
|
530
|
+
const gatewayPort = base;
|
|
531
|
+
const cdpPort = base + 1;
|
|
532
|
+
const webPort = base + 2;
|
|
533
|
+
const webUIPortChat = base + 3;
|
|
534
|
+
const webUIPortContext = base + 4;
|
|
535
|
+
const args = buildDevWorkerDockerRunArgs({
|
|
536
|
+
id,
|
|
537
|
+
imageName: options.imageName,
|
|
538
|
+
worktreePath,
|
|
539
|
+
worktreeName: options.worktreeName,
|
|
540
|
+
block,
|
|
541
|
+
gatewayPort,
|
|
542
|
+
cdpPort,
|
|
543
|
+
webPort,
|
|
544
|
+
webUIPortChat,
|
|
545
|
+
webUIPortContext,
|
|
546
|
+
createdAt,
|
|
547
|
+
holder: options.holder,
|
|
548
|
+
ttlSeconds: options.ttlSeconds,
|
|
549
|
+
idleSeconds: options.idleSeconds,
|
|
550
|
+
ralphJobId: options.ralphJobId,
|
|
551
|
+
ralphRunId: options.ralphRunId,
|
|
552
|
+
hostNodeModules: nodeModulesMount,
|
|
553
|
+
});
|
|
554
|
+
await execFileAsync("docker", args, { cwd: options.repoDir });
|
|
555
|
+
return { gatewayPort, cdpPort, webPort, webUIPortChat, webUIPortContext };
|
|
556
|
+
});
|
|
557
|
+
return {
|
|
558
|
+
id,
|
|
559
|
+
image: options.imageName ?? IMAGE_NAME,
|
|
560
|
+
gatewayHost: COMPUTE_PUBLISH_HOST,
|
|
561
|
+
...ports,
|
|
562
|
+
connect: `docker exec -it ${id} bash`,
|
|
563
|
+
worktree: worktreePath,
|
|
564
|
+
};
|
|
503
565
|
}
|
|
504
|
-
catch {
|
|
505
|
-
|
|
566
|
+
catch (error) {
|
|
567
|
+
const cleanupErrors = [];
|
|
568
|
+
try {
|
|
569
|
+
await execFileAsync("git", ["worktree", "remove", "--force", worktreePath], { cwd: options.repoDir });
|
|
570
|
+
}
|
|
571
|
+
catch (cleanupError) {
|
|
572
|
+
cleanupErrors.push(cleanupError);
|
|
573
|
+
}
|
|
574
|
+
if (!branchExisted) {
|
|
575
|
+
try {
|
|
576
|
+
await execFileAsync("git", ["branch", "-D", options.worktreeName], { cwd: options.repoDir });
|
|
577
|
+
}
|
|
578
|
+
catch (cleanupError) {
|
|
579
|
+
cleanupErrors.push(cleanupError);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (cleanupErrors.length > 0)
|
|
583
|
+
throw new AggregateError([error, ...cleanupErrors], `Dev worker spawn failed and cleanup was incomplete for ${options.worktreeName}`);
|
|
584
|
+
throw error;
|
|
506
585
|
}
|
|
507
|
-
const ports = await withDevPortAllocationLock(async () => {
|
|
508
|
-
const block = await findNextPortBlock();
|
|
509
|
-
const base = DEV_PORT_BASE + block * DEV_PORT_BLOCK_SIZE;
|
|
510
|
-
const gatewayPort = base;
|
|
511
|
-
const cdpPort = base + 1;
|
|
512
|
-
const webPort = base + 2;
|
|
513
|
-
const webUIPortChat = base + 3;
|
|
514
|
-
const webUIPortContext = base + 4;
|
|
515
|
-
const args = buildDevWorkerDockerRunArgs({
|
|
516
|
-
id,
|
|
517
|
-
imageName: options.imageName,
|
|
518
|
-
worktreePath,
|
|
519
|
-
worktreeName: options.worktreeName,
|
|
520
|
-
block,
|
|
521
|
-
gatewayPort,
|
|
522
|
-
cdpPort,
|
|
523
|
-
webPort,
|
|
524
|
-
webUIPortChat,
|
|
525
|
-
webUIPortContext,
|
|
526
|
-
createdAt,
|
|
527
|
-
holder: options.holder,
|
|
528
|
-
ttlSeconds: options.ttlSeconds,
|
|
529
|
-
idleSeconds: options.idleSeconds,
|
|
530
|
-
ralphJobId: options.ralphJobId,
|
|
531
|
-
ralphRunId: options.ralphRunId,
|
|
532
|
-
hostNodeModules: nodeModulesMount,
|
|
533
|
-
});
|
|
534
|
-
await execFileAsync("docker", args, { cwd: options.repoDir });
|
|
535
|
-
return { gatewayPort, cdpPort, webPort, webUIPortChat, webUIPortContext };
|
|
536
|
-
});
|
|
537
|
-
return {
|
|
538
|
-
id,
|
|
539
|
-
image: options.imageName ?? IMAGE_NAME,
|
|
540
|
-
gatewayHost: COMPUTE_PUBLISH_HOST,
|
|
541
|
-
...ports,
|
|
542
|
-
connect: `docker exec -it ${id} bash`,
|
|
543
|
-
worktree: worktreePath,
|
|
544
|
-
};
|
|
545
586
|
}
|
|
546
587
|
export function buildWorkerDockerRunArgs(options) {
|
|
547
588
|
const policy = options.policy ?? resolveComputeResourcePolicy();
|
|
@@ -727,6 +768,8 @@ function workerFromLabels(base) {
|
|
|
727
768
|
hostPid: base.hostPid,
|
|
728
769
|
createdAt: base.labels[LABEL_CREATED_AT] ?? "unknown",
|
|
729
770
|
lastUsedAt: base.labels[LABEL_LAST_USED_AT],
|
|
771
|
+
ttlSeconds: numberLabel(base.labels[LABEL_TTL_SECONDS]),
|
|
772
|
+
idleSeconds: numberLabel(base.labels[LABEL_IDLE_SECONDS]),
|
|
730
773
|
holder: base.labels[LABEL_HOLDER],
|
|
731
774
|
worktree: base.labels[LABEL_WORKTREE],
|
|
732
775
|
worktreePath: base.labels[LABEL_WORKTREE_PATH],
|
|
@@ -865,6 +908,7 @@ export function buildComputeWorkerReapPlan(workers, options = {}) {
|
|
|
865
908
|
const skipReasons = [];
|
|
866
909
|
const created = new Date(worker.createdAt).getTime();
|
|
867
910
|
const ageMinutes = Number.isNaN(created) ? undefined : (now.getTime() - created) / 1000 / 60;
|
|
911
|
+
const lastUsed = new Date(worker.lastUsedAt ?? worker.createdAt).getTime();
|
|
868
912
|
if (worker.role === "dev" && !resolved.includeDev)
|
|
869
913
|
skipReasons.push("dev-worker-preserved");
|
|
870
914
|
if (worker.role !== "worker" && worker.role !== "dev")
|
|
@@ -875,6 +919,10 @@ export function buildComputeWorkerReapPlan(workers, options = {}) {
|
|
|
875
919
|
reasons.push(worker.oomKilled ? "oom-killed" : "dirty");
|
|
876
920
|
if (ageMinutes !== undefined && ageMinutes > resolved.maxAgeMinutes)
|
|
877
921
|
reasons.push("old");
|
|
922
|
+
if (ageMinutes !== undefined && worker.ttlSeconds !== undefined && worker.ttlSeconds > 0 && ageMinutes * 60 >= worker.ttlSeconds)
|
|
923
|
+
reasons.push("ttl-expired");
|
|
924
|
+
if (!Number.isNaN(lastUsed) && worker.idleSeconds !== undefined && worker.idleSeconds > 0 && now.getTime() - lastUsed >= worker.idleSeconds * 1000)
|
|
925
|
+
reasons.push("idle-expired");
|
|
878
926
|
if (reasons.length === 0)
|
|
879
927
|
skipReasons.push("not-selected");
|
|
880
928
|
const action = skipReasons.length === 0 ? "remove" : "skip";
|
package/dist/core/base-prompt.js
CHANGED
|
@@ -10,6 +10,14 @@ function getBasePromptStatePath(cwd) {
|
|
|
10
10
|
function getCustomBasePromptPath(cwd) {
|
|
11
11
|
return resolve(cwd, ".pibo/base-prompt.md");
|
|
12
12
|
}
|
|
13
|
+
function getLegacyBasePromptPath(cwd) {
|
|
14
|
+
return resolve(cwd, ".pibo/SYSTEM.md");
|
|
15
|
+
}
|
|
16
|
+
function effectiveMode(state, customExists, legacyExists) {
|
|
17
|
+
if (legacyExists)
|
|
18
|
+
return "legacy";
|
|
19
|
+
return state.mode === "custom" && customExists ? "custom" : "library";
|
|
20
|
+
}
|
|
13
21
|
function normalizeMode(value) {
|
|
14
22
|
return value === "custom" ? "custom" : "library";
|
|
15
23
|
}
|
|
@@ -34,8 +42,7 @@ function writeBasePromptState(cwd, state) {
|
|
|
34
42
|
writeFileSync(path, `${JSON.stringify(state, null, 2)}\n`);
|
|
35
43
|
}
|
|
36
44
|
export function getActivePiboBasePromptPath(cwd = process.cwd()) {
|
|
37
|
-
|
|
38
|
-
if (existsSync(legacyOverridePath))
|
|
45
|
+
if (existsSync(getLegacyBasePromptPath(cwd)))
|
|
39
46
|
return undefined;
|
|
40
47
|
const customPath = getCustomBasePromptPath(cwd);
|
|
41
48
|
const state = readBasePromptState(cwd);
|
|
@@ -46,14 +53,17 @@ export function getActivePiboBasePromptPath(cwd = process.cwd()) {
|
|
|
46
53
|
export async function readPiboBasePrompt(cwd = process.cwd()) {
|
|
47
54
|
const state = readBasePromptState(cwd);
|
|
48
55
|
const customPath = getCustomBasePromptPath(cwd);
|
|
56
|
+
const legacyPath = getLegacyBasePromptPath(cwd);
|
|
49
57
|
const customExists = existsSync(customPath);
|
|
50
|
-
const
|
|
58
|
+
const legacyExists = existsSync(legacyPath);
|
|
59
|
+
const [libraryMarkdown, customMarkdown, legacyMarkdown] = await Promise.all([
|
|
51
60
|
readFile(PIBO_LIBRARY_BASE_PROMPT_PATH, "utf-8"),
|
|
52
61
|
customExists ? readFile(customPath, "utf-8") : Promise.resolve(""),
|
|
62
|
+
legacyExists ? readFile(legacyPath, "utf-8") : Promise.resolve(""),
|
|
53
63
|
]);
|
|
54
64
|
return {
|
|
55
65
|
mode: state.mode,
|
|
56
|
-
effectiveMode: state
|
|
66
|
+
effectiveMode: effectiveMode(state, customExists, legacyExists),
|
|
57
67
|
library: {
|
|
58
68
|
path: PIBO_LIBRARY_BASE_PROMPT_PATH,
|
|
59
69
|
markdown: libraryMarkdown,
|
|
@@ -64,6 +74,11 @@ export async function readPiboBasePrompt(cwd = process.cwd()) {
|
|
|
64
74
|
exists: customExists,
|
|
65
75
|
updatedAt: customExists ? state.updatedAt : undefined,
|
|
66
76
|
},
|
|
77
|
+
legacy: {
|
|
78
|
+
path: legacyPath,
|
|
79
|
+
markdown: legacyMarkdown,
|
|
80
|
+
exists: legacyExists,
|
|
81
|
+
},
|
|
67
82
|
};
|
|
68
83
|
}
|
|
69
84
|
export async function savePiboCustomBasePrompt(markdown, cwd = process.cwd()) {
|
|
@@ -87,9 +102,11 @@ export function setPiboBasePromptMode(mode, cwd = process.cwd()) {
|
|
|
87
102
|
});
|
|
88
103
|
const state = readBasePromptState(cwd);
|
|
89
104
|
const customExists = existsSync(customPath);
|
|
105
|
+
const legacyPath = getLegacyBasePromptPath(cwd);
|
|
106
|
+
const legacyExists = existsSync(legacyPath);
|
|
90
107
|
return {
|
|
91
108
|
mode: state.mode,
|
|
92
|
-
effectiveMode: state
|
|
109
|
+
effectiveMode: effectiveMode(state, customExists, legacyExists),
|
|
93
110
|
library: {
|
|
94
111
|
path: PIBO_LIBRARY_BASE_PROMPT_PATH,
|
|
95
112
|
markdown: readFileSync(PIBO_LIBRARY_BASE_PROMPT_PATH, "utf-8"),
|
|
@@ -100,5 +117,10 @@ export function setPiboBasePromptMode(mode, cwd = process.cwd()) {
|
|
|
100
117
|
exists: customExists,
|
|
101
118
|
updatedAt: customExists ? state.updatedAt : undefined,
|
|
102
119
|
},
|
|
120
|
+
legacy: {
|
|
121
|
+
path: legacyPath,
|
|
122
|
+
markdown: legacyExists ? readFileSync(legacyPath, "utf-8") : "",
|
|
123
|
+
exists: legacyExists,
|
|
124
|
+
},
|
|
103
125
|
};
|
|
104
126
|
}
|
|
@@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { createDefaultPiboProfile } from "./default-profile.js";
|
|
4
4
|
import { loadPiboModelDefaults, selectRequestedModelProfile, selectRequestedThinkingLevel } from "./model-defaults.js";
|
|
5
|
+
import { redactSensitiveText } from "./sensitive-data-redaction.js";
|
|
5
6
|
import { getMcpAgentContextFile } from "../mcp/agent-context.js";
|
|
6
7
|
import { PIBO_GOAL_TOOL_NAMES } from "../loops/tools.js";
|
|
7
8
|
import { getInstalledCliToolContextFile } from "../tools/registry.js";
|
|
@@ -38,11 +39,6 @@ export function createPiboRuntimeResolutionManifest(input) {
|
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
const SECRET_KEY_RE = /(api[_-]?key|authorization|bearer|cookie|credential|oauth|password|secret|token)/i;
|
|
41
|
-
const SECRET_TEXT_PATTERNS = [
|
|
42
|
-
/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi,
|
|
43
|
-
/\b(sk|pk|rk)-[A-Za-z0-9_-]{16,}\b/g,
|
|
44
|
-
/\b(api[_-]?key|authorization|cookie|password|secret|token)\b\s*[:=]\s*[^\s\n,;]+/gi,
|
|
45
|
-
];
|
|
46
42
|
function byteLength(text) {
|
|
47
43
|
return Buffer.byteLength(text, "utf-8");
|
|
48
44
|
}
|
|
@@ -79,15 +75,7 @@ function applyTokenEstimates(node) {
|
|
|
79
75
|
return node;
|
|
80
76
|
}
|
|
81
77
|
function redactText(text) {
|
|
82
|
-
|
|
83
|
-
for (const pattern of SECRET_TEXT_PATTERNS) {
|
|
84
|
-
value = value.replace(pattern, (match) => {
|
|
85
|
-
const separator = match.match(/[:=]/)?.[0];
|
|
86
|
-
if (!separator)
|
|
87
|
-
return "[REDACTED]";
|
|
88
|
-
return `${match.slice(0, match.indexOf(separator) + 1)} [REDACTED]`;
|
|
89
|
-
});
|
|
90
|
-
}
|
|
78
|
+
const value = redactSensitiveText(text).replaceAll("[redacted]", "[REDACTED]");
|
|
91
79
|
return { value, redacted: value !== text };
|
|
92
80
|
}
|
|
93
81
|
function redactJson(value) {
|
|
@@ -340,7 +328,9 @@ export async function inspectPiboContextBuild(options = {}) {
|
|
|
340
328
|
const generatedAt = new Date().toISOString();
|
|
341
329
|
const resourceLoader = runtime.services.resourceLoader;
|
|
342
330
|
const basePrompt = await readPiboBasePrompt(cwd);
|
|
343
|
-
const activePrompt = basePrompt.effectiveMode === "
|
|
331
|
+
const activePrompt = basePrompt.effectiveMode === "legacy"
|
|
332
|
+
? basePrompt.legacy
|
|
333
|
+
: basePrompt.effectiveMode === "custom" ? basePrompt.custom : basePrompt.library;
|
|
344
334
|
const activeToolNames = new Set(runtime.session.getActiveToolNames());
|
|
345
335
|
const allTools = runtime.session.getAllTools();
|
|
346
336
|
const toolInfoByName = new Map(allTools.map((tool) => [tool.name, tool]));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const SENSITIVE_KEY_PATTERN = /(?:authorization|cookie|credential|access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|bearer|oauth|token|secret|password|passwd)/i;
|
|
2
2
|
const SENSITIVE_TEXT_PATTERNS = [
|
|
3
3
|
[/\bBearer\s+\S+/gi, "Bearer [redacted]"],
|
|
4
|
-
[/\b(?:sk|
|
|
4
|
+
[/\b(?:sk-(?:proj-)?[A-Za-z0-9_-]{16,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{16,}|xox[baprs]-[A-Za-z0-9-]{16,})\b/g, "[redacted]"],
|
|
5
5
|
[/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, "[redacted]"],
|
|
6
|
-
[/\b(
|
|
6
|
+
[/\b([A-Z0-9_]*(?:ACCESS[_-]?TOKEN|REFRESH[_-]?TOKEN|ID[_-]?TOKEN|API[_-]?KEY|AUTHORIZATION|COOKIE|CREDENTIAL|OAUTH|TOKEN|SECRET|PASSWORD|PASSWD)[A-Z0-9_]*)\b(\s*["']?\s*[:=]\s*["']?)([^\s"'&,;}]+)/gi, "$1$2[redacted]"],
|
|
7
7
|
];
|
|
8
8
|
export function redactSensitiveText(value) {
|
|
9
9
|
let selected = value;
|