Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
"$schema": "https://biomejs.dev/schemas/2.5.8/schema.json",
"plugins": [
"./node_modules/@aliou/biome-plugins/plugins/no-buried-await.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-emojis.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-empty-catch.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-inline-imports.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-inner-types.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-is-record.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-js-import-extension.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-ts-import-extension.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-emojis.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-inner-types.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-buried-await.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-empty-catch.grit",
"./node_modules/@aliou/biome-plugins/plugins/no-unimported-text.grit",
"./plugins/no-pi-tui-truncate.grit"
],
"vcs": {
Expand All @@ -29,7 +31,7 @@
"linter": {
"enabled": true,
"rules": {
"recommended": true
"preset": "recommended"
}
},
"formatter": {
Expand Down
4 changes: 2 additions & 2 deletions extensions/processes-dock/client.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { EventBus } from "@earendil-works/pi-coding-agent";
import type { ProcessInfo } from "../../src/types";
import {
CHANNELS,
type ProcessProtocolConfig,
type RequestCombinedOutputPayload,
type RequestConfigPayload,
type RequestGetPayload,
type RequestListPayload,
} from "../../src/protocol";
import type { ProcessInfo } from "../../src/types";
} from "../shared/protocol";

export type ProcessLogLine = { type: "stdout" | "stderr"; text: string };

Expand Down
46 changes: 9 additions & 37 deletions extensions/processes-dock/widget/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import type {
ExtensionContext,
Theme,
} from "@earendil-works/pi-coding-agent";
import { LIVE_STATUSES, type ProcessInfo } from "../../../src/types";
import { buildDroppedOutputLine, trimToBudget } from "../../shared/line-buffer";
import {
CHANNELS,
type CommandPinPayload,
type ProcessesOutputChangedPayload,
type ProcessProtocolNotificationPayload,
} from "../../../src/protocol";
import { LIVE_STATUSES, type ProcessInfo } from "../../../src/types";
import { isRecord } from "../../../src/utils/is-record";
import { buildDroppedOutputLine, trimToBudget } from "../../shared/line-buffer";
import { isOutputChangedPayload } from "../../shared/output-payload";
} from "../../shared/protocol";
import {
type ProcessLogLine,
requestCombinedOutput,
Expand Down Expand Up @@ -349,11 +348,8 @@ export function setupDockWidgets(
scheduleRefresh();
};

const handleOutputChanged = (payload: unknown) => {
if (!isOutputChangedPayload(payload)) {
scheduleRefresh();
return;
}
const handleOutputChanged = (rawPayload: unknown) => {
const payload = rawPayload as ProcessesOutputChangedPayload;
if (
(!payload.appendedText || payload.appendedText.length === 0) &&
!payload.droppedLines
Expand Down Expand Up @@ -383,7 +379,6 @@ export function setupDockWidgets(

const handlePin = (payload: unknown) => {
const command = payload as CommandPinPayload;
if (!isCommandPinPayload(command)) return;
// COMMAND_PIN can arrive before the dock's throttled CHANGED refresh has
// run. Refresh the local snapshot first so expand/pin renders immediately
// against the current process list.
Expand Down Expand Up @@ -427,8 +422,9 @@ export function setupDockWidgets(
}),
);
disposers.push(
events.on(CHANNELS.NOTIFICATION, (payload) => {
if (!isLogMatchNotification(payload)) return;
events.on(CHANNELS.NOTIFICATION, (rawPayload) => {
const payload = rawPayload as ProcessProtocolNotificationPayload;
if (payload.kind !== "log_match" || !payload.logMatch) return;
const list = notifyMarkers.get(payload.processId) ?? [];
list.push({ line: payload.logMatch.line, timestamp: payload.timestamp });
if (list.length > MAX_NOTIFY_MARKERS_PER_PROCESS) {
Expand Down Expand Up @@ -486,30 +482,6 @@ function isLogsConnectionError(
return "ok" in connection && connection.ok === false;
}

function isLogMatchNotification(
payload: unknown,
): payload is ProcessProtocolNotificationPayload & {
kind: "log_match";
logMatch: NonNullable<ProcessProtocolNotificationPayload["logMatch"]>;
} {
return (
isRecord(payload) &&
payload.kind === "log_match" &&
typeof payload.processId === "string" &&
typeof payload.timestamp === "number" &&
isRecord(payload.logMatch) &&
typeof payload.logMatch.line === "string"
);
}

function isCommandPinPayload(payload: unknown): payload is CommandPinPayload {
return (
isRecord(payload) &&
(typeof payload.id === "string" || payload.id === null) &&
typeof payload.reply === "function"
);
}

function safeReply<T>(reply: (result: T) => void, result: T): void {
try {
reply(result);
Expand Down
8 changes: 4 additions & 4 deletions extensions/processes-logs/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { createEventBus } from "@earendil-works/pi-coding-agent";
import { describe, expect, it } from "vitest";
import type { ProcessInfo } from "../../src/types";
import { DEFAULT_CONFIG } from "../processes/config";
import type {
RequestConfigPayload,
RequestGetPayload,
RequestListPayload,
} from "../../src/protocol";
import { CHANNELS } from "../../src/protocol";
import type { ProcessInfo } from "../../src/types";
import { DEFAULT_CONFIG } from "../processes/config";
} from "../shared/protocol";
import { CHANNELS } from "../shared/protocol";
import { requestConfig, requestProcess, requestProcessList } from "./client";

function makeInfo(overrides: Partial<ProcessInfo> = {}): ProcessInfo {
Expand Down
4 changes: 2 additions & 2 deletions extensions/processes-logs/client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { EventBus } from "@earendil-works/pi-coding-agent";
import type { ProcessInfo } from "../../src/types";
import {
CHANNELS,
type ProcessProtocolConfig,
type RequestConfigPayload,
type RequestGetPayload,
type RequestListPayload,
} from "../../src/protocol";
import type { ProcessInfo } from "../../src/types";
} from "../shared/protocol";

export function requestProcessList(events: EventBus): ProcessInfo[] {
let processes: ProcessInfo[] = [];
Expand Down
2 changes: 1 addition & 1 deletion extensions/processes-logs/commands/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type {
ExtensionCommandContext,
Theme,
} from "@earendil-works/pi-coding-agent";
import type { ProcessProtocolConfig } from "../../../src/protocol";
import type { ProcessInfo } from "../../../src/types";
import { sanitizeForDisplay } from "../../shared/display-text";
import type { ProcessProtocolConfig } from "../../shared/protocol";
import { requestConfig, requestProcess, requestProcessList } from "../client";
import { allProcessCompletions } from "../completions";
import { LogOverlayComponent } from "../components/log-overlay-component";
Expand Down
4 changes: 2 additions & 2 deletions extensions/processes-logs/completions.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createEventBus } from "@earendil-works/pi-coding-agent";
import { describe, expect, it } from "vitest";
import type { RequestListPayload } from "../../src/protocol";
import { CHANNELS } from "../../src/protocol";
import type { ProcessInfo } from "../../src/types";
import type { RequestListPayload } from "../shared/protocol";
import { CHANNELS } from "../shared/protocol";
import { allProcessCompletions } from "./completions";

function makeInfo(overrides: Partial<ProcessInfo> = {}): ProcessInfo {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Theme } from "@earendil-works/pi-coding-agent";
import type { TUI } from "@earendil-works/pi-tui";
import { describe, expect, it, vi } from "vitest";
import { CHANNELS } from "../../../src/protocol";
import type { ProcessInfo } from "../../../src/types";
import { CHANNELS } from "../../shared/protocol";
import type { ProcessLogLine } from "../logs-client";
import { LogOverlayComponent } from "./log-overlay-component";

Expand Down
49 changes: 9 additions & 40 deletions extensions/processes-logs/components/log-overlay-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ import {
type TUI,
visibleWidth,
} from "@earendil-works/pi-tui";
import { LIVE_STATUSES, type ProcessInfo } from "../../../src/types";
import { formatRuntime } from "../../../src/utils/format";
import { truncateForDisplay } from "../../shared/display-text";
import { renderProcessTab } from "../../shared/process-tabs";
import {
CHANNELS,
type ProcessesChangedPayload,
type ProcessProtocolConfig,
type ProcessProtocolNotificationPayload,
} from "../../../src/protocol";
import { LIVE_STATUSES, type ProcessInfo } from "../../../src/types";
import { formatRuntime } from "../../../src/utils/format";
import { isRecord } from "../../../src/utils/is-record";
import { truncateForDisplay } from "../../shared/display-text";
import { renderProcessTab } from "../../shared/process-tabs";
} from "../../shared/protocol";
import { truncateToWidth } from "../../shared/truncate";
import { LineComponent, LinesComponent, RuleComponent } from "../../shared/ui";
import { requestProcessList } from "../client";
Expand Down Expand Up @@ -89,7 +88,7 @@ export class LogOverlayComponent implements Component {
this.refreshProcesses(opts.initialProcessId);
this.disposers.push(
opts.events.on(CHANNELS.CHANGED, (payload) => {
if (isChangedPayload(payload)) this.handleProcessesChanged(payload);
this.handleProcessesChanged(payload as ProcessesChangedPayload);
}),
);
this.disposers.push(
Expand All @@ -99,8 +98,9 @@ export class LogOverlayComponent implements Component {
);
}

private handleNotification(payload: unknown): void {
if (!isLogMatchNotification(payload)) return;
private handleNotification(rawPayload: unknown): void {
const payload = rawPayload as ProcessProtocolNotificationPayload;
if (payload.kind !== "log_match" || !payload.logMatch) return;
const mark: NotifyMatchMark = {
pattern: payload.logMatch.pattern,
line: payload.logMatch.line,
Expand Down Expand Up @@ -655,34 +655,3 @@ function centeredBlock(
lines[row] = truncateToWidth(`${" ".repeat(leftPad)}${content}`, width);
return lines;
}

function isChangedPayload(
payload: unknown,
): payload is ProcessesChangedPayload {
return (
isRecord(payload) &&
(payload.reason === "started" ||
payload.reason === "ended" ||
payload.reason === "cleared")
);
}

function isLogMatchNotification(
payload: unknown,
): payload is ProcessProtocolNotificationPayload & {
kind: "log_match";
logMatch: NonNullable<ProcessProtocolNotificationPayload["logMatch"]>;
} {
if (!isRecord(payload)) return false;
if (payload.kind !== "log_match") return false;
if (typeof payload.processId !== "string") return false;
if (typeof payload.timestamp !== "number") return false;
const logMatch = payload.logMatch;
if (!isRecord(logMatch)) return false;
if (typeof logMatch.pattern !== "string") return false;
if (typeof logMatch.line !== "string") return false;
if (logMatch.stream !== "stdout" && logMatch.stream !== "stderr")
return false;
if (typeof logMatch.matcherIndex !== "number") return false;
return true;
}
4 changes: 2 additions & 2 deletions extensions/processes-logs/logs-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createEventBus } from "@earendil-works/pi-coding-agent";
import { describe, expect, it, vi } from "vitest";
import type { LogsSubscribePayload } from "../../src/protocol";
import { CHANNELS } from "../../src/protocol";
import type { LogsSubscribePayload } from "../shared/protocol";
import { CHANNELS } from "../shared/protocol";
import { connectToProcessLogs } from "./logs-client";

describe("connectToProcessLogs", () => {
Expand Down
25 changes: 2 additions & 23 deletions extensions/processes-logs/logs-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import {
type LogsChunkPayload,
type LogsSubscribePayload,
type LogsUnsubscribePayload,
} from "../../src/protocol";
import { isRecord } from "../../src/utils/is-record";

} from "../shared/protocol";
export type ProcessLogLine = { type: "stdout" | "stderr"; text: string };

export interface LogsConnection {
Expand All @@ -29,8 +27,7 @@ export function connectToProcessLogs(

const disposeChunkListener = events.on(CHANNELS.LOGS_CHUNK, (raw) => {
if (disposed) return;
if (!isLogsChunkPayload(raw)) return;
const chunk = raw;
const chunk = raw as LogsChunkPayload;
if (chunk.subscriberId !== subscriberId || chunk.processId !== processId)
return;

Expand Down Expand Up @@ -81,21 +78,3 @@ export function connectToProcessLogs(
},
};
}

function isLogsChunkPayload(payload: unknown): payload is LogsChunkPayload {
return (
isRecord(payload) &&
typeof payload.subscriberId === "string" &&
typeof payload.processId === "string" &&
Array.isArray(payload.lines) &&
payload.lines.every(isProcessLogLine)
);
}

function isProcessLogLine(line: unknown): line is ProcessLogLine {
return (
isRecord(line) &&
(line.type === "stdout" || line.type === "stderr") &&
typeof line.text === "string"
);
}
4 changes: 2 additions & 2 deletions extensions/processes/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createEventBus } from "@earendil-works/pi-coding-agent";
import { describe, expect, it } from "vitest";
import type { CommandKillPayload } from "../../src/protocol";
import { CHANNELS } from "../../src/protocol";
import type { KillResult, ProcessInfo } from "../../src/types";
import type { CommandKillPayload } from "../shared/protocol";
import { CHANNELS } from "../shared/protocol";
import { requestKill } from "./client";

function makeInfo(overrides: Partial<ProcessInfo> = {}): ProcessInfo {
Expand Down
4 changes: 2 additions & 2 deletions extensions/processes/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import type { EventBus } from "@earendil-works/pi-coding-agent";
import type { KillResult, ProcessInfo } from "../../src/types";
import {
CHANNELS,
type CommandClearPayload,
Expand All @@ -19,8 +20,7 @@ import {
type RequestConfigPayload,
type RequestGetPayload,
type RequestListPayload,
} from "../../src/protocol";
import type { KillResult, ProcessInfo } from "../../src/types";
} from "../shared/protocol";

export type ProcessLogLine = { type: "stdout" | "stderr"; text: string };

Expand Down
2 changes: 1 addition & 1 deletion extensions/processes/commands/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type {
ExtensionCommandContext,
Theme,
} from "@earendil-works/pi-coding-agent";
import type { ProcessProtocolConfig } from "../../../src/protocol";
import type { ProcessInfo } from "../../../src/types";
import { sanitizeForDisplay } from "../../shared/display-text";
import type { ProcessProtocolConfig } from "../../shared/protocol";
import { requestConfig, requestProcess, requestProcessList } from "../client";
import { OverviewComponent } from "../components/overview-component";

Expand Down
11 changes: 7 additions & 4 deletions extensions/processes/components/overview-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
type TUI,
visibleWidth,
} from "@earendil-works/pi-tui";
import { CHANNELS, type ProcessProtocolConfig } from "../../../src/protocol";
import { LIVE_STATUSES, type ProcessInfo } from "../../../src/types";
import { formatRuntime } from "../../../src/utils/format";
import {
Expand All @@ -17,7 +16,11 @@ import {
} from "../../shared/display-text";
import { buildDroppedOutputLine, trimToBudget } from "../../shared/line-buffer";
import { renderLogLine } from "../../shared/log-line";
import { isOutputChangedPayload } from "../../shared/output-payload";
import {
CHANNELS,
type ProcessesOutputChangedPayload,
type ProcessProtocolConfig,
} from "../../shared/protocol";
import { truncateToWidth } from "../../shared/truncate";
import { LineComponent, LinesComponent, statusColor } from "../../shared/ui";
import {
Expand Down Expand Up @@ -195,8 +198,8 @@ export class OverviewComponent implements Component {
this.requestRender();
}

private handleOutputChanged(payload: unknown): void {
if (!isOutputChangedPayload(payload)) return;
private handleOutputChanged(rawPayload: unknown): void {
const payload = rawPayload as ProcessesOutputChangedPayload;
const selected = this.selectedProcess();
if (!selected || selected.id !== payload.id) return;
const appended = [
Expand Down
Loading