@oh-my-pi/pi-coding-agent 15.9.67 → 15.10.1
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 +136 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/gallery-cli.d.ts +43 -0
- package/dist/types/cli/gallery-fixtures/agentic.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/codeintel.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/edit.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/fs.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/index.d.ts +4 -0
- package/dist/types/cli/gallery-fixtures/interaction.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/memory.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/misc.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/shell.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +44 -0
- package/dist/types/cli/gallery-fixtures/web.d.ts +2 -0
- package/dist/types/cli/gallery-screenshot.d.ts +35 -0
- package/dist/types/commands/gallery.d.ts +47 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-id-affixes.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +25 -2
- package/dist/types/config/settings-schema.d.ts +41 -6
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/extensibility/plugins/marketplace-auto-update.d.ts +8 -0
- package/dist/types/lsp/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memory-backend/index.d.ts +2 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +1 -1
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +18 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/index.d.ts +5 -4
- package/dist/types/modes/interactive-mode.d.ts +16 -6
- package/dist/types/modes/setup-version.d.ts +11 -0
- package/dist/types/modes/setup-wizard/index.d.ts +2 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +19 -6
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +14 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/telemetry-export.d.ts +1 -1
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +1 -8
- package/dist/types/tools/fetch.d.ts +15 -7
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +13 -9
- package/dist/types/tools/renderers.d.ts +16 -2
- package/dist/types/tools/search.d.ts +5 -1
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +5 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/scrapers/github.d.ts +22 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/dist/types/web/search/providers/perplexity.d.ts +8 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +9 -9
- package/scripts/dev-launch +42 -0
- package/scripts/dev-launch-preload.ts +19 -0
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +226 -0
- package/src/cli/gallery-fixtures/agentic.ts +292 -0
- package/src/cli/gallery-fixtures/codeintel.ts +188 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +153 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +41 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/launch.ts +1 -1
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/model-equivalence.ts +35 -12
- package/src/config/model-id-affixes.ts +39 -22
- package/src/config/model-registry.ts +41 -18
- package/src/config/settings-schema.ts +28 -5
- package/src/config/settings.ts +31 -2
- package/src/dap/client.ts +14 -16
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +111 -119
- package/src/eval/__tests__/agent-bridge.test.ts +75 -32
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/agent-bridge.ts +34 -7
- package/src/eval/llm-bridge.ts +8 -3
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/plugins/doctor.ts +0 -1
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/goals/tools/goal-tool.ts +37 -27
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/client.ts +104 -55
- package/src/lsp/types.ts +10 -0
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +53 -56
- package/src/memories/index.ts +12 -5
- package/src/memory-backend/index.ts +13 -1
- package/src/memory-backend/resolve.ts +3 -5
- package/src/memory-backend/types.ts +1 -1
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +33 -1
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +115 -90
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +70 -57
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +135 -122
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +25 -27
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/index.ts +5 -4
- package/src/modes/interactive-mode.ts +171 -82
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +3 -2
- package/src/modes/setup-wizard/scenes/web-search.ts +3 -2
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +19 -8
- package/src/modes/utils/context-usage.ts +10 -6
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +32 -60
- package/src/session/agent-session.ts +89 -13
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +37 -10
- package/src/slash-commands/helpers/usage-report.ts +2 -0
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +25 -4
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -148
- package/src/telemetry-export.ts +25 -7
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval-backends.ts +6 -17
- package/src/tools/eval-render.ts +21 -18
- package/src/tools/eval.ts +5 -4
- package/src/tools/fetch.ts +391 -91
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +38 -40
- package/src/tools/renderers.ts +16 -1
- package/src/tools/report-tool-issue.ts +1 -1
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +189 -95
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +138 -59
- package/src/tools/write.ts +100 -60
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/scrapers/github.ts +255 -3
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/perplexity.ts +199 -51
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
- package/src/web/search/render.ts +39 -54
- package/src/web/search/types.ts +5 -1
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +0 -1
- package/src/eval/__tests__/shared-executors.test.ts +0 -609
package/src/task/render.ts
CHANGED
|
@@ -6,19 +6,20 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
9
|
-
import { Container, Text } from "@oh-my-pi/pi-tui";
|
|
9
|
+
import { Container, Markdown, Text } from "@oh-my-pi/pi-tui";
|
|
10
10
|
import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
11
11
|
import { settings } from "../config/settings";
|
|
12
12
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
13
13
|
import { formatContextUsage } from "../modes/components/status-line/context-thresholds";
|
|
14
|
-
import
|
|
14
|
+
import { shimmerEnabled, shimmerText } from "../modes/theme/shimmer";
|
|
15
|
+
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
15
16
|
import {
|
|
16
|
-
capPreviewLines,
|
|
17
17
|
formatBadge,
|
|
18
18
|
formatDuration,
|
|
19
19
|
formatMoreItems,
|
|
20
20
|
formatStatusIcon,
|
|
21
21
|
replaceTabs,
|
|
22
|
+
type ToolUIStatus,
|
|
22
23
|
truncateToWidth,
|
|
23
24
|
} from "../tools/render-utils";
|
|
24
25
|
import {
|
|
@@ -29,7 +30,8 @@ import {
|
|
|
29
30
|
type ReportFindingDetails,
|
|
30
31
|
type SubmitReviewDetails,
|
|
31
32
|
} from "../tools/review";
|
|
32
|
-
import {
|
|
33
|
+
import { framedBlock, renderStatusLine } from "../tui";
|
|
34
|
+
import { repairDoubleEncodedJsonString } from "./repair-args";
|
|
33
35
|
import { subprocessToolRegistry } from "./subprocess-tool-registry";
|
|
34
36
|
import type { AgentProgress, SingleResult, TaskItem, TaskParams, TaskToolDetails } from "./types";
|
|
35
37
|
|
|
@@ -507,28 +509,20 @@ function formatOutputInline(data: unknown, theme: Theme, maxWidth = 80): string
|
|
|
507
509
|
* preview. The args stream in token by token, so the array grows over time and
|
|
508
510
|
* trailing entries may be partially parsed — every field access is defensive.
|
|
509
511
|
*/
|
|
510
|
-
function renderTaskItemLines(
|
|
511
|
-
tasks: TaskItem[] | undefined,
|
|
512
|
-
contPrefix: string,
|
|
513
|
-
expanded: boolean,
|
|
514
|
-
theme: Theme,
|
|
515
|
-
): string[] {
|
|
512
|
+
function renderTaskItemLines(tasks: TaskItem[] | undefined, expanded: boolean, theme: Theme): string[] {
|
|
516
513
|
const items = tasks ?? [];
|
|
517
514
|
if (items.length === 0) return [];
|
|
518
515
|
|
|
519
|
-
const
|
|
520
|
-
const last = theme.fg("dim", theme.tree.last);
|
|
516
|
+
const bullet = theme.fg("dim", "•");
|
|
521
517
|
const cap = expanded ? items.length : Math.min(items.length, 12);
|
|
522
518
|
const truncated = cap < items.length;
|
|
523
519
|
|
|
524
520
|
const lines: string[] = [];
|
|
525
521
|
for (let i = 0; i < cap; i++) {
|
|
526
522
|
const task = items[i] as Partial<TaskItem> | undefined;
|
|
527
|
-
const isLastLine = !truncated && i === items.length - 1;
|
|
528
|
-
const connector = isLastLine ? last : branch;
|
|
529
523
|
const rawId = task?.id?.trim();
|
|
530
524
|
const idLabel = rawId ? formatTaskId(rawId) : `#${i + 1}`;
|
|
531
|
-
let line = `${
|
|
525
|
+
let line = `${bullet} ${theme.fg("accent", theme.bold(idLabel))}`;
|
|
532
526
|
const desc = task?.description?.trim();
|
|
533
527
|
if (desc) {
|
|
534
528
|
line += `: ${theme.fg("muted", truncateToWidth(replaceTabs(desc), 64))}`;
|
|
@@ -536,11 +530,42 @@ function renderTaskItemLines(
|
|
|
536
530
|
lines.push(line);
|
|
537
531
|
}
|
|
538
532
|
if (truncated) {
|
|
539
|
-
lines.push(`${
|
|
533
|
+
lines.push(`${bullet} ${theme.fg("dim", formatMoreItems(items.length - cap, "agent"))}`);
|
|
540
534
|
}
|
|
541
535
|
return lines;
|
|
542
536
|
}
|
|
543
537
|
|
|
538
|
+
/**
|
|
539
|
+
* Build the shared-context section (the `# Goal / # Constraints` background
|
|
540
|
+
* passed to every subagent). Rendered in both the streaming call preview and
|
|
541
|
+
* the merged result frame so the brief stays visible for the whole task
|
|
542
|
+
* lifecycle — not just until the first progress snapshot replaces the call view.
|
|
543
|
+
*/
|
|
544
|
+
type TaskRenderSection = { lines: string[] };
|
|
545
|
+
type ContextSectionRenderer = (width: number) => TaskRenderSection;
|
|
546
|
+
|
|
547
|
+
// Default output-block layout is: left border + one-cell content inset + right
|
|
548
|
+
// border. Render markdown at that inner width so the output block does not need
|
|
549
|
+
// to rewrap already-rendered context lines.
|
|
550
|
+
const CONTEXT_FRAME_INSET = 3;
|
|
551
|
+
|
|
552
|
+
function contextMarkdownWidth(frameWidth: number): number {
|
|
553
|
+
return Math.max(1, frameWidth - CONTEXT_FRAME_INSET);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function createContextSectionRenderer(args: TaskParams | undefined, theme: Theme): ContextSectionRenderer | undefined {
|
|
557
|
+
// `renderResult` receives the raw tool args (unlike `renderCall`, which is
|
|
558
|
+
// fed through `repairTaskParams`), so undo any per-field double-encoding here
|
|
559
|
+
// too. The repair is idempotent on already-clean text.
|
|
560
|
+
const context = repairDoubleEncodedJsonString(args?.context ?? "").trim();
|
|
561
|
+
if (!context) return undefined;
|
|
562
|
+
|
|
563
|
+
const markdown = new Markdown(context, 0, 0, getMarkdownTheme(), {
|
|
564
|
+
color: text => theme.fg("muted", text),
|
|
565
|
+
});
|
|
566
|
+
return width => ({ lines: markdown.render(contextMarkdownWidth(width)) });
|
|
567
|
+
}
|
|
568
|
+
|
|
544
569
|
/**
|
|
545
570
|
* Render the tool call arguments.
|
|
546
571
|
*/
|
|
@@ -549,44 +574,34 @@ export function renderCall(
|
|
|
549
574
|
options: RenderResultOptions & { renderContext?: { hasResult?: boolean } },
|
|
550
575
|
theme: Theme,
|
|
551
576
|
): Component {
|
|
552
|
-
const lines: string[] = [];
|
|
553
|
-
lines.push(renderStatusLine({ icon: "pending", title: "Task", description: args.agent }, theme));
|
|
554
|
-
|
|
555
|
-
const context = (args.context ?? "").trim();
|
|
556
|
-
const hasContext = context.length > 0;
|
|
557
|
-
const branch = theme.fg("dim", theme.tree.branch);
|
|
558
|
-
const last = theme.fg("dim", theme.tree.last);
|
|
559
|
-
const vertical = theme.fg("dim", theme.tree.vertical);
|
|
560
577
|
const showIsolated = "isolated" in args && args.isolated === true;
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
lines
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
// the call args stream in. Once a result snapshot exists, `renderResult`
|
|
579
|
-
// draws the same agents as progress/result lines (id + description), so
|
|
580
|
-
// emitting the preview here would render every task twice.
|
|
581
|
-
if (!options.renderContext?.hasResult) {
|
|
582
|
-
lines.push(...renderTaskItemLines(args.tasks, tasksContPrefix, options.expanded, theme));
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
if (showIsolated) {
|
|
586
|
-
lines.push(` ${last} ${theme.fg("dim", "Isolated")}: ${theme.fg("muted", "true")}`);
|
|
587
|
-
}
|
|
578
|
+
const header = renderStatusLine({ icon: "pending", title: "Task", description: args.agent }, theme);
|
|
579
|
+
const contextSectionRenderer = createContextSectionRenderer(args, theme);
|
|
580
|
+
return framedBlock(theme, width => {
|
|
581
|
+
const sections: Array<{ label?: string; lines: string[]; separator?: boolean }> = [];
|
|
582
|
+
|
|
583
|
+
if (contextSectionRenderer) sections.push(contextSectionRenderer(width));
|
|
584
|
+
|
|
585
|
+
// The per-task preview list only exists to surface dispatched agents while
|
|
586
|
+
// the call args stream in. Once a result snapshot exists, `renderResult`
|
|
587
|
+
// draws the same agents as progress/result lines, so showing the Tasks
|
|
588
|
+
// section here would just repeat the count the result frame already shows.
|
|
589
|
+
if (!options.renderContext?.hasResult) {
|
|
590
|
+
sections.push({
|
|
591
|
+
separator: true,
|
|
592
|
+
lines: renderTaskItemLines(args.tasks, options.expanded, theme),
|
|
593
|
+
});
|
|
594
|
+
}
|
|
588
595
|
|
|
589
|
-
|
|
596
|
+
return {
|
|
597
|
+
header,
|
|
598
|
+
headerMeta: showIsolated ? "isolated" : undefined,
|
|
599
|
+
sections,
|
|
600
|
+
state: "pending",
|
|
601
|
+
borderColor: "borderMuted",
|
|
602
|
+
width,
|
|
603
|
+
};
|
|
604
|
+
});
|
|
590
605
|
}
|
|
591
606
|
|
|
592
607
|
/**
|
|
@@ -594,14 +609,13 @@ export function renderCall(
|
|
|
594
609
|
*/
|
|
595
610
|
function renderAgentProgress(
|
|
596
611
|
progress: AgentProgress,
|
|
597
|
-
|
|
612
|
+
prefix: string,
|
|
613
|
+
continuePrefix: string,
|
|
598
614
|
expanded: boolean,
|
|
599
615
|
theme: Theme,
|
|
600
616
|
spinnerFrame?: number,
|
|
601
617
|
): string[] {
|
|
602
618
|
const lines: string[] = [];
|
|
603
|
-
const prefix = isLast ? theme.fg("dim", theme.tree.last) : theme.fg("dim", theme.tree.branch);
|
|
604
|
-
const continuePrefix = isLast ? " " : `${theme.fg("dim", theme.tree.vertical)} `;
|
|
605
619
|
|
|
606
620
|
const icon = getStatusIcon(progress.status, theme, spinnerFrame);
|
|
607
621
|
const iconColor =
|
|
@@ -615,11 +629,24 @@ function renderAgentProgress(
|
|
|
615
629
|
const description = progress.description?.trim();
|
|
616
630
|
const displayId = formatTaskId(progress.id);
|
|
617
631
|
const titlePart = description ? `${theme.bold(displayId)}: ${description}` : displayId;
|
|
618
|
-
|
|
632
|
+
const indent = prefix ? `${prefix} ` : "";
|
|
633
|
+
let statusLine: string;
|
|
634
|
+
if (progress.status === "running") {
|
|
635
|
+
const bullet = theme.fg("accent", "•");
|
|
636
|
+
const name = shimmerEnabled()
|
|
637
|
+
? shimmerText(displayId, theme)
|
|
638
|
+
: theme.fg("accent", description ? theme.bold(displayId) : displayId);
|
|
639
|
+
statusLine = `${indent}${bullet} ${name}`;
|
|
640
|
+
if (description) {
|
|
641
|
+
statusLine += theme.fg("accent", `: ${description}`);
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
statusLine = `${indent}${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)}`;
|
|
645
|
+
}
|
|
619
646
|
|
|
620
647
|
// Show retry-blocked badge so the parent immediately sees that a child
|
|
621
648
|
// is sleeping on a provider 429, not silently progressing. Wins over the
|
|
622
|
-
// generic running
|
|
649
|
+
// generic running marker because "we're waiting on a quota window" is
|
|
623
650
|
// the operationally meaningful state.
|
|
624
651
|
if (progress.retryState && progress.status === "running") {
|
|
625
652
|
statusLine += ` ${formatBadge("retrying", "warning", theme)}`;
|
|
@@ -868,10 +895,14 @@ function renderFindings(
|
|
|
868
895
|
/**
|
|
869
896
|
* Render final result for a single agent.
|
|
870
897
|
*/
|
|
871
|
-
function renderAgentResult(
|
|
898
|
+
function renderAgentResult(
|
|
899
|
+
result: SingleResult,
|
|
900
|
+
prefix: string,
|
|
901
|
+
continuePrefix: string,
|
|
902
|
+
expanded: boolean,
|
|
903
|
+
theme: Theme,
|
|
904
|
+
): string[] {
|
|
872
905
|
const lines: string[] = [];
|
|
873
|
-
const prefix = isLast ? theme.fg("dim", theme.tree.last) : theme.fg("dim", theme.tree.branch);
|
|
874
|
-
const continuePrefix = isLast ? " " : `${theme.fg("dim", theme.tree.vertical)} `;
|
|
875
906
|
|
|
876
907
|
const { warning: missingCompleteWarning, rest: outputWithoutWarning } = extractMissingYieldWarning(result.output);
|
|
877
908
|
const aborted = result.aborted ?? false;
|
|
@@ -900,7 +931,7 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool
|
|
|
900
931
|
const description = result.description?.trim();
|
|
901
932
|
const displayId = formatTaskId(result.id);
|
|
902
933
|
const titlePart = description ? `${theme.bold(displayId)}: ${description}` : displayId;
|
|
903
|
-
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)} ${formatBadge(
|
|
934
|
+
let statusLine = `${prefix ? `${prefix} ` : ""}${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)} ${formatBadge(
|
|
904
935
|
statusText,
|
|
905
936
|
iconColor,
|
|
906
937
|
theme,
|
|
@@ -1044,101 +1075,123 @@ export function renderResult(
|
|
|
1044
1075
|
result: { content: Array<{ type: string; text?: string }>; details?: TaskToolDetails },
|
|
1045
1076
|
options: RenderResultOptions,
|
|
1046
1077
|
theme: Theme,
|
|
1078
|
+
args?: TaskParams,
|
|
1047
1079
|
): Component {
|
|
1048
1080
|
const fallbackText = result.content.find(c => c.type === "text")?.text ?? "";
|
|
1049
1081
|
const details = result.details;
|
|
1082
|
+
const contextSectionRenderer = createContextSectionRenderer(args, theme);
|
|
1050
1083
|
|
|
1051
1084
|
if (!details) {
|
|
1052
1085
|
const text = result.content.find(c => c.type === "text")?.text || "";
|
|
1053
|
-
|
|
1086
|
+
const header = renderStatusLine({ icon: "success", title: "Task" }, theme);
|
|
1087
|
+
return framedBlock(theme, width => ({
|
|
1088
|
+
header,
|
|
1089
|
+
sections: [
|
|
1090
|
+
...(contextSectionRenderer ? [contextSectionRenderer(width)] : []),
|
|
1091
|
+
...(text ? [{ separator: true, lines: [theme.fg("dim", truncateToWidth(text, width))] }] : []),
|
|
1092
|
+
],
|
|
1093
|
+
state: "success",
|
|
1094
|
+
borderColor: "borderMuted",
|
|
1095
|
+
width,
|
|
1096
|
+
}));
|
|
1054
1097
|
}
|
|
1055
1098
|
|
|
1056
|
-
|
|
1099
|
+
const hasResults = Boolean(details.results && details.results.length > 0);
|
|
1100
|
+
const aborted = hasResults && details.results.some(r => r.aborted);
|
|
1101
|
+
const failed = hasResults && details.results.some(r => !r.aborted && r.exitCode !== 0);
|
|
1102
|
+
const mergeFailed = hasResults && details.results.some(r => !r.aborted && r.exitCode === 0 && Boolean(r.error));
|
|
1103
|
+
const isError = aborted || failed;
|
|
1104
|
+
const agentCount = hasResults ? details.results.length : (details.progress?.length ?? 0);
|
|
1105
|
+
const icon: ToolUIStatus = options.isPartial ? "running" : isError ? "error" : mergeFailed ? "warning" : "success";
|
|
1106
|
+
const header = renderStatusLine(
|
|
1107
|
+
{
|
|
1108
|
+
icon,
|
|
1109
|
+
title: "Task",
|
|
1110
|
+
meta: agentCount > 0 ? [`${agentCount} ${agentCount === 1 ? "agent" : "agents"}`] : undefined,
|
|
1111
|
+
},
|
|
1112
|
+
theme,
|
|
1113
|
+
);
|
|
1057
1114
|
|
|
1058
|
-
return {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
const key = new Hasher()
|
|
1062
|
-
.bool(expanded)
|
|
1063
|
-
.bool(isPartial)
|
|
1064
|
-
.u32(spinnerFrame ?? 0)
|
|
1065
|
-
.u32(width)
|
|
1066
|
-
.digest();
|
|
1067
|
-
if (cached?.key === key) return cached.lines;
|
|
1068
|
-
|
|
1069
|
-
const lines: string[] = [];
|
|
1070
|
-
|
|
1071
|
-
const shouldRenderProgress =
|
|
1072
|
-
Boolean(details.progress && details.progress.length > 0) && (isPartial || details.results.length === 0);
|
|
1073
|
-
if (shouldRenderProgress && details.progress) {
|
|
1074
|
-
details.progress.forEach((progress, i) => {
|
|
1075
|
-
const isLast = i === details.progress!.length - 1;
|
|
1076
|
-
lines.push(...renderAgentProgress(progress, isLast, expanded, theme, spinnerFrame));
|
|
1077
|
-
});
|
|
1078
|
-
} else if (details.results && details.results.length > 0) {
|
|
1079
|
-
details.results.forEach((res, i) => {
|
|
1080
|
-
const isLast = i === details.results.length - 1;
|
|
1081
|
-
lines.push(...renderAgentResult(res, isLast, expanded, theme));
|
|
1082
|
-
});
|
|
1083
|
-
|
|
1084
|
-
const abortedCount = details.results.filter(r => r.aborted).length;
|
|
1085
|
-
const mergeFailedCount = details.results.filter(r => !r.aborted && r.exitCode === 0 && r.error).length;
|
|
1086
|
-
const successCount = details.results.filter(r => !r.aborted && r.exitCode === 0 && !r.error).length;
|
|
1087
|
-
const failCount = details.results.length - successCount - mergeFailedCount - abortedCount;
|
|
1088
|
-
let summary = `${theme.fg("dim", "Total:")} `;
|
|
1089
|
-
if (abortedCount > 0) {
|
|
1090
|
-
summary += theme.fg("error", `${abortedCount} aborted`);
|
|
1091
|
-
if (successCount > 0 || mergeFailedCount > 0 || failCount > 0) summary += theme.sep.dot;
|
|
1092
|
-
}
|
|
1093
|
-
if (successCount > 0) {
|
|
1094
|
-
summary += theme.fg("success", `${successCount} succeeded`);
|
|
1095
|
-
if (mergeFailedCount > 0 || failCount > 0) summary += theme.sep.dot;
|
|
1096
|
-
}
|
|
1097
|
-
if (mergeFailedCount > 0) {
|
|
1098
|
-
summary += theme.fg("warning", `${mergeFailedCount} merge failed`);
|
|
1099
|
-
if (failCount > 0) summary += theme.sep.dot;
|
|
1100
|
-
}
|
|
1101
|
-
if (failCount > 0) {
|
|
1102
|
-
summary += theme.fg("error", `${failCount} failed`);
|
|
1103
|
-
}
|
|
1104
|
-
summary += `${theme.sep.dot}${theme.fg("dim", formatDuration(details.totalDurationMs))}`;
|
|
1105
|
-
lines.push(summary);
|
|
1106
|
-
}
|
|
1115
|
+
return framedBlock(theme, width => {
|
|
1116
|
+
const { expanded, isPartial, spinnerFrame } = options;
|
|
1117
|
+
const lines: string[] = [];
|
|
1107
1118
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1119
|
+
const shouldRenderProgress =
|
|
1120
|
+
Boolean(details.progress && details.progress.length > 0) && (isPartial || details.results.length === 0);
|
|
1121
|
+
if (shouldRenderProgress && details.progress) {
|
|
1122
|
+
details.progress.forEach(progress => {
|
|
1123
|
+
lines.push(...renderAgentProgress(progress, "", " ", expanded, theme, spinnerFrame));
|
|
1124
|
+
});
|
|
1125
|
+
} else if (details.results && details.results.length > 0) {
|
|
1126
|
+
details.results.forEach(res => {
|
|
1127
|
+
lines.push(...renderAgentResult(res, "", " ", expanded, theme));
|
|
1128
|
+
});
|
|
1114
1129
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1130
|
+
const abortedCount = details.results.filter(r => r.aborted).length;
|
|
1131
|
+
const mergeFailedCount = details.results.filter(r => !r.aborted && r.exitCode === 0 && r.error).length;
|
|
1132
|
+
const successCount = details.results.filter(r => !r.aborted && r.exitCode === 0 && !r.error).length;
|
|
1133
|
+
const failCount = details.results.length - successCount - mergeFailedCount - abortedCount;
|
|
1134
|
+
const summaryParts: string[] = [];
|
|
1135
|
+
if (abortedCount > 0) summaryParts.push(theme.fg("error", `${abortedCount} aborted`));
|
|
1136
|
+
if (successCount > 0) summaryParts.push(theme.fg("success", `${successCount} succeeded`));
|
|
1137
|
+
if (mergeFailedCount > 0) summaryParts.push(theme.fg("warning", `${mergeFailedCount} merge failed`));
|
|
1138
|
+
if (failCount > 0) summaryParts.push(theme.fg("error", `${failCount} failed`));
|
|
1139
|
+
summaryParts.push(theme.fg("dim", formatDuration(details.totalDurationMs)));
|
|
1140
|
+
// Wrap the run summary in the theme's bracket glyphs (dim chrome, colored
|
|
1141
|
+
// counts) to match the bash tool's `[Wall: … | Exit: …]` footer.
|
|
1142
|
+
lines.push(
|
|
1143
|
+
theme.fg("dim", theme.format.bracketLeft) +
|
|
1144
|
+
summaryParts.join(theme.fg("dim", theme.sep.dot)) +
|
|
1145
|
+
theme.fg("dim", theme.format.bracketRight),
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
const state = isPartial ? "running" : isError ? "error" : mergeFailed ? "warning" : "success";
|
|
1150
|
+
const borderColor = isError ? "error" : "borderMuted";
|
|
1151
|
+
|
|
1152
|
+
if (lines.length === 0) {
|
|
1153
|
+
const text = fallbackText.trim() ? fallbackText : "No results";
|
|
1154
|
+
return {
|
|
1155
|
+
header,
|
|
1156
|
+
sections: [
|
|
1157
|
+
...(contextSectionRenderer ? [contextSectionRenderer(width)] : []),
|
|
1158
|
+
{ separator: true, lines: [theme.fg("dim", truncateToWidth(text, width))] },
|
|
1159
|
+
],
|
|
1160
|
+
state,
|
|
1161
|
+
borderColor,
|
|
1162
|
+
width,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
if (fallbackText.trim()) {
|
|
1167
|
+
const summaryLines = fallbackText.split("\n");
|
|
1168
|
+
const markerIndex = summaryLines.findIndex(
|
|
1169
|
+
line =>
|
|
1170
|
+
line.includes("<system-notification>") ||
|
|
1171
|
+
line.startsWith("Applied patches:") ||
|
|
1172
|
+
line.startsWith("No changes to apply."),
|
|
1173
|
+
);
|
|
1174
|
+
if (markerIndex >= 0) {
|
|
1175
|
+
const extra = summaryLines.slice(markerIndex);
|
|
1176
|
+
for (const line of extra) {
|
|
1177
|
+
if (!line.trim()) continue;
|
|
1178
|
+
lines.push(theme.fg("dim", line));
|
|
1129
1179
|
}
|
|
1130
1180
|
}
|
|
1181
|
+
}
|
|
1131
1182
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1183
|
+
while (lines.length > 0 && lines[0].trim() === "") lines.shift();
|
|
1184
|
+
return {
|
|
1185
|
+
header,
|
|
1186
|
+
sections: [
|
|
1187
|
+
...(contextSectionRenderer ? [contextSectionRenderer(width)] : []),
|
|
1188
|
+
...(lines.length > 0 ? [{ separator: true, lines }] : []),
|
|
1189
|
+
],
|
|
1190
|
+
state,
|
|
1191
|
+
borderColor,
|
|
1192
|
+
width,
|
|
1193
|
+
};
|
|
1194
|
+
});
|
|
1142
1195
|
}
|
|
1143
1196
|
|
|
1144
1197
|
function isTaskToolDetails(value: unknown): value is TaskToolDetails {
|
|
@@ -1150,13 +1203,23 @@ function isTaskToolDetails(value: unknown): value is TaskToolDetails {
|
|
|
1150
1203
|
);
|
|
1151
1204
|
}
|
|
1152
1205
|
|
|
1206
|
+
// Nested subagent snapshots sit one or more levels below the frame border, so
|
|
1207
|
+
// they keep tree guides to convey depth (the parent prepends its own continue
|
|
1208
|
+
// prefix). Only the top-level agent list drops guides (the frame is its box).
|
|
1209
|
+
function nestedMarkers(isLast: boolean, theme: Theme): { prefix: string; continuePrefix: string } {
|
|
1210
|
+
return {
|
|
1211
|
+
prefix: isLast ? theme.fg("dim", theme.tree.last) : theme.fg("dim", theme.tree.branch),
|
|
1212
|
+
continuePrefix: isLast ? " " : `${theme.fg("dim", theme.tree.vertical)} `,
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1153
1216
|
function renderNestedTaskResults(detailsList: TaskToolDetails[], expanded: boolean, theme: Theme): string[] {
|
|
1154
1217
|
const lines: string[] = [];
|
|
1155
1218
|
for (const details of detailsList) {
|
|
1156
1219
|
if (!details.results || details.results.length === 0) continue;
|
|
1157
1220
|
details.results.forEach((result, index) => {
|
|
1158
|
-
const
|
|
1159
|
-
lines.push(...renderAgentResult(result,
|
|
1221
|
+
const { prefix, continuePrefix } = nestedMarkers(index === details.results.length - 1, theme);
|
|
1222
|
+
lines.push(...renderAgentResult(result, prefix, continuePrefix, expanded, theme));
|
|
1160
1223
|
});
|
|
1161
1224
|
}
|
|
1162
1225
|
return lines;
|
|
@@ -1178,16 +1241,16 @@ function renderNestedTaskTree(
|
|
|
1178
1241
|
const hasResults = Boolean(details.results && details.results.length > 0);
|
|
1179
1242
|
if (hasResults) {
|
|
1180
1243
|
details.results.forEach((result, index) => {
|
|
1181
|
-
const
|
|
1182
|
-
lines.push(...renderAgentResult(result,
|
|
1244
|
+
const { prefix, continuePrefix } = nestedMarkers(index === details.results.length - 1, theme);
|
|
1245
|
+
lines.push(...renderAgentResult(result, prefix, continuePrefix, expanded, theme));
|
|
1183
1246
|
});
|
|
1184
1247
|
continue;
|
|
1185
1248
|
}
|
|
1186
1249
|
const inflight = details.progress;
|
|
1187
1250
|
if (inflight && inflight.length > 0) {
|
|
1188
1251
|
inflight.forEach((prog, index) => {
|
|
1189
|
-
const
|
|
1190
|
-
lines.push(...renderAgentProgress(prog,
|
|
1252
|
+
const { prefix, continuePrefix } = nestedMarkers(index === inflight.length - 1, theme);
|
|
1253
|
+
lines.push(...renderAgentProgress(prog, prefix, continuePrefix, expanded, theme, spinnerFrame));
|
|
1191
1254
|
});
|
|
1192
1255
|
}
|
|
1193
1256
|
}
|
|
@@ -1208,4 +1271,5 @@ subprocessToolRegistry.register<TaskToolDetails>("task", {
|
|
|
1208
1271
|
export const taskToolRenderer = {
|
|
1209
1272
|
renderCall,
|
|
1210
1273
|
renderResult,
|
|
1274
|
+
mergeCallAndResult: true,
|
|
1211
1275
|
};
|
package/src/telemetry-export.ts
CHANGED
|
@@ -23,11 +23,7 @@
|
|
|
23
23
|
* `sdk-trace-base@2.7` exports cleanly on Bun.
|
|
24
24
|
*/
|
|
25
25
|
import { logger, postmortem } from "@oh-my-pi/pi-utils";
|
|
26
|
-
import
|
|
27
|
-
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
28
|
-
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
29
|
-
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
30
|
-
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
26
|
+
import type * as TraceNode from "@opentelemetry/sdk-trace-node";
|
|
31
27
|
|
|
32
28
|
/**
|
|
33
29
|
* Periodic flush interval. A long-lived `omp` process (the ACP server is
|
|
@@ -36,7 +32,8 @@ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
|
36
32
|
*/
|
|
37
33
|
const FLUSH_INTERVAL_MS = 30_000;
|
|
38
34
|
|
|
39
|
-
let provider: NodeTracerProvider | undefined;
|
|
35
|
+
let provider: TraceNode.NodeTracerProvider | undefined;
|
|
36
|
+
let initPromise: Promise<void> | undefined;
|
|
40
37
|
|
|
41
38
|
/**
|
|
42
39
|
* Whether {@link initTelemetryExport} registered a real provider. The CLI uses
|
|
@@ -53,8 +50,10 @@ export function isTelemetryExportEnabled(): boolean {
|
|
|
53
50
|
* the OTEL kill-switches are engaged), so it is safe to call unconditionally at
|
|
54
51
|
* startup.
|
|
55
52
|
*/
|
|
56
|
-
export function initTelemetryExport(): void {
|
|
53
|
+
export async function initTelemetryExport(): Promise<void> {
|
|
57
54
|
if (provider) return;
|
|
55
|
+
if (initPromise) return initPromise;
|
|
56
|
+
|
|
58
57
|
// The OTEL env contract parses booleans and enum lists case-insensitively, so
|
|
59
58
|
// OTEL_SDK_DISABLED=TRUE and OTEL_TRACES_EXPORTER=None must also disable export.
|
|
60
59
|
if (process.env.OTEL_SDK_DISABLED?.trim().toLowerCase() === "true") return;
|
|
@@ -77,6 +76,25 @@ export function initTelemetryExport(): void {
|
|
|
77
76
|
return;
|
|
78
77
|
}
|
|
79
78
|
|
|
79
|
+
initPromise = registerProvider();
|
|
80
|
+
return initPromise;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function registerProvider(): Promise<void> {
|
|
84
|
+
const [
|
|
85
|
+
{ AsyncLocalStorageContextManager },
|
|
86
|
+
{ OTLPTraceExporter },
|
|
87
|
+
{ resourceFromAttributes },
|
|
88
|
+
{ BatchSpanProcessor },
|
|
89
|
+
{ NodeTracerProvider },
|
|
90
|
+
] = await Promise.all([
|
|
91
|
+
import("@opentelemetry/context-async-hooks"),
|
|
92
|
+
import("@opentelemetry/exporter-trace-otlp-proto"),
|
|
93
|
+
import("@opentelemetry/resources"),
|
|
94
|
+
import("@opentelemetry/sdk-trace-base"),
|
|
95
|
+
import("@opentelemetry/sdk-trace-node"),
|
|
96
|
+
]);
|
|
97
|
+
|
|
80
98
|
// The exporter reads endpoint/headers/timeout from OTEL_EXPORTER_OTLP_* itself,
|
|
81
99
|
// so there is nothing to thread through here.
|
|
82
100
|
const exporter = new OTLPTraceExporter();
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
1
4
|
import { inflateSync, strFromU8 } from "fflate";
|
|
2
5
|
|
|
6
|
+
import { formatBytes } from "./render-utils";
|
|
3
7
|
import { ToolError } from "./tool-errors";
|
|
4
8
|
|
|
5
9
|
export type ArchiveFormat = "zip" | "tar" | "tar.gz";
|
|
@@ -123,11 +127,21 @@ function getArchiveFormatFromPath(filePath: string): ArchiveFormat | undefined {
|
|
|
123
127
|
return undefined;
|
|
124
128
|
}
|
|
125
129
|
|
|
130
|
+
export function formatArchiveEntryLines(entries: readonly ArchiveDirectoryEntry[]): string[] {
|
|
131
|
+
return entries.map(entry => {
|
|
132
|
+
if (entry.isDirectory) return `${entry.name}/`;
|
|
133
|
+
|
|
134
|
+
const sizeSuffix = entry.size > 0 ? ` (${formatBytes(entry.size)})` : "";
|
|
135
|
+
return `${entry.name}${sizeSuffix}`;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
126
139
|
const ZIP_LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
|
|
127
140
|
const ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014b50;
|
|
128
141
|
const ZIP64_EOCD_SIGNATURE = 0x06064b50;
|
|
129
142
|
const ZIP64_EOCD_LOCATOR_SIGNATURE = 0x07064b50;
|
|
130
143
|
const ZIP_EOCD_SIGNATURE = 0x06054b50;
|
|
144
|
+
const ZIP_DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
|
|
131
145
|
const ZIP_EOCD_MIN_LENGTH = 22;
|
|
132
146
|
const ZIP_EOCD_MAX_COMMENT_LENGTH = 0xffff;
|
|
133
147
|
const ZIP64_EOCD_LOCATOR_LENGTH = 20;
|
|
@@ -167,6 +181,37 @@ function readUInt32LE(bytes: Uint8Array, offset: number): number {
|
|
|
167
181
|
return (bytes[offset]! | (bytes[offset + 1]! << 8) | (bytes[offset + 2]! << 16) | (bytes[offset + 3]! << 24)) >>> 0;
|
|
168
182
|
}
|
|
169
183
|
|
|
184
|
+
function bytesMatchAscii(bytes: Uint8Array, offset: number, value: string): boolean {
|
|
185
|
+
if (bytes.byteLength < offset + value.length) return false;
|
|
186
|
+
for (let index = 0; index < value.length; index++) {
|
|
187
|
+
if (bytes[offset + index] !== value.charCodeAt(index)) return false;
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function sniffArchiveFormat(bytes: Uint8Array): ArchiveFormat | undefined {
|
|
193
|
+
if (bytes.byteLength >= 4) {
|
|
194
|
+
const signature = readUInt32LE(bytes, 0);
|
|
195
|
+
if (
|
|
196
|
+
signature === ZIP_LOCAL_FILE_HEADER_SIGNATURE ||
|
|
197
|
+
signature === ZIP_EOCD_SIGNATURE ||
|
|
198
|
+
signature === ZIP_DATA_DESCRIPTOR_SIGNATURE
|
|
199
|
+
) {
|
|
200
|
+
return "zip";
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (bytes.byteLength >= 2 && bytes[0] === 0x1f && bytes[1] === 0x8b) {
|
|
205
|
+
return "tar.gz";
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (bytesMatchAscii(bytes, 257, "ustar")) {
|
|
209
|
+
return "tar";
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
170
215
|
function readUInt64LEAsNumber(bytes: Uint8Array, offset: number): number {
|
|
171
216
|
const value = readUInt32LE(bytes, offset) + readUInt32LE(bytes, offset + 4) * ZIP_UINT32_RANGE;
|
|
172
217
|
if (!Number.isSafeInteger(value)) {
|
|
@@ -627,3 +672,22 @@ export async function openArchive(filePath: string): Promise<ArchiveReader> {
|
|
|
627
672
|
format === "zip" ? await readZipEntries(filePath) : await readTarEntries(await Bun.file(filePath).bytes());
|
|
628
673
|
return new ArchiveReader(format, entries);
|
|
629
674
|
}
|
|
675
|
+
|
|
676
|
+
export async function listArchiveRoot(
|
|
677
|
+
bytes: Uint8Array,
|
|
678
|
+
format: ArchiveFormat,
|
|
679
|
+
opts: { limit?: number } = {},
|
|
680
|
+
): Promise<string> {
|
|
681
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-archive-"));
|
|
682
|
+
const tempPath = path.join(tempDir, `payload.${format}`);
|
|
683
|
+
try {
|
|
684
|
+
await Bun.write(tempPath, bytes);
|
|
685
|
+
const archive = await openArchive(tempPath);
|
|
686
|
+
const entries = archive.listDirectory("");
|
|
687
|
+
const limitedEntries = opts.limit !== undefined && opts.limit > 0 ? entries.slice(0, opts.limit) : entries;
|
|
688
|
+
const lines = formatArchiveEntryLines(limitedEntries);
|
|
689
|
+
return lines.length > 0 ? lines.join("\n") : "(empty archive directory)";
|
|
690
|
+
} finally {
|
|
691
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
692
|
+
}
|
|
693
|
+
}
|