@pasko70/pibo 1.4.5 → 1.5.0
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/apps/chat/agent-profiles.js +4 -1
- package/dist/apps/chat/agent-store.js +196 -3
- package/dist/apps/chat/chat-settings-routes.js +24 -1
- package/dist/apps/chat/data/project-service.js +13 -3
- package/dist/apps/chat/telemetry-retention-service.js +69 -0
- package/dist/apps/chat/web-app.js +23 -12
- package/dist/apps/chat-ui/assets/{dist-oLAGkW6G.js → dist-B9sopUkn.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BtF63vik.js → dist-BDQhMN_4.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-85Cc5Hut.js → dist-BEStK5um.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D1Laodgo.js → dist-BiBY_4CK.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-eG89IJAV.js → dist-C2OyzisT.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DE8W5WKg.js → dist-C9stINOY.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-ecTM1pdv.js → dist-CQKLsKIo.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-B8jspmzT.js → dist-CtSZyFkJ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DcME5mJj.js → dist-DPLEwPsG.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CQXtvBTs.js → dist-DRTq4wrN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BGqK-7Ep.js → dist-F2W_jRom.js} +1 -1
- package/dist/apps/chat-ui/assets/index-D_60RTKn.css +1 -0
- package/dist/apps/chat-ui/assets/index-iaNLOwJ-.js +157 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/{index-CRUSv6iR.js → index-lA76A7Pc.js} +4 -4
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/inkMarkdown.js +8 -3
- package/dist/apps/cli-ui/inkSyntaxHighlighter.js +166 -0
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/{pibo-vscode-ext-1.4.5.vsix → pibo-vscode-ext-1.5.0.vsix} +0 -0
- package/dist/cli.js +20 -0
- package/dist/core/runtime-telemetry.js +11 -3
- package/dist/core/session-router.js +7 -1
- package/dist/core/telemetry-retention-settings.js +34 -0
- package/dist/core/user-settings.js +11 -0
- package/dist/data/telemetry.js +11 -0
- package/dist/mcp/agent-context.js +10 -6
- package/dist/mcp/commands/info.js +19 -7
- package/dist/mcp/config.js +98 -65
- package/dist/plugins/builtin.js +15 -1
- package/dist/session-ui/terminalRows.js +56 -2
- package/dist/shared/trace-nodes.js +12 -0
- package/dist/skills/cli.js +25 -1
- package/dist/tools/guides.js +71 -0
- package/dist/tools/index.js +7 -3
- package/dist/tools/python-runtime.js +2 -2
- package/dist/tools/registry.js +25 -1
- package/package.json +93 -93
- package/skills/builtin/graphify/SKILL.md +52 -0
- package/dist/apps/chat-ui/assets/index-B-qaya1G.css +0 -1
- package/dist/apps/chat-ui/assets/index-D4uifikB.js +0 -165
|
@@ -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-lA76A7Pc.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-B5QK07zO.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sanitizeTerminalText, tokenizeJsonTextLine } from "./inkJson.js";
|
|
2
|
+
import { highlightInkCodeLine, normalizeInkCodeLanguage } from "./inkSyntaxHighlighter.js";
|
|
2
3
|
export function renderInkMarkdownLines(markdown, options = {}) {
|
|
3
4
|
return renderInkMarkdownTerminalLines(markdown, options).map((line) => line.tokens.map((token) => token.text).join(""));
|
|
4
5
|
}
|
|
@@ -105,14 +106,18 @@ function inlineMarkdownTokens(text, defaults = {}) {
|
|
|
105
106
|
return tokens.filter((token) => token.text.length > 0);
|
|
106
107
|
}
|
|
107
108
|
function codeFenceTokens(sourceLine, language) {
|
|
108
|
-
|
|
109
|
+
const normalizedLanguage = normalizeInkCodeLanguage(language);
|
|
110
|
+
if (isBashLanguage(normalizedLanguage))
|
|
109
111
|
return [{ text: " " }, ...tokenizeInkBashCommand(sourceLine)];
|
|
110
|
-
if (
|
|
112
|
+
if (normalizedLanguage === "json")
|
|
111
113
|
return [{ text: " " }, ...tokenizeJsonTextLine(sourceLine)];
|
|
114
|
+
const highlighted = highlightInkCodeLine(sourceLine, normalizedLanguage);
|
|
115
|
+
if (highlighted)
|
|
116
|
+
return [{ text: " " }, ...highlighted];
|
|
112
117
|
return [{ text: ` ${sourceLine}`, tone: "default" }];
|
|
113
118
|
}
|
|
114
119
|
function isBashLanguage(language) {
|
|
115
|
-
return
|
|
120
|
+
return language === "bash";
|
|
116
121
|
}
|
|
117
122
|
function line(tokens) {
|
|
118
123
|
return { prefix: "none", tokens };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
const languageAliases = new Map([
|
|
4
|
+
["sh", "bash"],
|
|
5
|
+
["shell", "bash"],
|
|
6
|
+
["shellscript", "bash"],
|
|
7
|
+
["zsh", "bash"],
|
|
8
|
+
["js", "javascript"],
|
|
9
|
+
["mjs", "javascript"],
|
|
10
|
+
["cjs", "javascript"],
|
|
11
|
+
["ts", "typescript"],
|
|
12
|
+
["mts", "typescript"],
|
|
13
|
+
["cts", "typescript"],
|
|
14
|
+
["md", "markdown"],
|
|
15
|
+
["yml", "yaml"],
|
|
16
|
+
["jsonc", "json"],
|
|
17
|
+
["py", "python"],
|
|
18
|
+
["rs", "rust"],
|
|
19
|
+
]);
|
|
20
|
+
const grammarComponents = {
|
|
21
|
+
bash: ["bash"],
|
|
22
|
+
css: ["css"],
|
|
23
|
+
go: ["go"],
|
|
24
|
+
html: ["markup"],
|
|
25
|
+
javascript: ["javascript"],
|
|
26
|
+
json: ["json"],
|
|
27
|
+
jsx: ["markup", "javascript", "jsx"],
|
|
28
|
+
markdown: ["markup", "markdown"],
|
|
29
|
+
python: ["python"],
|
|
30
|
+
rust: ["rust"],
|
|
31
|
+
sql: ["sql"],
|
|
32
|
+
tsx: ["markup", "javascript", "jsx", "typescript", "tsx"],
|
|
33
|
+
typescript: ["javascript", "typescript"],
|
|
34
|
+
yaml: ["yaml"],
|
|
35
|
+
};
|
|
36
|
+
const loadedComponents = new Set();
|
|
37
|
+
let prismInstance;
|
|
38
|
+
export function highlightInkCodeLine(sourceLine, language) {
|
|
39
|
+
if (shouldUsePlainCodeTokens())
|
|
40
|
+
return [{ text: sourceLine, tone: "default" }];
|
|
41
|
+
const normalizedLanguage = normalizeInkCodeLanguage(language);
|
|
42
|
+
const prism = loadPrism();
|
|
43
|
+
if (!loadGrammar(prism, normalizedLanguage))
|
|
44
|
+
return undefined;
|
|
45
|
+
const grammar = prism.languages[normalizedLanguage];
|
|
46
|
+
if (!grammar)
|
|
47
|
+
return undefined;
|
|
48
|
+
try {
|
|
49
|
+
return flattenPrismTokens(prism.tokenize(sourceLine, grammar));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function normalizeInkCodeLanguage(language) {
|
|
56
|
+
const normalized = language.trim().toLowerCase();
|
|
57
|
+
return languageAliases.get(normalized) ?? normalized;
|
|
58
|
+
}
|
|
59
|
+
function loadPrism() {
|
|
60
|
+
prismInstance ??= require("prismjs");
|
|
61
|
+
return prismInstance;
|
|
62
|
+
}
|
|
63
|
+
function loadGrammar(prism, language) {
|
|
64
|
+
const components = grammarComponents[language];
|
|
65
|
+
if (!components)
|
|
66
|
+
return false;
|
|
67
|
+
for (const component of components) {
|
|
68
|
+
if (loadedComponents.has(component))
|
|
69
|
+
continue;
|
|
70
|
+
require(`prismjs/components/prism-${component}.js`);
|
|
71
|
+
loadedComponents.add(component);
|
|
72
|
+
}
|
|
73
|
+
return Boolean(prism.languages[language]);
|
|
74
|
+
}
|
|
75
|
+
function flattenPrismTokens(tokens, inheritedTone = "default") {
|
|
76
|
+
const result = [];
|
|
77
|
+
for (const token of tokens) {
|
|
78
|
+
if (typeof token === "string") {
|
|
79
|
+
if (token.length > 0)
|
|
80
|
+
result.push({ text: token, tone: inheritedTone });
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const tone = toneForPrismToken(token) ?? inheritedTone;
|
|
84
|
+
appendPrismContent(result, token.content, tone);
|
|
85
|
+
}
|
|
86
|
+
return mergeAdjacentTokens(result);
|
|
87
|
+
}
|
|
88
|
+
function appendPrismContent(result, content, tone) {
|
|
89
|
+
if (typeof content === "string") {
|
|
90
|
+
if (content.length > 0)
|
|
91
|
+
result.push({ text: content, tone });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(content)) {
|
|
95
|
+
result.push(...flattenPrismTokens(content, tone));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const nestedTone = toneForPrismToken(content) ?? tone;
|
|
99
|
+
appendPrismContent(result, content.content, nestedTone);
|
|
100
|
+
}
|
|
101
|
+
function toneForPrismToken(token) {
|
|
102
|
+
const classes = [token.type, ...aliasesForToken(token)];
|
|
103
|
+
for (const className of classes) {
|
|
104
|
+
switch (className) {
|
|
105
|
+
case "comment":
|
|
106
|
+
case "prolog":
|
|
107
|
+
case "doctype":
|
|
108
|
+
case "cdata":
|
|
109
|
+
return "dim";
|
|
110
|
+
case "string":
|
|
111
|
+
case "char":
|
|
112
|
+
case "attr-value":
|
|
113
|
+
case "url":
|
|
114
|
+
return "green";
|
|
115
|
+
case "number":
|
|
116
|
+
case "boolean":
|
|
117
|
+
case "constant":
|
|
118
|
+
return "blue";
|
|
119
|
+
case "keyword":
|
|
120
|
+
case "operator":
|
|
121
|
+
case "punctuation":
|
|
122
|
+
case "important":
|
|
123
|
+
case "atrule":
|
|
124
|
+
return "magenta";
|
|
125
|
+
case "function":
|
|
126
|
+
case "method":
|
|
127
|
+
case "selector":
|
|
128
|
+
case "class-name":
|
|
129
|
+
return "yellow";
|
|
130
|
+
case "tag":
|
|
131
|
+
case "property":
|
|
132
|
+
case "attr-name":
|
|
133
|
+
case "variable":
|
|
134
|
+
case "regex":
|
|
135
|
+
return "cyan";
|
|
136
|
+
case "builtin":
|
|
137
|
+
case "symbol":
|
|
138
|
+
case "deleted":
|
|
139
|
+
return "red";
|
|
140
|
+
default:
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
function aliasesForToken(token) {
|
|
147
|
+
if (!token.alias)
|
|
148
|
+
return [];
|
|
149
|
+
return Array.isArray(token.alias) ? token.alias : [token.alias];
|
|
150
|
+
}
|
|
151
|
+
function mergeAdjacentTokens(tokens) {
|
|
152
|
+
const merged = [];
|
|
153
|
+
for (const token of tokens) {
|
|
154
|
+
const previous = merged[merged.length - 1];
|
|
155
|
+
if (previous && previous.tone === token.tone && previous.weight === token.weight && previous.italic === token.italic) {
|
|
156
|
+
previous.text += token.text;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
merged.push({ ...token });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return merged;
|
|
163
|
+
}
|
|
164
|
+
function shouldUsePlainCodeTokens() {
|
|
165
|
+
return Boolean(process.env.NO_COLOR) || process.env.TERM === "dumb" || process.env.PIBO_ASCII_PROGRESS === "1";
|
|
166
|
+
}
|
|
Binary file
|
package/dist/apps/vscode-artifacts/{pibo-vscode-ext-1.4.5.vsix → pibo-vscode-ext-1.5.0.vsix}
RENAMED
|
Binary file
|
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import { PIBO_CONFIG_KEYS, getDefaultPiboConfigPath, deletePiboConfigValue, getDisplayPiboConfigValue, loadPiboConfig, redactPiboConfig, savePiboConfig, setPiboConfigValue, } from "./config/config.js";
|
|
3
4
|
import { parsePiboThinkingLevel } from "./core/thinking.js";
|
|
@@ -20,6 +21,16 @@ function printConfigKeys() {
|
|
|
20
21
|
function printRootDiscovery() {
|
|
21
22
|
console.log(printRootDiscoveryText());
|
|
22
23
|
}
|
|
24
|
+
function getPiboVersion() {
|
|
25
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
26
|
+
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
27
|
+
throw new Error("Unable to read Pibo package version");
|
|
28
|
+
}
|
|
29
|
+
return packageJson.version;
|
|
30
|
+
}
|
|
31
|
+
function printPiboVersion() {
|
|
32
|
+
console.log(getPiboVersion());
|
|
33
|
+
}
|
|
23
34
|
function printConfigDiscovery() {
|
|
24
35
|
console.log(printConfigDiscoveryText());
|
|
25
36
|
}
|
|
@@ -46,6 +57,10 @@ export async function runPiboCli(argv = process.argv) {
|
|
|
46
57
|
printRootDiscovery();
|
|
47
58
|
return;
|
|
48
59
|
}
|
|
60
|
+
if (argv[2] === "--version" || argv[2] === "-V") {
|
|
61
|
+
printPiboVersion();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
49
64
|
if (argv[2] === "mcp") {
|
|
50
65
|
const { runMcpCli } = await import("./mcp/index.js");
|
|
51
66
|
await runMcpCli([argv[0] ?? "node", "pibo mcp", ...argv.slice(3)]);
|
|
@@ -348,6 +363,7 @@ export async function runPiboCli(argv = process.argv) {
|
|
|
348
363
|
.option("--auth <mode>", "Auth service mode: 'better-auth' (default) or 'local' (loopback-only, no Google OAuth)")
|
|
349
364
|
.option("--web-host <host>", "Bind the HTTP web host, for example 0.0.0.0 for LAN access")
|
|
350
365
|
.option("--web-port <port>", "Bind the HTTP web host port", parsePort)
|
|
366
|
+
.option("--gateway-port <port>", "Bind the agent-runtime gateway port", parsePort)
|
|
351
367
|
.action(async (options) => {
|
|
352
368
|
const { runWebGatewayServer } = await import("./gateway/web.js");
|
|
353
369
|
const authMode = options.auth;
|
|
@@ -360,6 +376,7 @@ export async function runPiboCli(argv = process.argv) {
|
|
|
360
376
|
}
|
|
361
377
|
await runWebGatewayServer({
|
|
362
378
|
authMode: authMode,
|
|
379
|
+
port: options.gatewayPort,
|
|
363
380
|
web: {
|
|
364
381
|
host: options.webHost,
|
|
365
382
|
port: options.webPort,
|
|
@@ -403,6 +420,9 @@ Commands:
|
|
|
403
420
|
gateway Inspect and restart host gateways through safe CLI commands
|
|
404
421
|
gateway:web Start a web gateway runtime (use --auth=local for loopback-only local auth)
|
|
405
422
|
|
|
423
|
+
Options:
|
|
424
|
+
--version Print the Pibo CLI version
|
|
425
|
+
|
|
406
426
|
Next:
|
|
407
427
|
pibo <command> --help
|
|
408
428
|
`;
|
|
@@ -5,10 +5,12 @@ export class PiboRuntimeTelemetryRecorder {
|
|
|
5
5
|
store;
|
|
6
6
|
onError;
|
|
7
7
|
telemetry;
|
|
8
|
-
|
|
8
|
+
providerEventMode;
|
|
9
|
+
constructor(store, onError, options = {}) {
|
|
9
10
|
this.store = store;
|
|
10
11
|
this.onError = onError;
|
|
11
12
|
this.telemetry = new BestEffortTelemetryService(store, onError);
|
|
13
|
+
this.providerEventMode = options.providerEventMode ?? "aggregate";
|
|
12
14
|
}
|
|
13
15
|
recordOutput(event, context = {}) {
|
|
14
16
|
if (!this.store)
|
|
@@ -91,7 +93,7 @@ export class PiboRuntimeTelemetryRecorder {
|
|
|
91
93
|
if (summary.upstreamResponseId && summary.upstreamResponseId !== providerRequest.upstreamResponseId) {
|
|
92
94
|
this.upsertProviderRequestFromExisting(providerRequest, { upstreamResponseId: summary.upstreamResponseId });
|
|
93
95
|
}
|
|
94
|
-
|
|
96
|
+
const providerEventInput = {
|
|
95
97
|
providerRequestId: providerRequest.providerRequestId,
|
|
96
98
|
piboSessionId: turn.piboSessionId,
|
|
97
99
|
turnId: turn.turnId,
|
|
@@ -106,7 +108,13 @@ export class PiboRuntimeTelemetryRecorder {
|
|
|
106
108
|
itemId: summary.itemId,
|
|
107
109
|
toolCallId: summary.toolCallId,
|
|
108
110
|
safeFields: summary.safeFields,
|
|
109
|
-
}
|
|
111
|
+
};
|
|
112
|
+
if (this.providerEventMode === "detailed") {
|
|
113
|
+
this.telemetry.appendProviderEventSummary(providerEventInput);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.telemetry.recordProviderEventSummary(providerEventInput);
|
|
117
|
+
}
|
|
110
118
|
if (summary.toolCallId && summary.assistantEventType?.startsWith("toolcall_")) {
|
|
111
119
|
this.recordPiToolCallProgress(turn, providerRequest.providerRequestId, summary, now);
|
|
112
120
|
}
|
|
@@ -110,6 +110,10 @@ function piboRoomIdFromMetadata(metadata) {
|
|
|
110
110
|
function telemetryStoreFromSessionStore(store) {
|
|
111
111
|
return store.getTelemetryStore?.();
|
|
112
112
|
}
|
|
113
|
+
function providerEventTelemetryModeFromEnv(env = process.env) {
|
|
114
|
+
const value = env.PIBO_TELEMETRY_PROVIDER_EVENTS?.trim().toLowerCase();
|
|
115
|
+
return value === "1" || value === "true" || value === "detailed" ? "detailed" : "aggregate";
|
|
116
|
+
}
|
|
113
117
|
export class PiboSessionRouter {
|
|
114
118
|
options;
|
|
115
119
|
sessions = new Map();
|
|
@@ -130,7 +134,9 @@ export class PiboSessionRouter {
|
|
|
130
134
|
this.pluginRegistry = options.pluginRegistry ?? createDefaultPiboPluginRegistry();
|
|
131
135
|
this.sessionStore = options.sessionStore ?? new InMemoryPiboSessionStore();
|
|
132
136
|
this.telemetryStore = options.telemetryStore ?? telemetryStoreFromSessionStore(this.sessionStore);
|
|
133
|
-
this.telemetryRecorder = this.telemetryStore
|
|
137
|
+
this.telemetryRecorder = this.telemetryStore
|
|
138
|
+
? new PiboRuntimeTelemetryRecorder(this.telemetryStore, undefined, { providerEventMode: providerEventTelemetryModeFromEnv() })
|
|
139
|
+
: undefined;
|
|
134
140
|
const defaultProfileName = selectDefaultPiboProfileName(this.pluginRegistry);
|
|
135
141
|
this.baseProfile = options.profile ?? createPiboProfileFromRegistryOrDefault(this.pluginRegistry, defaultProfileName);
|
|
136
142
|
this.reliabilityStore = options.reliabilityStore ?? (options.persistSession === false ? undefined : createDefaultPiboReliabilityStore());
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const DEFAULT_TELEMETRY_RETENTION_DAYS = 30;
|
|
2
|
+
export const MIN_TELEMETRY_RETENTION_DAYS = 1;
|
|
3
|
+
export const MAX_TELEMETRY_RETENTION_DAYS = 365;
|
|
4
|
+
export function sanitizeTelemetryRetentionSettings(value) {
|
|
5
|
+
const raw = value && typeof value === "object" && !Array.isArray(value)
|
|
6
|
+
? value
|
|
7
|
+
: {};
|
|
8
|
+
const lastPrunedAt = sanitizeTelemetryRetentionTimestamp(raw.lastPrunedAt);
|
|
9
|
+
return {
|
|
10
|
+
enabled: typeof raw.enabled === "boolean" ? raw.enabled : true,
|
|
11
|
+
days: sanitizeTelemetryRetentionDays(raw.days) ?? DEFAULT_TELEMETRY_RETENTION_DAYS,
|
|
12
|
+
...(lastPrunedAt ? { lastPrunedAt } : {}),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function sanitizeTelemetryRetentionDays(value) {
|
|
16
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
17
|
+
return undefined;
|
|
18
|
+
const days = Math.trunc(value);
|
|
19
|
+
if (days < MIN_TELEMETRY_RETENTION_DAYS || days > MAX_TELEMETRY_RETENTION_DAYS)
|
|
20
|
+
return undefined;
|
|
21
|
+
return days;
|
|
22
|
+
}
|
|
23
|
+
export function sanitizeTelemetryRetentionTimestamp(value) {
|
|
24
|
+
if (typeof value !== "string")
|
|
25
|
+
return undefined;
|
|
26
|
+
const timestamp = value.trim();
|
|
27
|
+
if (!timestamp)
|
|
28
|
+
return undefined;
|
|
29
|
+
const ms = Date.parse(timestamp);
|
|
30
|
+
return Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;
|
|
31
|
+
}
|
|
32
|
+
export function telemetryRetentionCutoff(days, now = new Date()) {
|
|
33
|
+
return new Date(now.getTime() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
34
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { piboHomePath } from "./pibo-home.js";
|
|
4
|
+
import { sanitizeTelemetryRetentionSettings } from "./telemetry-retention-settings.js";
|
|
4
5
|
import { sanitizeTelemetryStaleThresholdSettings } from "./telemetry-staleness.js";
|
|
5
6
|
export const DEFAULT_USER_TIMEZONE = "UTC";
|
|
6
7
|
export const DEFAULT_WEB_ANNOTATIONS_TOGGLE_SHORTCUT = "Alt+Shift+A";
|
|
@@ -15,6 +16,15 @@ export function updatePiboUserSettings(patch) {
|
|
|
15
16
|
writeState(state);
|
|
16
17
|
return next;
|
|
17
18
|
}
|
|
19
|
+
export function updateTelemetryRetentionLastPrunedAt(lastPrunedAt) {
|
|
20
|
+
const current = loadPiboUserSettings();
|
|
21
|
+
return updatePiboUserSettings({
|
|
22
|
+
telemetryRetention: {
|
|
23
|
+
...current.telemetryRetention,
|
|
24
|
+
lastPrunedAt,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
18
28
|
export function sanitizeShortcutSettings(value) {
|
|
19
29
|
const raw = value && typeof value === "object" && !Array.isArray(value)
|
|
20
30
|
? value
|
|
@@ -49,6 +59,7 @@ function sanitizeUserSettings(value) {
|
|
|
49
59
|
timezone: sanitizeTimezone(raw.timezone) ?? DEFAULT_USER_TIMEZONE,
|
|
50
60
|
shortcuts: sanitizeShortcutSettings(raw.shortcuts),
|
|
51
61
|
telemetryStaleThresholds: sanitizeTelemetryStaleThresholdSettings(raw.telemetryStaleThresholds),
|
|
62
|
+
telemetryRetention: sanitizeTelemetryRetentionSettings(raw.telemetryRetention),
|
|
52
63
|
};
|
|
53
64
|
}
|
|
54
65
|
function readState() {
|
package/dist/data/telemetry.js
CHANGED
|
@@ -168,6 +168,14 @@ export class TelemetryStore {
|
|
|
168
168
|
`).run(providerRequestId, input.piboSessionId, input.rootSessionId ?? null, input.roomId ?? null, input.turnId, input.phaseId ?? null, input.provider, input.api, input.model, input.transport ?? existing?.transport ?? "unknown", input.serviceTier ?? null, input.status ?? existing?.status ?? "started", startedAt, input.responseHeadersAt ?? null, input.firstByteAt ?? null, input.lastRawEventAt ?? null, input.lastNormalizedEventAt ?? null, input.completedAt ?? null, input.httpStatus ?? null, input.upstreamResponseId ?? null, input.rawEventCount ?? existing?.rawEventCount ?? 0, input.normalizedEventCount ?? existing?.normalizedEventCount ?? 0, input.parseErrorCount ?? existing?.parseErrorCount ?? 0, input.unknownEventCount ?? existing?.unknownEventCount ?? 0, input.bytesReceived ?? existing?.bytesReceived ?? null, JSON.stringify(input.eventTypeCounts ?? existing?.eventTypeCounts ?? {}), input.eventStreamId ?? null, input.eventId ?? null, input.payloadRef ?? null, input.errorCategory ?? null, input.errorMessage ?? null, input.captureMode ?? existing?.captureMode ?? "metadata_only", input.retentionClass ?? existing?.retentionClass ?? "diagnostic", input.createdAt ?? existing?.createdAt ?? now, now);
|
|
169
169
|
return this.getProviderRequest(providerRequestId) ?? fail(`Failed to upsert telemetry provider request ${providerRequestId}`);
|
|
170
170
|
}
|
|
171
|
+
recordProviderEventSummary(input) {
|
|
172
|
+
const now = input.updatedAt ?? new Date().toISOString();
|
|
173
|
+
const receivedAt = input.receivedAt ?? now;
|
|
174
|
+
const byteSize = input.byteSize ?? 0;
|
|
175
|
+
const parseStatus = input.parseStatus ?? "ok";
|
|
176
|
+
const normalizedDelta = input.normalizedEventDelta ?? (input.normalizedType ? 1 : 0);
|
|
177
|
+
this.incrementProviderCounters(input.providerRequestId, input.eventType, receivedAt, byteSize, parseStatus, normalizedDelta);
|
|
178
|
+
}
|
|
171
179
|
appendProviderEventSummary(input) {
|
|
172
180
|
const now = input.updatedAt ?? new Date().toISOString();
|
|
173
181
|
const receivedAt = input.receivedAt ?? now;
|
|
@@ -336,6 +344,9 @@ export class BestEffortTelemetryService {
|
|
|
336
344
|
upsertProviderRequest(input) {
|
|
337
345
|
return this.safe(() => this.store?.upsertProviderRequest(input));
|
|
338
346
|
}
|
|
347
|
+
recordProviderEventSummary(input) {
|
|
348
|
+
this.safe(() => this.store?.recordProviderEventSummary(input));
|
|
349
|
+
}
|
|
339
350
|
appendProviderEventSummary(input) {
|
|
340
351
|
return this.safe(() => this.store?.appendProviderEventSummary(input));
|
|
341
352
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { ensureConfigExists,
|
|
2
|
+
import { ensureConfigExists, isHttpServer, loadConfig, } from './config.js';
|
|
3
3
|
import { ErrorCode, formatCliError } from './errors.js';
|
|
4
4
|
export const MCP_SERVER_DESCRIPTION_MAX_LENGTH = 480;
|
|
5
5
|
export const ENABLED_MCP_SERVERS_CONTEXT_PATH = '.pibo/context/enabled-mcp-servers.md';
|
|
@@ -22,11 +22,15 @@ export function normalizeMcpServerDescription(value) {
|
|
|
22
22
|
return description;
|
|
23
23
|
}
|
|
24
24
|
export async function listMcpServerInfos(configPath) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return [];
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
try {
|
|
26
|
+
const config = await loadConfig(configPath);
|
|
27
|
+
return Object.entries(config.mcpServers).map(([name, server]) => mcpServerInfoFromConfig(name, server));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error.message.includes('CONFIG_NOT_FOUND'))
|
|
31
|
+
return [];
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
export async function setMcpServerDescription(serverName, descriptionInput, configPath) {
|
|
32
36
|
const description = normalizeMcpServerDescription(descriptionInput);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Info command - Show server or tool details
|
|
3
3
|
*/
|
|
4
4
|
import { getConnection, safeClose } from '../client.js';
|
|
5
|
-
import {
|
|
5
|
+
import { formatConfigSourceSummaries, getConfigSourceSummaries, loadConfig, } from '../config.js';
|
|
6
6
|
import { ErrorCode, formatCliError, serverConnectionError, toolNotFoundError, } from '../errors.js';
|
|
7
7
|
import { formatServerDetails, formatToolSchema } from '../output.js';
|
|
8
8
|
/**
|
|
@@ -28,12 +28,24 @@ export async function infoCommand(options) {
|
|
|
28
28
|
process.exit(ErrorCode.CLIENT_ERROR);
|
|
29
29
|
}
|
|
30
30
|
const { server: serverName, tool: toolName } = parseTarget(options.target);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
console.error(
|
|
31
|
+
const serverConfig = config.mcpServers[serverName];
|
|
32
|
+
if (!serverConfig) {
|
|
33
|
+
const available = Object.keys(config.mcpServers);
|
|
34
|
+
const serverList = available.length > 0 ? available.join(', ') : '(none)';
|
|
35
|
+
const summaries = await getConfigSourceSummaries(options.configPath);
|
|
36
|
+
console.error(formatCliError({
|
|
37
|
+
code: ErrorCode.CLIENT_ERROR,
|
|
38
|
+
type: 'SERVER_NOT_FOUND',
|
|
39
|
+
message: `Server "${serverName}" not found in config`,
|
|
40
|
+
details: [
|
|
41
|
+
`Merged available servers: ${serverList}`,
|
|
42
|
+
'Config search paths:',
|
|
43
|
+
formatConfigSourceSummaries(summaries),
|
|
44
|
+
].join('\n'),
|
|
45
|
+
suggestion: available.length > 0
|
|
46
|
+
? `Use one of: ${available.map((s) => `pibo mcp info ${s}`).join(', ')}`
|
|
47
|
+
: `Add server to mcp_servers.json: { "mcpServers": { "${serverName}": { ... } } }`,
|
|
48
|
+
}));
|
|
37
49
|
process.exit(ErrorCode.CLIENT_ERROR);
|
|
38
50
|
}
|
|
39
51
|
let connection;
|