@oh-my-pi/pi-coding-agent 17.0.8 → 17.1.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/CHANGELOG.md +87 -1
- package/README.md +1 -1
- package/dist/cli.js +5495 -5021
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +350 -50
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -0
- package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +10 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +14 -1
- package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
- package/dist/types/live/controller.d.ts +40 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +48 -0
- package/dist/types/live/visualizer.d.ts +33 -0
- package/dist/types/lsp/client.d.ts +18 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/manager.d.ts +6 -2
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/render.d.ts +2 -2
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/mcp/tool-bridge.d.ts +7 -3
- package/dist/types/mcp/types.d.ts +6 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/oauth-selector.d.ts +8 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/settings-defs.d.ts +8 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +10 -1
- package/dist/types/modes/rpc/rpc-frame.d.ts +16 -0
- package/dist/types/modes/rpc/rpc-messages.d.ts +26 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +40 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +9 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +314 -0
- package/dist/types/session/agent-session.d.ts +120 -750
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +49 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +22 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-loader.d.ts +6 -4
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +143 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/task/types.d.ts +14 -0
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/hub/jobs.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/report-tool-issue.d.ts +24 -9
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +14 -0
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +95 -1
- package/package.json +13 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/scripts/legacy-pi-virtual-module.ts +1 -1
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +2 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/grievances-cli.ts +2 -2
- package/src/cli/usage-cli.ts +11 -2
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/__tests__/model-registry.test.ts +147 -0
- package/src/config/model-registry.ts +50 -2
- package/src/config/model-resolver.ts +0 -1
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +250 -54
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +89 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +10 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +870 -311
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/registry-helpers.ts +40 -0
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/audio-worklet.txt +59 -0
- package/src/live/browser-runtime.txt +221 -0
- package/src/live/controller.ts +464 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +490 -0
- package/src/live/visualizer.ts +234 -0
- package/src/lsp/client.ts +52 -9
- package/src/lsp/clients/biome-client.ts +3 -4
- package/src/lsp/index.ts +44 -11
- package/src/main.ts +9 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/manager.ts +39 -8
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/render.ts +94 -35
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/mcp/tool-bridge.ts +107 -11
- package/src/mcp/types.ts +7 -0
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +27 -10
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/oauth-selector.ts +24 -7
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/settings-defs.ts +28 -3
- package/src/modes/components/settings-selector.ts +178 -6
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.test.ts +63 -2
- package/src/modes/components/tool-execution.ts +11 -2
- package/src/modes/controllers/command-controller.ts +70 -22
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/live-command-controller.ts +178 -0
- package/src/modes/controllers/mcp-command-controller.ts +80 -41
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +18 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +160 -38
- package/src/modes/rpc/rpc-client.ts +94 -3
- package/src/modes/rpc/rpc-frame.ts +164 -4
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +79 -7
- package/src/modes/rpc/rpc-types.ts +34 -2
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/setup-wizard/scenes/model.ts +5 -7
- package/src/modes/setup-wizard/scenes/providers.ts +3 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +8 -2
- package/src/modes/setup-wizard/scenes/theme.ts +13 -3
- package/src/modes/setup-wizard/scenes/types.ts +9 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +23 -10
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/goals/guided-goal-system.md +24 -3
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/task.md +12 -2
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +255 -139
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +334 -0
- package/src/session/agent-session.ts +1910 -12213
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +212 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +263 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.test.ts +224 -1
- package/src/session/session-context.ts +47 -5
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +10 -51
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +943 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +92 -11
- package/src/slash-commands/helpers/usage-report.ts +25 -4
- package/src/system-prompt.ts +41 -17
- package/src/task/executor.ts +288 -70
- package/src/task/index.ts +148 -111
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +4 -1
- package/src/task/types.ts +16 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +26 -0
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/hub/index.ts +1 -1
- package/src/tools/hub/jobs.ts +67 -8
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +12 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +7 -1
- package/src/tools/report-tool-issue.ts +79 -28
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +67 -3
- package/src/tts/streaming-player.ts +62 -4
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/firecrawl.ts +46 -13
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +8 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { $which, logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
|
|
5
|
+
/** Default cap on a single `direnv` invocation. The first export for a devenv
|
|
6
|
+
* `.envrc` can build a shell; callers may raise this via `bash.direnvLoadTimeoutMs`. */
|
|
7
|
+
export const DEFAULT_DIRENV_TIMEOUT_MS = 30_000;
|
|
8
|
+
|
|
9
|
+
/** Walk up from `startDir` to the nearest directory containing an `.envrc`. */
|
|
10
|
+
export async function findEnvrc(startDir: string): Promise<string | null> {
|
|
11
|
+
let dir = path.resolve(startDir);
|
|
12
|
+
for (;;) {
|
|
13
|
+
const candidate = path.join(dir, ".envrc");
|
|
14
|
+
try {
|
|
15
|
+
if ((await fs.stat(candidate)).isFile()) return candidate;
|
|
16
|
+
} catch {
|
|
17
|
+
// no .envrc here — keep walking up
|
|
18
|
+
}
|
|
19
|
+
const parent = path.dirname(dir);
|
|
20
|
+
if (parent === dir) return null;
|
|
21
|
+
dir = parent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DirenvExportDiff {
|
|
26
|
+
/** Variables direnv sets to a concrete value. */
|
|
27
|
+
set: Record<string, string>;
|
|
28
|
+
/** Variables direnv removes (JSON `null`). */
|
|
29
|
+
unset: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Parse `direnv export json` output (`{VAR: value|null}`) into set/unset halves. */
|
|
33
|
+
export function parseDirenvExport(jsonText: string): DirenvExportDiff {
|
|
34
|
+
const trimmed = jsonText.trim();
|
|
35
|
+
if (trimmed.length === 0) return { set: {}, unset: [] };
|
|
36
|
+
let parsed: unknown;
|
|
37
|
+
try {
|
|
38
|
+
parsed = JSON.parse(trimmed);
|
|
39
|
+
} catch {
|
|
40
|
+
return { set: {}, unset: [] };
|
|
41
|
+
}
|
|
42
|
+
const set: Record<string, string> = {};
|
|
43
|
+
const unset: string[] = [];
|
|
44
|
+
if (parsed && typeof parsed === "object") {
|
|
45
|
+
for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {
|
|
46
|
+
if (value === null) unset.push(key);
|
|
47
|
+
else if (typeof value === "string") set[key] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { set, unset };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let direnvLookup: { bin: string | null } | undefined;
|
|
54
|
+
function direnvBinary(): string | null {
|
|
55
|
+
if (!direnvLookup) direnvLookup = { bin: $which("direnv") };
|
|
56
|
+
return direnvLookup.bin;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** direnv computes its diff relative to the spawning env; strip any inherited
|
|
60
|
+
* direnv state so it loads the target `.envrc` from a clean baseline. */
|
|
61
|
+
function cleanSpawnEnv(): Record<string, string> {
|
|
62
|
+
const out: Record<string, string> = {};
|
|
63
|
+
for (const [key, value] of Object.entries(Bun.env)) {
|
|
64
|
+
if (value !== undefined && !key.startsWith("DIRENV_")) out[key] = value;
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function runDirenv(
|
|
70
|
+
bin: string,
|
|
71
|
+
args: string[],
|
|
72
|
+
cwd: string,
|
|
73
|
+
timeoutMs: number,
|
|
74
|
+
env: Record<string, string>,
|
|
75
|
+
signal?: AbortSignal,
|
|
76
|
+
): Promise<{ exitCode: number; stdout: string; stderr: string }> {
|
|
77
|
+
// Bail on the caller's cancellation as well as the per-invocation cap so a
|
|
78
|
+
// cold `.envrc` load can't outlive an aborted / short-timeout bash call.
|
|
79
|
+
const abortSignal = signal
|
|
80
|
+
? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)])
|
|
81
|
+
: AbortSignal.timeout(timeoutMs);
|
|
82
|
+
const proc = Bun.spawn([bin, ...args], {
|
|
83
|
+
cwd,
|
|
84
|
+
env,
|
|
85
|
+
stdout: "pipe",
|
|
86
|
+
stderr: "pipe",
|
|
87
|
+
signal: abortSignal,
|
|
88
|
+
});
|
|
89
|
+
// Drain stdout AND stderr concurrently: a cold `use devenv`/Nix load emits
|
|
90
|
+
// enough diagnostics to fill the stderr pipe and block the child forever if
|
|
91
|
+
// only stdout is read (it would then wait out `timeoutMs`).
|
|
92
|
+
const [stdout, stderr] = await Promise.all([
|
|
93
|
+
new Response(proc.stdout as ReadableStream<Uint8Array>).text(),
|
|
94
|
+
new Response(proc.stderr as ReadableStream<Uint8Array>).text(),
|
|
95
|
+
]);
|
|
96
|
+
const exitCode = await proc.exited;
|
|
97
|
+
return { exitCode, stdout, stderr };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve the nearest `.envrc` from `cwd` and return its `direnv export` diff
|
|
102
|
+
* (variables to set, and variables direnv removes). Returns `null` when there
|
|
103
|
+
* is no `.envrc`, `direnv` is not installed, the `.envrc` is not on direnv's
|
|
104
|
+
* allow list, or the export fails/times out.
|
|
105
|
+
*
|
|
106
|
+
* direnv's own allow list is honored — an `.envrc` the user has not
|
|
107
|
+
* `direnv allow`ed is NEVER executed or auto-allowed. This keeps OMP's trust
|
|
108
|
+
* boundary identical to the user's own shell: cloning a repo with a poisoned
|
|
109
|
+
* `.envrc` grants it nothing until the user explicitly allows it.
|
|
110
|
+
*
|
|
111
|
+
* Always re-invokes `direnv export json` rather than serving a cached diff:
|
|
112
|
+
* direnv is fast once warm, and its own watch/mtime invalidation is the
|
|
113
|
+
* authoritative freshness signal (a content-hash cache here would go stale when
|
|
114
|
+
* a `watch_file` target changes without the `.envrc` text changing).
|
|
115
|
+
*/
|
|
116
|
+
export async function loadDirenvEnv(
|
|
117
|
+
cwd: string,
|
|
118
|
+
opts?: { timeoutMs?: number; signal?: AbortSignal },
|
|
119
|
+
): Promise<DirenvExportDiff | null> {
|
|
120
|
+
const envrcPath = await findEnvrc(cwd);
|
|
121
|
+
if (!envrcPath) return null;
|
|
122
|
+
const bin = direnvBinary();
|
|
123
|
+
if (!bin) return null;
|
|
124
|
+
|
|
125
|
+
const dir = path.dirname(envrcPath);
|
|
126
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_DIRENV_TIMEOUT_MS;
|
|
127
|
+
const env = cleanSpawnEnv();
|
|
128
|
+
try {
|
|
129
|
+
const { exitCode, stdout, stderr } = await runDirenv(bin, ["export", "json"], dir, timeoutMs, env, opts?.signal);
|
|
130
|
+
if (exitCode !== 0) {
|
|
131
|
+
// A not-yet-allowed .envrc is an expected steady state (the user opted
|
|
132
|
+
// out by never running `direnv allow`), not an error worth warning on.
|
|
133
|
+
if (stderr.includes("is blocked")) {
|
|
134
|
+
logger.debug("direnv .envrc not allowed; skipping", { dir });
|
|
135
|
+
} else {
|
|
136
|
+
logger.warn("direnv export failed", { dir, exitCode });
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return parseDirenvExport(stdout);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
logger.warn("direnv load failed", { dir, error: err instanceof Error ? err.message : String(err) });
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/export` argument parsing, split from `./index.ts` so slash-command
|
|
3
|
+
* registries can parse arguments without eagerly loading the export module's
|
|
4
|
+
* embedded template/tool-view text.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Dark and light TUI theme names bundled into a dual-theme export. */
|
|
8
|
+
export interface ExportThemeNames {
|
|
9
|
+
dark: string;
|
|
10
|
+
light: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Parse `/export [--themes] [path]`; paths containing spaces were never supported. */
|
|
14
|
+
export function parseExportArgs(args: string): { outputPath?: string; useUserThemes: boolean } {
|
|
15
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
16
|
+
const useUserThemes = parts.includes("--themes");
|
|
17
|
+
const paths = parts.filter(part => part !== "--themes");
|
|
18
|
+
if (paths.length > 1) throw new Error("Usage: /export [--themes] [path]");
|
|
19
|
+
return { outputPath: paths[0], useUserThemes };
|
|
20
|
+
}
|
package/src/export/html/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { getResolvedThemeColors, getThemeExportColors } from "../../modes/theme/
|
|
|
6
6
|
import type { SessionEntry, SessionHeader } from "../../session/session-entries";
|
|
7
7
|
import { loadEntriesFromFile } from "../../session/session-loader";
|
|
8
8
|
import { SessionManager } from "../../session/session-manager";
|
|
9
|
+
import type { ExportThemeNames } from "./args";
|
|
9
10
|
import templateCss from "./template.css" with { type: "text" };
|
|
10
11
|
import templateHtml from "./template.html" with { type: "text" };
|
|
11
12
|
import templateJs from "./template.js" with { type: "text" };
|
|
@@ -14,6 +15,8 @@ import templateJs from "./template.js" with { type: "text" };
|
|
|
14
15
|
import toolViewsJs from "./tool-views.generated.js" with { type: "text" };
|
|
15
16
|
import { webExportThemeVars } from "./web-palette";
|
|
16
17
|
|
|
18
|
+
export { type ExportThemeNames, parseExportArgs } from "./args";
|
|
19
|
+
|
|
17
20
|
let cachedTemplate: string | undefined;
|
|
18
21
|
|
|
19
22
|
/** Compose the standalone export template: minified CSS, tool renderers, and viewer JS inlined. */
|
|
@@ -37,20 +40,12 @@ export function getTemplate(): string {
|
|
|
37
40
|
|
|
38
41
|
export interface ExportOptions {
|
|
39
42
|
outputPath?: string;
|
|
40
|
-
/**
|
|
41
|
-
* Which color palette the export ships with.
|
|
42
|
-
* - `"web"` (default) — the omp brand identity (collab-web pink/purple),
|
|
43
|
-
* so public HTML exports and the `/s/<id>` share viewer match the live
|
|
44
|
-
* `my.omp.sh` client. See `web-palette.ts`.
|
|
45
|
-
* - `"theme"` — derive from `themeName` (or the active TUI theme), preserving
|
|
46
|
-
* the pre-15.12 behavior where an export mirrored the user's terminal.
|
|
47
|
-
*/
|
|
43
|
+
/** `"web"` bundles the omp web themes; `"theme"` bundles TUI themes. */
|
|
48
44
|
palette?: "web" | "theme";
|
|
49
|
-
/**
|
|
50
|
-
* TUI theme to derive colors from when `palette: "theme"`. Ignored for the
|
|
51
|
-
* default `"web"` palette. Resolves to the active TUI theme when omitted.
|
|
52
|
-
*/
|
|
45
|
+
/** Legacy single TUI theme name. Prefer `themeNames` for dual-theme exports. */
|
|
53
46
|
themeName?: string;
|
|
47
|
+
/** Dark and light TUI themes to bundle when `palette` is `"theme"`. */
|
|
48
|
+
themeNames?: ExportThemeNames;
|
|
54
49
|
/** Embed subagent session transcripts found next to the session file (default true). */
|
|
55
50
|
includeSubSessions?: boolean;
|
|
56
51
|
}
|
|
@@ -116,50 +111,49 @@ function deriveExportColors(baseColor: string): { pageBg: string; cardBg: string
|
|
|
116
111
|
}
|
|
117
112
|
|
|
118
113
|
/**
|
|
119
|
-
* Generate CSS custom properties for
|
|
120
|
-
*
|
|
121
|
-
* Two call shapes:
|
|
122
|
-
* • `generateThemeVars("web" | "theme", themeName?)` — explicit palette.
|
|
123
|
-
* `"web"` (the default for public artifacts) returns the fixed omp brand
|
|
124
|
-
* palette from `web-palette.ts` — collab-web pink/purple identity, shared
|
|
125
|
-
* with the live `my.omp.sh` client, so exports and the share viewer render
|
|
126
|
-
* identically to it. `"theme"` derives from the TUI theme via
|
|
127
|
-
* `getResolvedThemeColors(themeName)` plus the three
|
|
128
|
-
* `export.{pageBg,cardBg,infoBg}` surface overrides.
|
|
129
|
-
* • `generateThemeVars(themeName)` — legacy single-arg form: derive from the
|
|
130
|
-
* named TUI theme. Kept so existing callers (and the theme-islight test)
|
|
131
|
-
* keep working; equivalent to `generateThemeVars("theme", themeName)`.
|
|
114
|
+
* Generate CSS custom properties for one export theme.
|
|
132
115
|
*
|
|
133
|
-
*
|
|
116
|
+
* The single-argument theme-name form remains available to callers that need
|
|
117
|
+
* one TUI palette. Standalone HTML uses `generateThemeStyles()` below.
|
|
134
118
|
*/
|
|
135
119
|
export async function generateThemeVars(
|
|
136
120
|
palette: "web" | "theme" | (string & {}) = "web",
|
|
137
121
|
themeName?: string,
|
|
138
122
|
): Promise<string> {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (palette !== "web" && palette !== "theme") {
|
|
142
|
-
return generateThemeVars("theme", palette);
|
|
143
|
-
}
|
|
144
|
-
if (palette === "web") return webExportThemeVars();
|
|
123
|
+
if (palette !== "web" && palette !== "theme") return generateThemeVars("theme", palette);
|
|
124
|
+
if (palette === "web") return webExportThemeVars("dark");
|
|
145
125
|
|
|
146
126
|
const colors = await getResolvedThemeColors(themeName);
|
|
147
|
-
const lines
|
|
148
|
-
for (const key in colors) {
|
|
149
|
-
lines.push(`--${key}: ${colors[key]};`);
|
|
150
|
-
}
|
|
151
|
-
|
|
127
|
+
const lines = Object.entries(colors).map(([key, value]) => `--${key}: ${value};`);
|
|
152
128
|
const themeExport = await getThemeExportColors(themeName);
|
|
153
|
-
const
|
|
154
|
-
const derived = deriveExportColors(userMessageBg);
|
|
129
|
+
const derived = deriveExportColors(colors.userMessageBg || "#343541");
|
|
155
130
|
|
|
156
131
|
lines.push(`--body-bg: ${themeExport.pageBg ?? derived.pageBg};`);
|
|
157
132
|
lines.push(`--container-bg: ${themeExport.cardBg ?? derived.cardBg};`);
|
|
158
133
|
lines.push(`--info-bg: ${themeExport.infoBg ?? derived.infoBg};`);
|
|
159
|
-
|
|
160
134
|
return lines.join(" ");
|
|
161
135
|
}
|
|
162
136
|
|
|
137
|
+
/** Generate dark, light, and auto-following CSS rules for a standalone viewer. */
|
|
138
|
+
export async function generateThemeStyles(
|
|
139
|
+
palette: "web" | "theme",
|
|
140
|
+
themeNames?: ExportThemeNames,
|
|
141
|
+
legacyThemeName?: string,
|
|
142
|
+
): Promise<string> {
|
|
143
|
+
const [dark, light] =
|
|
144
|
+
palette === "web"
|
|
145
|
+
? [webExportThemeVars("dark"), webExportThemeVars("light")]
|
|
146
|
+
: await Promise.all([
|
|
147
|
+
generateThemeVars("theme", themeNames?.dark ?? legacyThemeName ?? "dark"),
|
|
148
|
+
generateThemeVars("theme", themeNames?.light ?? legacyThemeName ?? "light"),
|
|
149
|
+
]);
|
|
150
|
+
return [
|
|
151
|
+
`:root, :root[data-theme="dark"] { color-scheme: dark; ${dark} }`,
|
|
152
|
+
`:root[data-theme="light"] { color-scheme: light; ${light} }`,
|
|
153
|
+
`@media (prefers-color-scheme: light) { :root:not([data-theme]) { color-scheme: light; ${light} } }`,
|
|
154
|
+
].join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
163
157
|
/** Embedded subagent session transcript, keyed by slash-joined agent path in `SessionData.subSessions`. */
|
|
164
158
|
export interface SubSession {
|
|
165
159
|
/** Bare agent id (session file stem), e.g. "ToolAsk". */
|
|
@@ -240,15 +234,19 @@ async function collectSubSessionsFromDir(
|
|
|
240
234
|
}
|
|
241
235
|
|
|
242
236
|
/** Generate HTML from bundled template with runtime substitutions. */
|
|
243
|
-
async function generateHtml(
|
|
244
|
-
|
|
237
|
+
async function generateHtml(
|
|
238
|
+
sessionData: SessionData,
|
|
239
|
+
palette: "web" | "theme",
|
|
240
|
+
themeNames?: ExportThemeNames,
|
|
241
|
+
themeName?: string,
|
|
242
|
+
): Promise<string> {
|
|
243
|
+
const themeStyles = await generateThemeStyles(palette, themeNames, themeName);
|
|
245
244
|
const sessionDataBase64 = Buffer.from(JSON.stringify(sessionData)).toBase64();
|
|
246
245
|
|
|
247
246
|
// Use function replacements so `$'`, `$&`, `$$`, `$n`, etc. in the
|
|
248
|
-
// substituted CSS/base64 are not interpreted as substitution patterns
|
|
249
|
-
// (see https://mdn.io/String.replace).
|
|
247
|
+
// substituted CSS/base64 are not interpreted as substitution patterns.
|
|
250
248
|
return getTemplate()
|
|
251
|
-
.replace("<theme-vars/>", () => `<style
|
|
249
|
+
.replace("<theme-vars/>", () => `<style>${themeStyles}</style>`)
|
|
252
250
|
.replace("{{SESSION_DATA}}", () => sessionDataBase64);
|
|
253
251
|
}
|
|
254
252
|
|
|
@@ -270,7 +268,7 @@ export async function exportSessionToHtml(
|
|
|
270
268
|
}
|
|
271
269
|
|
|
272
270
|
const palette = opts.palette ?? (opts.themeName ? "theme" : "web");
|
|
273
|
-
const html = await generateHtml(sessionData, palette, opts.themeName);
|
|
271
|
+
const html = await generateHtml(sessionData, palette, opts.themeNames, opts.themeName);
|
|
274
272
|
const outputPath = opts.outputPath || `${APP_NAME}-session-${path.basename(sessionFile, ".jsonl")}.html`;
|
|
275
273
|
|
|
276
274
|
await Bun.write(outputPath, html);
|
|
@@ -300,7 +298,7 @@ export async function exportFromFile(inputPath: string, options?: ExportOptions
|
|
|
300
298
|
}
|
|
301
299
|
|
|
302
300
|
const palette = opts.palette ?? (opts.themeName ? "theme" : "web");
|
|
303
|
-
const html = await generateHtml(sessionData, palette, opts.themeName);
|
|
301
|
+
const html = await generateHtml(sessionData, palette, opts.themeNames, opts.themeName);
|
|
304
302
|
const outputPath = opts.outputPath || `${APP_NAME}-session-${path.basename(inputPath, ".jsonl")}.html`;
|
|
305
303
|
|
|
306
304
|
await Bun.write(outputPath, html);
|
|
@@ -104,11 +104,14 @@
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
.sidebar-controls {
|
|
107
|
+
display: flex;
|
|
108
|
+
gap: 6px;
|
|
107
109
|
padding: 0 0 6px;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
.sidebar-search {
|
|
111
|
-
|
|
113
|
+
flex: 1;
|
|
114
|
+
min-width: 0;
|
|
112
115
|
box-sizing: border-box;
|
|
113
116
|
padding: 5px 9px;
|
|
114
117
|
font-size: 11px;
|
|
@@ -138,6 +141,21 @@
|
|
|
138
141
|
color: var(--muted);
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
.theme-select {
|
|
145
|
+
padding: 5px 8px;
|
|
146
|
+
font: inherit;
|
|
147
|
+
font-size: 10px;
|
|
148
|
+
background: var(--body-bg);
|
|
149
|
+
color: var(--text);
|
|
150
|
+
border: 1px solid var(--hairline);
|
|
151
|
+
border-radius: var(--radius-ctl);
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.theme-select:hover {
|
|
156
|
+
border-color: var(--hairline-strong);
|
|
157
|
+
}
|
|
158
|
+
|
|
141
159
|
.sidebar-filters {
|
|
142
160
|
display: flex;
|
|
143
161
|
gap: 4px;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<title>Session Export</title>
|
|
9
9
|
<template-css/>
|
|
10
10
|
<theme-vars/>
|
|
11
|
+
<script>try{const t=localStorage.getItem("omp-export-theme");if(t==="light"||t==="dark")document.documentElement.dataset.theme=t}catch{}</script>
|
|
11
12
|
</head>
|
|
12
13
|
<body>
|
|
13
14
|
<button id="hamburger" title="Open sidebar" aria-label="Open sidebar"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><path d="M3 6h18M3 12h18M3 18h18"/></svg></button>
|
|
@@ -17,6 +18,11 @@
|
|
|
17
18
|
<div class="sidebar-header">
|
|
18
19
|
<div class="sidebar-controls">
|
|
19
20
|
<input type="text" class="sidebar-search" id="tree-search" placeholder="Search entries…" autocomplete="off" spellcheck="false">
|
|
21
|
+
<select class="theme-select" id="theme-select" title="Color theme" aria-label="Color theme">
|
|
22
|
+
<option value="auto">Auto</option>
|
|
23
|
+
<option value="light">Light</option>
|
|
24
|
+
<option value="dark">Dark</option>
|
|
25
|
+
</select>
|
|
20
26
|
</div>
|
|
21
27
|
<div class="sidebar-filters">
|
|
22
28
|
<button class="filter-btn active" data-filter="default" title="Hide settings entries">Default</button>
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
(function() {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
const THEME_STORAGE_KEY = 'omp-export-theme';
|
|
5
|
+
const themeSelect = document.getElementById('theme-select');
|
|
6
|
+
let themePreference = 'auto';
|
|
7
|
+
try {
|
|
8
|
+
const stored = localStorage.getItem(THEME_STORAGE_KEY);
|
|
9
|
+
if (stored === 'light' || stored === 'dark' || stored === 'auto') themePreference = stored;
|
|
10
|
+
} catch {}
|
|
11
|
+
|
|
12
|
+
function applyThemePreference(next) {
|
|
13
|
+
themePreference = next;
|
|
14
|
+
if (next === 'light' || next === 'dark') document.documentElement.dataset.theme = next;
|
|
15
|
+
else delete document.documentElement.dataset.theme;
|
|
16
|
+
if (themeSelect) themeSelect.value = next;
|
|
17
|
+
try {
|
|
18
|
+
localStorage.setItem(THEME_STORAGE_KEY, next);
|
|
19
|
+
} catch {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
applyThemePreference(themePreference);
|
|
23
|
+
if (themeSelect) themeSelect.addEventListener('change', () => applyThemePreference(themeSelect.value));
|
|
24
|
+
|
|
4
25
|
// ============================================================
|
|
5
26
|
// BOOT
|
|
6
27
|
// ============================================================
|