mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-28 18:06:30 +02:00
bootstrap new electron app
This commit is contained in:
parent
2491bacea1
commit
505e3ea620
89 changed files with 12397 additions and 8435 deletions
35
apps/x/packages/shared/src/agent.ts
Normal file
35
apps/x/packages/shared/src/agent.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const BaseTool = z.object({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export const BuiltinTool = BaseTool.extend({
|
||||
type: z.literal("builtin"),
|
||||
});
|
||||
|
||||
export const McpTool = BaseTool.extend({
|
||||
type: z.literal("mcp"),
|
||||
description: z.string(),
|
||||
inputSchema: z.any(),
|
||||
mcpServerName: z.string(),
|
||||
});
|
||||
|
||||
export const AgentAsATool = BaseTool.extend({
|
||||
type: z.literal("agent"),
|
||||
});
|
||||
|
||||
export const ToolAttachment = z.discriminatedUnion("type", [
|
||||
BuiltinTool,
|
||||
McpTool,
|
||||
AgentAsATool,
|
||||
]);
|
||||
|
||||
export const Agent = z.object({
|
||||
name: z.string(),
|
||||
provider: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
instructions: z.string(),
|
||||
tools: z.record(z.string(), ToolAttachment).optional(),
|
||||
});
|
||||
12
apps/x/packages/shared/src/example.ts
Normal file
12
apps/x/packages/shared/src/example.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import z from "zod"
|
||||
import { Agent } from "./agent.js"
|
||||
import { McpServerDefinition } from "./mcp.js";
|
||||
|
||||
export const Example = z.object({
|
||||
id: z.string(),
|
||||
instructions: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
entryAgent: z.string().optional(),
|
||||
agents: z.array(Agent).optional(),
|
||||
mcpServers: z.record(z.string(), McpServerDefinition).optional(),
|
||||
});
|
||||
6
apps/x/packages/shared/src/index.ts
Normal file
6
apps/x/packages/shared/src/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { PrefixLogger } from './prefix-logger.js';
|
||||
|
||||
export * as ipc from './ipc.js';
|
||||
export * as workspace from './workspace.js';
|
||||
export * as mcp from './mcp.js';
|
||||
export { PrefixLogger };
|
||||
212
apps/x/packages/shared/src/ipc.ts
Normal file
212
apps/x/packages/shared/src/ipc.ts
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
import { z } from 'zod';
|
||||
import { RelPath, Encoding, Stat, DirEntry, ReaddirOptions, ReadFileResult, WorkspaceChangeEvent, WriteFileOptions, WriteFileResult, RemoveOptions } from './workspace.js';
|
||||
import { ListToolsResponse } from './mcp.js';
|
||||
import { AskHumanResponsePayload, CreateRunOptions, Run, ToolPermissionAuthorizePayload } from './runs.js';
|
||||
|
||||
// ============================================================================
|
||||
// Runtime Validation Schemas (Single Source of Truth)
|
||||
// ============================================================================
|
||||
|
||||
const ipcSchemas = {
|
||||
'app:getVersions': {
|
||||
req: z.null(),
|
||||
res: z.object({
|
||||
chrome: z.string(),
|
||||
node: z.string(),
|
||||
electron: z.string(),
|
||||
}),
|
||||
},
|
||||
'workspace:getRoot': {
|
||||
req: z.null(),
|
||||
res: z.object({
|
||||
root: z.string(),
|
||||
}),
|
||||
},
|
||||
'workspace:exists': {
|
||||
req: z.object({
|
||||
path: RelPath,
|
||||
}),
|
||||
res: z.object({
|
||||
exists: z.boolean(),
|
||||
}),
|
||||
},
|
||||
'workspace:stat': {
|
||||
req: z.object({
|
||||
path: RelPath,
|
||||
}),
|
||||
res: Stat,
|
||||
},
|
||||
'workspace:readdir': {
|
||||
req: z.object({
|
||||
path: z.string(), // Empty string allowed for root directory
|
||||
opts: ReaddirOptions.optional(),
|
||||
}),
|
||||
res: z.array(DirEntry),
|
||||
},
|
||||
'workspace:readFile': {
|
||||
req: z.object({
|
||||
path: RelPath,
|
||||
encoding: Encoding.optional(),
|
||||
}),
|
||||
res: ReadFileResult,
|
||||
},
|
||||
'workspace:writeFile': {
|
||||
req: z.object({
|
||||
path: RelPath,
|
||||
data: z.string(),
|
||||
opts: WriteFileOptions.optional(),
|
||||
}),
|
||||
res: WriteFileResult,
|
||||
},
|
||||
'workspace:mkdir': {
|
||||
req: z.object({
|
||||
path: RelPath,
|
||||
recursive: z.boolean().optional(),
|
||||
}),
|
||||
res: z.object({
|
||||
ok: z.literal(true),
|
||||
}),
|
||||
},
|
||||
'workspace:rename': {
|
||||
req: z.object({
|
||||
from: RelPath,
|
||||
to: RelPath,
|
||||
overwrite: z.boolean().optional(),
|
||||
}),
|
||||
res: z.object({
|
||||
ok: z.literal(true),
|
||||
}),
|
||||
},
|
||||
'workspace:copy': {
|
||||
req: z.object({
|
||||
from: RelPath,
|
||||
to: RelPath,
|
||||
overwrite: z.boolean().optional(),
|
||||
}),
|
||||
res: z.object({
|
||||
ok: z.literal(true),
|
||||
}),
|
||||
},
|
||||
'workspace:remove': {
|
||||
req: z.object({
|
||||
path: RelPath,
|
||||
opts: RemoveOptions.optional(),
|
||||
}),
|
||||
res: z.object({
|
||||
ok: z.literal(true),
|
||||
}),
|
||||
},
|
||||
'workspace:didChange': {
|
||||
req: WorkspaceChangeEvent,
|
||||
res: z.null(),
|
||||
},
|
||||
'mcp:listTools': {
|
||||
req: z.object({
|
||||
serverName: z.string(),
|
||||
cursor: z.string().optional(),
|
||||
}),
|
||||
res: ListToolsResponse,
|
||||
},
|
||||
'mcp:executeTool': {
|
||||
req: z.object({
|
||||
serverName: z.string(),
|
||||
toolName: z.string(),
|
||||
input: z.record(z.string(), z.unknown()),
|
||||
}),
|
||||
res: z.object({
|
||||
result: z.unknown(),
|
||||
}),
|
||||
},
|
||||
'runs:create': {
|
||||
req: CreateRunOptions,
|
||||
res: Run,
|
||||
},
|
||||
'runs:createMessage': {
|
||||
req: z.object({
|
||||
runId: z.string(),
|
||||
message: z.string(),
|
||||
}),
|
||||
res: z.object({
|
||||
messageId: z.string(),
|
||||
}),
|
||||
},
|
||||
'runs:authorizePermission': {
|
||||
req: z.object({
|
||||
runId: z.string(),
|
||||
authorization: ToolPermissionAuthorizePayload,
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.literal(true),
|
||||
}),
|
||||
},
|
||||
'runs:provideHumanInput': {
|
||||
req: z.object({
|
||||
runId: z.string(),
|
||||
reply: AskHumanResponsePayload,
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.literal(true),
|
||||
}),
|
||||
},
|
||||
'runs:stop': {
|
||||
req: z.object({
|
||||
runId: z.string(),
|
||||
}),
|
||||
res: z.object({
|
||||
success: z.literal(true),
|
||||
}),
|
||||
},
|
||||
'runs:events': {
|
||||
req: z.null(),
|
||||
res: z.null(),
|
||||
}
|
||||
} as const;
|
||||
|
||||
// ============================================================================
|
||||
// Type Helpers
|
||||
// ============================================================================
|
||||
|
||||
export type IPCChannels = {
|
||||
[K in keyof typeof ipcSchemas]: {
|
||||
req: z.infer<typeof ipcSchemas[K]['req']>;
|
||||
res: z.infer<typeof ipcSchemas[K]['res']>;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Channels that use invoke/handle (request/response pattern)
|
||||
* These are channels with non-null responses
|
||||
*/
|
||||
export type InvokeChannels = {
|
||||
[K in keyof IPCChannels]:
|
||||
IPCChannels[K]['res'] extends null ? never : K
|
||||
}[keyof IPCChannels];
|
||||
|
||||
/**
|
||||
* Channels that use send/on (fire-and-forget pattern)
|
||||
* These are channels with null responses (no response expected)
|
||||
*/
|
||||
export type SendChannels = {
|
||||
[K in keyof IPCChannels]:
|
||||
IPCChannels[K]['res'] extends null ? K : never
|
||||
}[keyof IPCChannels];
|
||||
|
||||
// ============================================================================
|
||||
// Type Guards
|
||||
// ============================================================================
|
||||
|
||||
export function validateRequest<K extends keyof IPCChannels>(
|
||||
channel: K,
|
||||
data: unknown
|
||||
): IPCChannels[K]['req'] {
|
||||
const schema = ipcSchemas[channel].req;
|
||||
return schema.parse(data) as IPCChannels[K]['req'];
|
||||
}
|
||||
|
||||
export function validateResponse<K extends keyof IPCChannels>(
|
||||
channel: K,
|
||||
data: unknown
|
||||
): IPCChannels[K]['res'] {
|
||||
const schema = ipcSchemas[channel].res;
|
||||
return schema.parse(data) as IPCChannels[K]['res'];
|
||||
}
|
||||
63
apps/x/packages/shared/src/llm-step-events.ts
Normal file
63
apps/x/packages/shared/src/llm-step-events.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { z } from "zod";
|
||||
import { ProviderOptions } from "./message.js";
|
||||
|
||||
const BaseEvent = z.object({
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
})
|
||||
|
||||
export const LlmStepStreamReasoningStartEvent = BaseEvent.extend({
|
||||
type: z.literal("reasoning-start"),
|
||||
});
|
||||
|
||||
export const LlmStepStreamReasoningDeltaEvent = BaseEvent.extend({
|
||||
type: z.literal("reasoning-delta"),
|
||||
delta: z.string(),
|
||||
});
|
||||
|
||||
export const LlmStepStreamReasoningEndEvent = BaseEvent.extend({
|
||||
type: z.literal("reasoning-end"),
|
||||
});
|
||||
|
||||
export const LlmStepStreamTextStartEvent = BaseEvent.extend({
|
||||
type: z.literal("text-start"),
|
||||
});
|
||||
|
||||
export const LlmStepStreamTextDeltaEvent = BaseEvent.extend({
|
||||
type: z.literal("text-delta"),
|
||||
delta: z.string(),
|
||||
});
|
||||
|
||||
export const LlmStepStreamTextEndEvent = BaseEvent.extend({
|
||||
type: z.literal("text-end"),
|
||||
});
|
||||
|
||||
export const LlmStepStreamToolCallEvent = BaseEvent.extend({
|
||||
type: z.literal("tool-call"),
|
||||
toolCallId: z.string(),
|
||||
toolName: z.string(),
|
||||
input: z.any(),
|
||||
});
|
||||
|
||||
export const LlmStepStreamFinishStepEvent = z.object({
|
||||
type: z.literal("finish-step"),
|
||||
finishReason: z.enum(["stop", "tool-calls", "length", "content-filter", "error", "other", "unknown"]),
|
||||
usage: z.object({
|
||||
inputTokens: z.number().optional(),
|
||||
outputTokens: z.number().optional(),
|
||||
totalTokens: z.number().optional(),
|
||||
reasoningTokens: z.number().optional(),
|
||||
cachedInputTokens: z.number().optional(),
|
||||
}),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const LlmStepStreamEvent = z.union([
|
||||
LlmStepStreamReasoningStartEvent,
|
||||
LlmStepStreamReasoningDeltaEvent,
|
||||
LlmStepStreamReasoningEndEvent,
|
||||
LlmStepStreamTextStartEvent,
|
||||
LlmStepStreamTextDeltaEvent,
|
||||
LlmStepStreamTextEndEvent,
|
||||
LlmStepStreamToolCallEvent,
|
||||
LlmStepStreamFinishStepEvent,
|
||||
]);
|
||||
50
apps/x/packages/shared/src/mcp.ts
Normal file
50
apps/x/packages/shared/src/mcp.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import z from "zod";
|
||||
|
||||
export const StdioMcpServerConfig = z.object({
|
||||
type: z.literal("stdio").optional(),
|
||||
command: z.string(),
|
||||
args: z.array(z.string()).optional(),
|
||||
env: z.record(z.string(), z.string()).optional(),
|
||||
});
|
||||
|
||||
export const HttpMcpServerConfig = z.object({
|
||||
type: z.literal("http").optional(),
|
||||
url: z.string(),
|
||||
headers: z.record(z.string(), z.string()).optional(),
|
||||
});
|
||||
|
||||
export const McpServerDefinition = z.union([StdioMcpServerConfig, HttpMcpServerConfig]);
|
||||
|
||||
export const McpServerConfig = z.object({
|
||||
mcpServers: z.record(z.string(), McpServerDefinition),
|
||||
});
|
||||
|
||||
export const connectionState = z.enum(["disconnected", "connected", "error"]);
|
||||
|
||||
export const McpServerList = z.object({
|
||||
mcpServers: z.record(z.string(), z.object({
|
||||
config: McpServerDefinition,
|
||||
state: connectionState,
|
||||
error: z.string().nullable(),
|
||||
})),
|
||||
});
|
||||
|
||||
export const Tool = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
inputSchema: z.object({
|
||||
type: z.literal("object"),
|
||||
properties: z.record(z.string(), z.any()).optional(),
|
||||
required: z.array(z.string()).optional(),
|
||||
}),
|
||||
outputSchema: z.object({
|
||||
type: z.literal("object"),
|
||||
properties: z.record(z.string(), z.any()).optional(),
|
||||
required: z.array(z.string()).optional(),
|
||||
}).optional(),
|
||||
});
|
||||
|
||||
export const ListToolsResponse = z.object({
|
||||
tools: z.array(Tool),
|
||||
nextCursor: z.string().optional(),
|
||||
});
|
||||
67
apps/x/packages/shared/src/message.ts
Normal file
67
apps/x/packages/shared/src/message.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const ProviderOptions = z.record(z.string(), z.record(z.string(), z.json()));
|
||||
|
||||
export const TextPart = z.object({
|
||||
type: z.literal("text"),
|
||||
text: z.string(),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const ReasoningPart = z.object({
|
||||
type: z.literal("reasoning"),
|
||||
text: z.string(),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const ToolCallPart = z.object({
|
||||
type: z.literal("tool-call"),
|
||||
toolCallId: z.string(),
|
||||
toolName: z.string(),
|
||||
arguments: z.any(),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const AssistantContentPart = z.union([
|
||||
TextPart,
|
||||
ReasoningPart,
|
||||
ToolCallPart,
|
||||
]);
|
||||
|
||||
export const UserMessage = z.object({
|
||||
role: z.literal("user"),
|
||||
content: z.string(),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const AssistantMessage = z.object({
|
||||
role: z.literal("assistant"),
|
||||
content: z.union([
|
||||
z.string(),
|
||||
z.array(AssistantContentPart),
|
||||
]),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const SystemMessage = z.object({
|
||||
role: z.literal("system"),
|
||||
content: z.string(),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const ToolMessage = z.object({
|
||||
role: z.literal("tool"),
|
||||
content: z.string(),
|
||||
toolCallId: z.string(),
|
||||
toolName: z.string(),
|
||||
providerOptions: ProviderOptions.optional(),
|
||||
});
|
||||
|
||||
export const Message = z.discriminatedUnion("role", [
|
||||
AssistantMessage,
|
||||
SystemMessage,
|
||||
ToolMessage,
|
||||
UserMessage,
|
||||
]);
|
||||
|
||||
export const MessageList = z.array(Message);
|
||||
26
apps/x/packages/shared/src/prefix-logger.ts
Normal file
26
apps/x/packages/shared/src/prefix-logger.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// create a PrefixLogger class that wraps console.log with a prefix
|
||||
// and allows chaining with a parent logger
|
||||
export class PrefixLogger {
|
||||
private prefix: string;
|
||||
private parent: PrefixLogger | null;
|
||||
|
||||
constructor(prefix: string, parent: PrefixLogger | null = null) {
|
||||
this.prefix = prefix;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
log(...args: unknown[]) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const prefix = '[' + this.prefix + ']';
|
||||
|
||||
if (this.parent) {
|
||||
this.parent.log(prefix, ...args);
|
||||
} else {
|
||||
console.log(timestamp, prefix, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
child(childPrefix: string): PrefixLogger {
|
||||
return new PrefixLogger(childPrefix, this);
|
||||
}
|
||||
}
|
||||
129
apps/x/packages/shared/src/runs.ts
Normal file
129
apps/x/packages/shared/src/runs.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import { LlmStepStreamEvent } from "./llm-step-events.js";
|
||||
import { Message, ToolCallPart } from "./message.js";
|
||||
import z from "zod";
|
||||
|
||||
const BaseRunEvent = z.object({
|
||||
runId: z.string(),
|
||||
ts: z.iso.datetime().optional(),
|
||||
subflow: z.array(z.string()),
|
||||
});
|
||||
|
||||
export const RunProcessingStartEvent = BaseRunEvent.extend({
|
||||
type: z.literal("run-processing-start"),
|
||||
});
|
||||
|
||||
export const RunProcessingEndEvent = BaseRunEvent.extend({
|
||||
type: z.literal("run-processing-end"),
|
||||
});
|
||||
|
||||
export const StartEvent = BaseRunEvent.extend({
|
||||
type: z.literal("start"),
|
||||
agentName: z.string(),
|
||||
});
|
||||
|
||||
export const SpawnSubFlowEvent = BaseRunEvent.extend({
|
||||
type: z.literal("spawn-subflow"),
|
||||
agentName: z.string(),
|
||||
toolCallId: z.string(),
|
||||
});
|
||||
|
||||
export const LlmStreamEvent = BaseRunEvent.extend({
|
||||
type: z.literal("llm-stream-event"),
|
||||
event: LlmStepStreamEvent,
|
||||
});
|
||||
|
||||
export const MessageEvent = BaseRunEvent.extend({
|
||||
type: z.literal("message"),
|
||||
messageId: z.string(),
|
||||
message: Message,
|
||||
});
|
||||
|
||||
export const ToolInvocationEvent = BaseRunEvent.extend({
|
||||
type: z.literal("tool-invocation"),
|
||||
toolCallId: z.string().optional(),
|
||||
toolName: z.string(),
|
||||
input: z.string(),
|
||||
});
|
||||
|
||||
export const ToolResultEvent = BaseRunEvent.extend({
|
||||
type: z.literal("tool-result"),
|
||||
toolCallId: z.string().optional(),
|
||||
toolName: z.string(),
|
||||
result: z.any(),
|
||||
});
|
||||
|
||||
export const AskHumanRequestEvent = BaseRunEvent.extend({
|
||||
type: z.literal("ask-human-request"),
|
||||
toolCallId: z.string(),
|
||||
query: z.string(),
|
||||
});
|
||||
|
||||
export const AskHumanResponseEvent = BaseRunEvent.extend({
|
||||
type: z.literal("ask-human-response"),
|
||||
toolCallId: z.string(),
|
||||
response: z.string(),
|
||||
});
|
||||
|
||||
export const ToolPermissionRequestEvent = BaseRunEvent.extend({
|
||||
type: z.literal("tool-permission-request"),
|
||||
toolCall: ToolCallPart,
|
||||
});
|
||||
|
||||
export const ToolPermissionResponseEvent = BaseRunEvent.extend({
|
||||
type: z.literal("tool-permission-response"),
|
||||
toolCallId: z.string(),
|
||||
response: z.enum(["approve", "deny"]),
|
||||
});
|
||||
|
||||
export const RunErrorEvent = BaseRunEvent.extend({
|
||||
type: z.literal("error"),
|
||||
error: z.string(),
|
||||
});
|
||||
|
||||
export const RunEvent = z.union([
|
||||
RunProcessingStartEvent,
|
||||
RunProcessingEndEvent,
|
||||
StartEvent,
|
||||
SpawnSubFlowEvent,
|
||||
LlmStreamEvent,
|
||||
MessageEvent,
|
||||
ToolInvocationEvent,
|
||||
ToolResultEvent,
|
||||
AskHumanRequestEvent,
|
||||
AskHumanResponseEvent,
|
||||
ToolPermissionRequestEvent,
|
||||
ToolPermissionResponseEvent,
|
||||
RunErrorEvent,
|
||||
]);
|
||||
|
||||
export const ToolPermissionAuthorizePayload = ToolPermissionResponseEvent.pick({
|
||||
subflow: true,
|
||||
toolCallId: true,
|
||||
response: true,
|
||||
});
|
||||
|
||||
export const AskHumanResponsePayload = AskHumanResponseEvent.pick({
|
||||
subflow: true,
|
||||
toolCallId: true,
|
||||
response: true,
|
||||
});
|
||||
|
||||
export const Run = z.object({
|
||||
id: z.string(),
|
||||
createdAt: z.iso.datetime(),
|
||||
agentId: z.string(),
|
||||
log: z.array(RunEvent),
|
||||
});
|
||||
|
||||
export const ListRunsResponse = z.object({
|
||||
runs: z.array(Run.pick({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
agentId: true,
|
||||
})),
|
||||
nextCursor: z.string().optional(),
|
||||
});
|
||||
|
||||
export const CreateRunOptions = Run.pick({
|
||||
agentId: true,
|
||||
});
|
||||
93
apps/x/packages/shared/src/workspace.ts
Normal file
93
apps/x/packages/shared/src/workspace.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
// ============================================================================
|
||||
// Workspace Filesystem Schema Definitions
|
||||
// ============================================================================
|
||||
|
||||
// All paths are workspace-relative POSIX strings
|
||||
export const RelPath = z.string().min(1);
|
||||
|
||||
export const NodeKind = z.enum(['file', 'dir']);
|
||||
|
||||
export const Encoding = z.enum(['utf8', 'base64', 'binary']);
|
||||
|
||||
export const Stat = z.object({
|
||||
kind: NodeKind,
|
||||
size: z.number().min(0),
|
||||
mtimeMs: z.number().min(0),
|
||||
ctimeMs: z.number().min(0),
|
||||
isSymlink: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const DirEntry = z.object({
|
||||
name: z.string(),
|
||||
path: RelPath,
|
||||
kind: NodeKind,
|
||||
stat: z
|
||||
.object({
|
||||
size: z.number().min(0),
|
||||
mtimeMs: z.number().min(0),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const ReaddirOptions = z.object({
|
||||
recursive: z.boolean().optional(),
|
||||
includeStats: z.boolean().optional(),
|
||||
includeHidden: z.boolean().optional(),
|
||||
allowedExtensions: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
export const ReadFileResult = z.object({
|
||||
path: RelPath,
|
||||
encoding: Encoding,
|
||||
data: z.string(),
|
||||
stat: Stat,
|
||||
etag: z.string(),
|
||||
});
|
||||
|
||||
export const WriteFileOptions = z.object({
|
||||
encoding: Encoding.optional(),
|
||||
atomic: z.boolean().optional(),
|
||||
mkdirp: z.boolean().optional(),
|
||||
expectedEtag: z.string().optional(),
|
||||
});
|
||||
|
||||
export const WriteFileResult = ReadFileResult.pick({
|
||||
path: true,
|
||||
stat: true,
|
||||
etag: true,
|
||||
});
|
||||
|
||||
export const RemoveOptions = z.object({
|
||||
recursive: z.boolean().optional(),
|
||||
trash: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const WorkspaceChangeEvent = z.discriminatedUnion('type', [
|
||||
z.object({
|
||||
type: z.literal('created'),
|
||||
path: RelPath,
|
||||
kind: NodeKind.optional(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('deleted'),
|
||||
path: RelPath,
|
||||
kind: NodeKind.optional(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('changed'),
|
||||
path: RelPath,
|
||||
kind: NodeKind.optional(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('moved'),
|
||||
from: RelPath,
|
||||
to: RelPath,
|
||||
kind: NodeKind.optional(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('bulkChanged'),
|
||||
paths: z.array(RelPath).optional(),
|
||||
}),
|
||||
]);
|
||||
Loading…
Add table
Add a link
Reference in a new issue