@xandreed/plugin-workspace 0.14.0 → 0.15.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/README.md +1 -1
- package/dist/bash-tool.adapter.js +10 -9
- package/dist/bash.adapter.d.ts +60 -8
- package/dist/bash.adapter.js +56 -13
- package/dist/file-system.adapter.d.ts +5 -1
- package/dist/file-system.adapter.js +1 -1
- package/dist/image-resizer.adapter.d.ts +5 -0
- package/dist/image-resizer.adapter.js +68 -0
- package/dist/image-resizer.entity.d.ts +23 -0
- package/dist/image-resizer.entity.functions.d.ts +15 -0
- package/dist/image-resizer.entity.functions.js +4 -0
- package/dist/image-resizer.entity.js +7 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -1
- package/dist/local-system.entity.d.ts +10 -3
- package/dist/local-system.entity.functions.d.ts +3 -2
- package/dist/local-system.entity.functions.js +16 -2
- package/dist/local-system.entity.js +3 -2
- package/dist/plugin.adapter.d.ts +103 -40
- package/dist/plugin.adapter.js +16 -5
- package/dist/read-tools.adapter.d.ts +10 -4
- package/dist/read-tools.adapter.js +40 -31
- package/dist/ripgrep.adapter.d.ts +74 -0
- package/dist/ripgrep.adapter.js +56 -0
- package/dist/ripgrep.entity.d.ts +63 -0
- package/dist/ripgrep.entity.functions.d.ts +87 -0
- package/dist/ripgrep.entity.functions.js +62 -0
- package/dist/ripgrep.entity.js +10 -0
- package/dist/shell.entity.functions.d.ts +2 -0
- package/dist/shell.entity.functions.js +1 -0
- package/dist/skills/skills-directory.adapter.d.ts +20 -14
- package/dist/skills/skills-directory.adapter.js +2 -2
- package/dist/skills/skills-directory.entity.d.ts +8 -5
- package/dist/skills/skills-directory.entity.functions.d.ts +58 -3
- package/dist/skills/skills-directory.entity.functions.js +5 -5
- package/dist/text-edit.entity.functions.js +3 -3
- package/dist/text-search.entity.functions.d.ts +7 -0
- package/dist/text-search.entity.functions.js +2 -2
- package/dist/workspace-path.entity.d.ts +21 -0
- package/dist/workspace-path.entity.functions.d.ts +39 -9
- package/dist/workspace-path.entity.functions.js +43 -18
- package/dist/workspace-path.entity.js +3 -2
- package/dist/workspace-tools.adapter.d.ts +48 -3
- package/dist/workspace-tools.adapter.js +23 -9
- package/dist/workspace-tools.entity.d.ts +136 -17
- package/dist/workspace-tools.entity.functions.d.ts +80 -5
- package/dist/workspace-tools.entity.functions.js +87 -31
- package/dist/workspace-tools.entity.js +26 -5
- package/dist/write-tools.adapter.js +10 -8
- package/package.json +6 -5
- package/dist/bash.adapter.test.d.ts +0 -1
- package/dist/file-system.adapter.test.d.ts +0 -1
- package/dist/local-system.entity.functions.test.d.ts +0 -1
- package/dist/plugin.adapter.test.d.ts +0 -1
- package/dist/skills/skills-directory.adapter.test.d.ts +0 -1
- package/dist/skills/skills-directory.entity.functions.test.d.ts +0 -1
- package/dist/text-edit.entity.functions.test.d.ts +0 -1
- package/dist/text-search.entity.functions.test.d.ts +0 -1
- package/dist/text-window.entity.functions.test.d.ts +0 -1
- package/dist/workspace-path.entity.functions.test.d.ts +0 -1
- package/dist/workspace-tools.adapter.test.d.ts +0 -1
- package/dist/workspace-tools.entity.functions.test.d.ts +0 -1
- package/dist/workspace-tools.journey.test.d.ts +0 -1
package/dist/plugin.adapter.d.ts
CHANGED
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
import { Layer } from "effect";
|
|
2
|
-
import { Bash, Capabilities, FileSystem } from "@xandreed/core";
|
|
2
|
+
import { Bash, Capabilities, FileSystem, ImageResizer } from "@xandreed/core";
|
|
3
3
|
/**
|
|
4
4
|
* The machine the agent runs on as its `FileSystem` and `Bash`: the local
|
|
5
5
|
* disk (`FileSystemLive`) and commands run with the configured shell
|
|
6
6
|
* (`BashLive`), built once per agent. Activation fails when the shell is not
|
|
7
|
-
* installed. Commands inherit the host process's environment
|
|
8
|
-
* `resolveBashEnv`'s allowlist.
|
|
7
|
+
* installed. Commands inherit `parentEnv` (the host process's environment
|
|
8
|
+
* when null) through `resolveBashEnv`'s allowlist.
|
|
9
9
|
*/
|
|
10
10
|
export declare const localSystemPlugin: import("@xandreed/core").TypedPlugin<{
|
|
11
11
|
readonly shell: string;
|
|
12
12
|
readonly killBackground: boolean;
|
|
13
13
|
readonly exitGraceMs: number;
|
|
14
|
+
readonly parentEnv: {
|
|
15
|
+
readonly [x: string]: string;
|
|
16
|
+
} | null;
|
|
14
17
|
}, {
|
|
15
18
|
readonly shell: string;
|
|
16
19
|
readonly killBackground: boolean;
|
|
17
20
|
readonly exitGraceMs: number;
|
|
21
|
+
readonly parentEnv: {
|
|
22
|
+
readonly [x: string]: string;
|
|
23
|
+
} | null;
|
|
18
24
|
}, FileSystem | Bash, import("@xandreed/core").BashError, never>;
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The local system as a typed layer: provides FileSystem and Bash; requires nothing.
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export declare const LocalSystemPluginLive: (options?: {
|
|
31
|
+
readonly shell?: string;
|
|
32
|
+
readonly killBackground?: boolean;
|
|
33
|
+
readonly exitGraceMs?: number;
|
|
34
|
+
readonly parentEnv?: {
|
|
35
|
+
readonly [x: string]: string | undefined;
|
|
36
|
+
} | null;
|
|
37
|
+
} | undefined) => Layer.Layer<FileSystem | Bash, import("@xandreed/core").HarnessError | import("@xandreed/core").BashError, never>;
|
|
25
38
|
/**
|
|
26
39
|
* The workspace tools (`workspaceTools`): read, ls, find and grep always on,
|
|
27
40
|
* write and edit behind `fs.write`, bash behind `shell.exec`, over the
|
|
@@ -32,9 +45,14 @@ export declare const LocalSystemPluginLive: (options?: Partial<{
|
|
|
32
45
|
* `PermissionGrants`.
|
|
33
46
|
*/
|
|
34
47
|
export declare const workspaceToolsPlugin: import("@xandreed/core").TypedPlugin<{
|
|
35
|
-
readonly tools: readonly ("
|
|
48
|
+
readonly tools: readonly ("find" | "write" | "read" | "grep" | "bash" | "ls" | "edit")[];
|
|
36
49
|
readonly always: readonly ("read" | "edit" | "shell")[];
|
|
37
50
|
readonly confine: boolean;
|
|
51
|
+
readonly workspaceAccess: "write" | "read";
|
|
52
|
+
readonly roots: readonly {
|
|
53
|
+
readonly path: string;
|
|
54
|
+
readonly access: "write" | "read";
|
|
55
|
+
}[];
|
|
38
56
|
readonly protect: readonly string[];
|
|
39
57
|
readonly exclude: readonly string[];
|
|
40
58
|
readonly maxLines: number;
|
|
@@ -43,8 +61,12 @@ export declare const workspaceToolsPlugin: import("@xandreed/core").TypedPlugin<
|
|
|
43
61
|
readonly findResults: number;
|
|
44
62
|
readonly grepMatches: number;
|
|
45
63
|
readonly grepLineChars: number;
|
|
64
|
+
readonly grepEngine: "auto" | "ripgrep" | "builtin";
|
|
65
|
+
readonly grepTimeoutMs: number;
|
|
66
|
+
readonly grepIgnoreFiles: boolean;
|
|
46
67
|
readonly maxFileBytes: number;
|
|
47
68
|
readonly readImages: boolean;
|
|
69
|
+
readonly imageMaxSide: number;
|
|
48
70
|
readonly fuzzyEdit: boolean;
|
|
49
71
|
readonly bashTimeoutMs: number;
|
|
50
72
|
readonly bashMaxTimeoutMs: number;
|
|
@@ -60,9 +82,14 @@ export declare const workspaceToolsPlugin: import("@xandreed/core").TypedPlugin<
|
|
|
60
82
|
readonly environmentSection: boolean;
|
|
61
83
|
readonly sectionOrder: number;
|
|
62
84
|
}, {
|
|
63
|
-
readonly tools: readonly ("
|
|
85
|
+
readonly tools: readonly ("find" | "write" | "read" | "grep" | "bash" | "ls" | "edit")[];
|
|
64
86
|
readonly always: readonly ("read" | "edit" | "shell")[];
|
|
65
87
|
readonly confine: boolean;
|
|
88
|
+
readonly workspaceAccess: "write" | "read";
|
|
89
|
+
readonly roots: readonly {
|
|
90
|
+
readonly path: string;
|
|
91
|
+
readonly access: "write" | "read";
|
|
92
|
+
}[];
|
|
66
93
|
readonly protect: readonly string[];
|
|
67
94
|
readonly exclude: readonly string[];
|
|
68
95
|
readonly maxLines: number;
|
|
@@ -71,8 +98,12 @@ export declare const workspaceToolsPlugin: import("@xandreed/core").TypedPlugin<
|
|
|
71
98
|
readonly findResults: number;
|
|
72
99
|
readonly grepMatches: number;
|
|
73
100
|
readonly grepLineChars: number;
|
|
101
|
+
readonly grepEngine: "auto" | "ripgrep" | "builtin";
|
|
102
|
+
readonly grepTimeoutMs: number;
|
|
103
|
+
readonly grepIgnoreFiles: boolean;
|
|
74
104
|
readonly maxFileBytes: number;
|
|
75
105
|
readonly readImages: boolean;
|
|
106
|
+
readonly imageMaxSide: number;
|
|
76
107
|
readonly fuzzyEdit: boolean;
|
|
77
108
|
readonly bashTimeoutMs: number;
|
|
78
109
|
readonly bashMaxTimeoutMs: number;
|
|
@@ -88,33 +119,65 @@ export declare const workspaceToolsPlugin: import("@xandreed/core").TypedPlugin<
|
|
|
88
119
|
readonly environmentSection: boolean;
|
|
89
120
|
readonly sectionOrder: number;
|
|
90
121
|
}, Capabilities, import("@xandreed/core").HarnessError, never>;
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
readonly
|
|
98
|
-
readonly
|
|
99
|
-
readonly
|
|
100
|
-
readonly
|
|
101
|
-
readonly
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
readonly
|
|
106
|
-
readonly
|
|
107
|
-
readonly
|
|
108
|
-
readonly
|
|
109
|
-
readonly
|
|
110
|
-
|
|
122
|
+
/**
|
|
123
|
+
* The workspace tools as a typed layer: contributes their capability; its handlers need FileSystem and SessionEnvironment (and Bash for bash) where the tools are opened.
|
|
124
|
+
*
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
export declare const WorkspaceToolsPluginLive: (options?: {
|
|
128
|
+
readonly tools?: readonly ("find" | "write" | "read" | "grep" | "bash" | "ls" | "edit")[];
|
|
129
|
+
readonly always?: readonly ("read" | "edit" | "shell")[];
|
|
130
|
+
readonly confine?: boolean;
|
|
131
|
+
readonly workspaceAccess?: "write" | "read";
|
|
132
|
+
readonly roots?: readonly {
|
|
133
|
+
readonly path: string;
|
|
134
|
+
readonly access: "write" | "read";
|
|
135
|
+
}[];
|
|
136
|
+
readonly protect?: readonly string[];
|
|
137
|
+
readonly exclude?: readonly string[];
|
|
138
|
+
readonly maxLines?: number;
|
|
139
|
+
readonly maxBytes?: number;
|
|
140
|
+
readonly lsEntries?: number;
|
|
141
|
+
readonly findResults?: number;
|
|
142
|
+
readonly grepMatches?: number;
|
|
143
|
+
readonly grepLineChars?: number;
|
|
144
|
+
readonly grepEngine?: "auto" | "ripgrep" | "builtin";
|
|
145
|
+
readonly grepTimeoutMs?: number;
|
|
146
|
+
readonly grepIgnoreFiles?: boolean;
|
|
147
|
+
readonly maxFileBytes?: number;
|
|
148
|
+
readonly readImages?: boolean;
|
|
149
|
+
readonly imageMaxSide?: number;
|
|
150
|
+
readonly fuzzyEdit?: boolean;
|
|
151
|
+
readonly bashTimeoutMs?: number;
|
|
152
|
+
readonly bashMaxTimeoutMs?: number;
|
|
153
|
+
readonly bashEnv?: {
|
|
154
|
+
readonly [x: string]: string | undefined;
|
|
111
155
|
};
|
|
112
|
-
readonly bashInheritEnv
|
|
113
|
-
readonly bashEnvAllow
|
|
114
|
-
readonly bashSessionEnv
|
|
115
|
-
readonly spillDir
|
|
116
|
-
readonly spillKeep
|
|
117
|
-
readonly progressMs
|
|
118
|
-
readonly environmentSection
|
|
119
|
-
readonly sectionOrder
|
|
120
|
-
}
|
|
156
|
+
readonly bashInheritEnv?: boolean;
|
|
157
|
+
readonly bashEnvAllow?: readonly string[];
|
|
158
|
+
readonly bashSessionEnv?: boolean;
|
|
159
|
+
readonly spillDir?: string | null;
|
|
160
|
+
readonly spillKeep?: number;
|
|
161
|
+
readonly progressMs?: number;
|
|
162
|
+
readonly environmentSection?: boolean;
|
|
163
|
+
readonly sectionOrder?: number;
|
|
164
|
+
} | undefined) => Layer.Layer<Capabilities, import("@xandreed/core").HarnessError, never>;
|
|
165
|
+
/** Optional CLI image fitting on the host's matching Bash and FileSystem. @public */
|
|
166
|
+
export declare const imageResizerPlugin: import("@xandreed/core").TypedPlugin<{
|
|
167
|
+
readonly tool: "auto" | "magick" | "convert" | "vips" | "sips";
|
|
168
|
+
readonly tempDir: string | null;
|
|
169
|
+
readonly timeoutMs: number;
|
|
170
|
+
readonly maxSourcePixels: number;
|
|
171
|
+
}, {
|
|
172
|
+
readonly tool: "auto" | "magick" | "convert" | "vips" | "sips";
|
|
173
|
+
readonly tempDir: string | null;
|
|
174
|
+
readonly timeoutMs: number;
|
|
175
|
+
readonly maxSourcePixels: number;
|
|
176
|
+
}, ImageResizer, import("@xandreed/core").HarnessError, FileSystem | Bash>;
|
|
177
|
+
/** @public */
|
|
178
|
+
export declare const ImageResizerPluginLive: (options?: {
|
|
179
|
+
readonly tool?: "auto" | "magick" | "convert" | "vips" | "sips";
|
|
180
|
+
readonly tempDir?: string | null;
|
|
181
|
+
readonly timeoutMs?: number;
|
|
182
|
+
readonly maxSourcePixels?: number;
|
|
183
|
+
} | undefined) => Layer.Layer<ImageResizer, import("@xandreed/core").HarnessError, FileSystem | Bash>;
|
package/dist/plugin.adapter.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
|
-
import { Bash, Capabilities, definePlugin, FileSystem, SessionEnvironment } from "@xandreed/core";
|
|
2
|
+
import { Bash, Capabilities, definePlugin, FileSystem, ImageResizer, SessionEnvironment } from "@xandreed/core";
|
|
3
3
|
import { BashLive } from "./bash.adapter.js";
|
|
4
4
|
import { FileSystemLive } from "./file-system.adapter.js";
|
|
5
5
|
import { LocalSystemConfig, localSystemDefaults } from "./local-system.entity.js";
|
|
6
|
+
import { ImageResizerCliLive } from "./image-resizer.adapter.js";
|
|
7
|
+
import { ImageResizerConfig, imageResizerDefaults } from "./image-resizer.entity.js";
|
|
6
8
|
import { workspaceTools } from "./workspace-tools.adapter.js";
|
|
7
9
|
import { WorkspaceToolsConfig, workspaceToolsDefaults } from "./workspace-tools.entity.js";
|
|
8
10
|
export const localSystemPlugin = definePlugin({
|
|
9
11
|
id: "@xandreed/plugin-workspace/local",
|
|
10
|
-
version: "0.
|
|
12
|
+
version: "0.15.1",
|
|
11
13
|
scope: "runtime",
|
|
12
14
|
config: LocalSystemConfig,
|
|
13
15
|
defaults: localSystemDefaults,
|
|
@@ -15,13 +17,22 @@ export const localSystemPlugin = definePlugin({
|
|
|
15
17
|
layer: (config) => Layer.merge(FileSystemLive, BashLive(config))
|
|
16
18
|
}), LocalSystemPluginLive = localSystemPlugin.live, workspaceToolsPlugin = definePlugin({
|
|
17
19
|
id: "@xandreed/plugin-workspace/tools",
|
|
18
|
-
version: "0.
|
|
20
|
+
version: "0.15.1",
|
|
19
21
|
scope: "runtime",
|
|
20
22
|
config: WorkspaceToolsConfig,
|
|
21
23
|
defaults: workspaceToolsDefaults,
|
|
22
24
|
requires: [FileSystem, SessionEnvironment],
|
|
23
|
-
optional: [Bash],
|
|
25
|
+
optional: [Bash, ImageResizer],
|
|
24
26
|
provides: [],
|
|
25
27
|
contributes: [Capabilities],
|
|
26
28
|
layer: (config) => Layer.effect(Capabilities, workspaceTools(config).pipe(Effect.map((capability) => [capability])))
|
|
27
|
-
}), WorkspaceToolsPluginLive = workspaceToolsPlugin.live
|
|
29
|
+
}), WorkspaceToolsPluginLive = workspaceToolsPlugin.live, imageResizerPlugin = definePlugin({
|
|
30
|
+
id: "@xandreed/plugin-workspace/images",
|
|
31
|
+
version: "0.15.1",
|
|
32
|
+
scope: "runtime",
|
|
33
|
+
config: ImageResizerConfig,
|
|
34
|
+
defaults: imageResizerDefaults,
|
|
35
|
+
requires: [FileSystem, Bash],
|
|
36
|
+
provides: [ImageResizer],
|
|
37
|
+
layer: (config) => ImageResizerCliLive(config)
|
|
38
|
+
}), ImageResizerPluginLive = imageResizerPlugin.live;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Path, RcMap, Semaphore } from "effect";
|
|
2
|
-
import { Failure } from "@xandreed/core";
|
|
2
|
+
import { Failure, FileSystem, SessionEnvironment } from "@xandreed/core";
|
|
3
3
|
import type { RegisteredTool, Subject } from "@xandreed/core";
|
|
4
4
|
import type { ProtectRule } from "./workspace-path.entity.functions.js";
|
|
5
5
|
import type { WorkspaceToolsConfig } from "./workspace-tools.entity.js";
|
|
@@ -9,14 +9,17 @@ export interface WorkspaceToolContext {
|
|
|
9
9
|
readonly path: Path.Path;
|
|
10
10
|
readonly protect: ReadonlyArray<ProtectRule>;
|
|
11
11
|
readonly locks: RcMap.RcMap<string, Semaphore.Semaphore>;
|
|
12
|
+
readonly resizesImages: boolean;
|
|
12
13
|
}
|
|
14
|
+
/** The services every workspace tool reads on each call, and the `workspace.environment` section on each render: the file system, and the session's workspace on it (`bash` reads `Bash` too). */
|
|
15
|
+
export declare const WORKSPACE_SERVICES: readonly [typeof FileSystem, typeof SessionEnvironment];
|
|
13
16
|
export declare const failure: (error: string, message: string) => Failure;
|
|
14
17
|
/** The refusal of a path that is neither a regular file nor a directory: reading a pipe, socket or device could wait forever. */
|
|
15
18
|
export declare const notAFile: (display: string, tool: string) => Failure;
|
|
16
19
|
/** A file the result names, for memory to keep track of; none for a name memory could not store as an id. */
|
|
17
20
|
export declare const fileSubjects: (path: string) => ReadonlyArray<Subject>;
|
|
18
21
|
/** `read`: a window of a text file's lines, an image (`readImages`), or what a binary file is. */
|
|
19
|
-
export declare const readTool: ({ config, path }: WorkspaceToolContext) => RegisteredTool;
|
|
22
|
+
export declare const readTool: ({ config, path, resizesImages }: WorkspaceToolContext) => RegisteredTool;
|
|
20
23
|
/** `ls`: a directory's entries by name, at most `lsEntries` of them. */
|
|
21
24
|
export declare const lsTool: ({ config, path }: WorkspaceToolContext) => RegisteredTool;
|
|
22
25
|
/** `find`: the files under a directory whose path matches a glob, as paths relative to the workspace root. */
|
|
@@ -26,8 +29,11 @@ export declare const findTool: ({ config, path }: WorkspaceToolContext) => Regis
|
|
|
26
29
|
* Files come from a walk that never enters links or excluded names; each is
|
|
27
30
|
* resolved again before it is read and skipped when it leads outside the
|
|
28
31
|
* workspace, is not a regular file, is larger than `maxFileBytes` or is
|
|
29
|
-
* binary
|
|
32
|
+
* binary; one the file system refuses (permission denied, say) is named in
|
|
33
|
+
* `unreadable` with the reason, not counted as skipped. Lines are cut to `grepLineChars` before matching. Searching a
|
|
30
34
|
* directory, one file gives at most a quarter of the limit, so one large
|
|
31
|
-
* file cannot fill the result
|
|
35
|
+
* file cannot fill the result, however many files the directory holds (as
|
|
36
|
+
* with ripgrep, which cannot know that ahead). A pattern with a line break
|
|
37
|
+
* is refused (`singleLinePattern`) whichever engine runs.
|
|
32
38
|
*/
|
|
33
39
|
export declare const grepTool: ({ config, path }: WorkspaceToolContext) => RegisteredTool;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { Tool } from "effect/ai";
|
|
2
2
|
import { Effect, Option, Result } from "effect";
|
|
3
|
-
import { defineTool, Failure } from "@xandreed/core";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
3
|
+
import { Bash, defineTool, Failure, FileSystem, ImageResizer, SessionEnvironment, SubjectId } from "@xandreed/core";
|
|
4
|
+
import { searchRipgrep } from "./ripgrep.adapter.js";
|
|
5
|
+
import { compilePattern, searchText, singleLinePattern } from "./text-search.entity.functions.js";
|
|
6
|
+
import { decodeText, formatBytes, lineWindow, looksBinary, mediaTypeOf } from "./text-window.entity.functions.js";
|
|
7
|
+
import { displayOf, excludesBelow, fsFailure, isWithin, openWorkspace, resolveTarget, withinAnyRoot } from "./workspace-path.entity.functions.js";
|
|
8
|
+
import { FindResult, GrepResult, LsResult, ReadResult } from "./workspace-tools.entity.js";
|
|
9
|
+
import { binaryRead, compactFind, fileTooLargeHint, compactGrep, compactLs, compactRead, readMedia, renderFind, renderGrep, renderLs, renderRead, toolDescriptions, toolParameters } from "./workspace-tools.entity.functions.js";
|
|
10
|
+
export const WORKSPACE_SERVICES = [FileSystem, SessionEnvironment], failure = (error, message) => ({ error, message }), notAFile = (display, tool) => failure("NotAFile", `${display} is not a regular file (a pipe, socket or device); ${tool} works on regular files only, use bash for it`);
|
|
10
11
|
const walkExcludes = (access, target, excludes) => excludesBelow(excludes, isWithin(access.root, target.path) ? Option.some(access.path.relative(access.root, target.path)) : Option.none());
|
|
11
|
-
export const fileSubjects = (path) => path.length > 0 && path.trim() === path ? [{ kind: "file", id: path, label: Option.none(), data: Option.none() }] : [];
|
|
12
|
-
const ReadTool = (
|
|
13
|
-
export const readTool = ({ config, path }) => defineTool({
|
|
14
|
-
tool: ReadTool(
|
|
12
|
+
export const fileSubjects = (path) => path.length > 0 && path.trim() === path ? [{ kind: "file", id: SubjectId.make(path), label: Option.none(), data: Option.none() }] : [];
|
|
13
|
+
const ReadTool = (config, resizesImages) => Tool.make("read", { description: toolDescriptions(config, resizesImages).read, parameters: toolParameters(config).read, success: ReadResult, failure: Failure, failureMode: "return" }), LsTool = (config) => Tool.make("ls", { description: toolDescriptions(config).ls, parameters: toolParameters(config).ls, success: LsResult, failure: Failure, failureMode: "return" }), FindTool = (config) => Tool.make("find", { description: toolDescriptions(config).find, parameters: toolParameters(config).find, success: FindResult, failure: Failure, failureMode: "return" }), GrepTool = (config) => Tool.make("grep", { description: toolDescriptions(config).grep, parameters: toolParameters(config).grep, success: GrepResult, failure: Failure, failureMode: "return" });
|
|
14
|
+
export const readTool = ({ config, path, resizesImages }) => defineTool({
|
|
15
|
+
tool: ReadTool(config, resizesImages),
|
|
16
|
+
requires: resizesImages ? [...WORKSPACE_SERVICES, ImageResizer] : WORKSPACE_SERVICES,
|
|
15
17
|
handler: (params) => Effect.gen(function* () {
|
|
16
|
-
const access = yield* openWorkspace(path), target = yield* resolveTarget(access, params.path, { confine: config.confine }), info = yield* access.fs.stat(target.path).pipe(Effect.mapError(fsFailure(target.display)));
|
|
18
|
+
const access = yield* openWorkspace(path, config), target = yield* resolveTarget(access, params.path, { confine: config.confine }), info = yield* access.fs.stat(target.path).pipe(Effect.mapError(fsFailure(target.display)));
|
|
17
19
|
if (info.kind === "directory")
|
|
18
20
|
return yield* Effect.fail(failure("IsDirectory", `${target.display} is a directory; use ls to list it`));
|
|
19
21
|
if (info.kind !== "file")
|
|
@@ -22,8 +24,8 @@ export const readTool = ({ config, path }) => defineTool({
|
|
|
22
24
|
return yield* Effect.fail(failure("FileTooLarge", `${target.display} is ${formatBytes(info.size)}, more than the ${formatBytes(config.maxFileBytes)} read loads; ${fileTooLargeHint(target.display, config.readImages)}`));
|
|
23
25
|
const bytes = yield* access.fs.readBytes(target.path).pipe(Effect.mapError(fsFailure(target.display)));
|
|
24
26
|
if (looksBinary(bytes))
|
|
25
|
-
return yield* binaryRead(target.display, bytes, config.readImages);
|
|
26
|
-
const decoded = decodeText(bytes), window = lineWindow(decoded.text, { offset: params.offset ?? 1, limit:
|
|
27
|
+
return yield* binaryRead(target.display, bytes, config.readImages, Option.getOrNull(mediaTypeOf(target.display)), resizesImages ? Option.map(yield* Effect.serviceOption(ImageResizer), (resizer) => resizer.fit) : Option.none(), config.imageMaxSide);
|
|
28
|
+
const decoded = decodeText(bytes), window = lineWindow(decoded.text, { offset: params.offset ?? 1, limit: params.limit ?? config.maxLines, maxBytes: config.maxBytes });
|
|
27
29
|
return yield* Result.match(window, {
|
|
28
30
|
onFailure: (total) => Effect.fail(failure("OffsetPastEnd", `${target.display} has ${total} line${total === 1 ? "" : "s"}; offset must be at most ${Math.max(1, total)}`)),
|
|
29
31
|
onSuccess: (lines) => Effect.succeed({
|
|
@@ -40,61 +42,68 @@ export const readTool = ({ config, path }) => defineTool({
|
|
|
40
42
|
})
|
|
41
43
|
});
|
|
42
44
|
}),
|
|
43
|
-
view: { version: "
|
|
45
|
+
view: { version: "3", render: renderRead, compact: compactRead, subjects: (result) => fileSubjects(result.path), media: readMedia },
|
|
44
46
|
annotations: { readOnly: !0 }
|
|
45
47
|
}), lsTool = ({ config, path }) => defineTool({
|
|
46
|
-
tool: LsTool(
|
|
48
|
+
tool: LsTool(config),
|
|
49
|
+
requires: WORKSPACE_SERVICES,
|
|
47
50
|
handler: (params) => Effect.gen(function* () {
|
|
48
|
-
const access = yield* openWorkspace(path), target = yield* resolveTarget(access, params.path ?? ".", { confine: config.confine }), entries = yield* access.fs.list(target.path).pipe(Effect.mapError((error) => error.reason === "NotDirectory" ? failure("NotDirectory", `${target.display} is not a directory; use read to see a file`) : fsFailure(target.display)(error))), limit =
|
|
51
|
+
const access = yield* openWorkspace(path, config), target = yield* resolveTarget(access, params.path ?? ".", { confine: config.confine }), entries = yield* access.fs.list(target.path).pipe(Effect.mapError((error) => error.reason === "NotDirectory" ? failure("NotDirectory", `${target.display} is not a directory; use read to see a file`) : fsFailure(target.display)(error))), limit = params.limit ?? config.lsEntries;
|
|
49
52
|
return { path: target.display, entries: entries.slice(0, limit).map((entry) => ({ name: entry.name, kind: entry.kind })), total: entries.length };
|
|
50
53
|
}),
|
|
51
|
-
view: { version: "
|
|
54
|
+
view: { version: "2", render: renderLs, compact: compactLs },
|
|
52
55
|
annotations: { readOnly: !0 }
|
|
53
56
|
}), findTool = ({ config, path }) => defineTool({
|
|
54
|
-
tool: FindTool(
|
|
57
|
+
tool: FindTool(config),
|
|
58
|
+
requires: WORKSPACE_SERVICES,
|
|
55
59
|
handler: (params) => Effect.gen(function* () {
|
|
56
|
-
const access = yield* openWorkspace(path), target = yield* resolveTarget(access, params.path ?? ".", { confine: config.confine }), limit =
|
|
60
|
+
const access = yield* openWorkspace(path, config), target = yield* resolveTarget(access, params.path ?? ".", { confine: config.confine }), limit = params.limit ?? config.findResults, found = yield* access.fs.glob(params.pattern, { cwd: target.path, exclude: walkExcludes(access, target, config.exclude), dot: !0, onlyFiles: !0, limit }).pipe(Effect.mapError((error) => error.reason === "Invalid" ? failure("InvalidPattern", `${params.pattern}: ${error.message}`) : fsFailure(target.display)(error)));
|
|
57
61
|
return { pattern: params.pattern, path: target.display, paths: found.paths.map((relative) => displayOf(access, access.path.join(target.path, relative))), truncated: found.truncated };
|
|
58
62
|
}),
|
|
59
|
-
view: { version: "
|
|
63
|
+
view: { version: "2", render: (result) => renderFind(result, config.maxLines, config.maxBytes), compact: compactFind },
|
|
60
64
|
annotations: { readOnly: !0 }
|
|
61
65
|
}), grepTool = ({ config, path }) => defineTool({
|
|
62
|
-
tool: GrepTool(
|
|
66
|
+
tool: GrepTool(config),
|
|
67
|
+
requires: config.grepEngine === "ripgrep" ? [...WORKSPACE_SERVICES, Bash] : WORKSPACE_SERVICES,
|
|
63
68
|
handler: (params) => Effect.gen(function* () {
|
|
64
|
-
const access = yield* openWorkspace(path), target = yield* resolveTarget(access, params.path ?? ".", { confine: config.confine })
|
|
69
|
+
const access = yield* openWorkspace(path, config), target = yield* resolveTarget(access, params.path ?? ".", { confine: config.confine });
|
|
70
|
+
yield* Effect.fromResult(singleLinePattern(params.pattern));
|
|
71
|
+
const pattern = config.grepEngine === "ripgrep" ? Option.none() : Option.some(yield* Effect.fromResult(compilePattern(params.pattern, { literal: params.literal ?? !1, ignoreCase: params.ignoreCase ?? !1 }))), info = yield* access.fs.stat(target.path).pipe(Effect.mapError(fsFailure(target.display)));
|
|
65
72
|
if (info.kind !== "file" && info.kind !== "directory")
|
|
66
73
|
return yield* Effect.fail(notAFile(target.display, "grep"));
|
|
74
|
+
if (config.grepEngine === "ripgrep")
|
|
75
|
+
return yield* searchRipgrep(access, target, info.kind === "directory", params, config);
|
|
67
76
|
const glob = params.glob === void 0 ? "**/*" : params.glob.includes("/") ? params.glob : `**/${params.glob}`, files = info.kind === "file" ? [{ path: target.path, display: target.display }] : yield* access.fs.glob(glob, { cwd: target.path, exclude: walkExcludes(access, target, config.exclude), dot: !0, onlyFiles: !0 }).pipe(Effect.map((found) => found.paths.map((relative) => {
|
|
68
77
|
const absolute = access.path.join(target.path, relative);
|
|
69
78
|
return { path: absolute, display: displayOf(access, absolute) };
|
|
70
|
-
})), Effect.mapError((error) => error.reason === "Invalid" ? failure("InvalidPattern", `glob ${glob}: ${error.message}`) : fsFailure(target.display)(error))), limit =
|
|
79
|
+
})), Effect.mapError((error) => error.reason === "Invalid" ? failure("InvalidPattern", `glob ${params.glob ?? glob}: ${error.message}`) : fsFailure(target.display)(error))), limit = params.limit ?? config.grepMatches, perFile = info.kind === "directory" ? Math.max(1, Math.ceil(limit / 4)) : limit, context = params.context ?? 0, skip = (state) => ({ ...state, skipped: state.skipped + 1 }), searched = yield* Effect.reduce(files, () => ({ lines: [], matches: 0, files: 0, skipped: 0, truncated: !1, unreadable: [] }), (state, file) => {
|
|
71
80
|
if (state.matches >= limit)
|
|
72
81
|
return Effect.succeed({ ...state, truncated: !0 });
|
|
73
82
|
return Effect.gen(function* () {
|
|
74
83
|
const real = yield* access.fs.realPath(file.path);
|
|
75
|
-
if (config.confine && !
|
|
84
|
+
if (config.confine && !withinAnyRoot(access, real))
|
|
76
85
|
return Option.none();
|
|
77
86
|
const found = yield* access.fs.stat(real);
|
|
78
87
|
if (found.kind !== "file" || found.size > config.maxFileBytes)
|
|
79
88
|
return Option.none();
|
|
80
89
|
const bytes = yield* access.fs.readBytes(real);
|
|
81
90
|
return looksBinary(bytes) ? Option.none() : Option.some(bytes);
|
|
82
|
-
}).pipe(Effect.
|
|
91
|
+
}).pipe(Effect.map(Option.match({
|
|
83
92
|
onNone: () => skip(state),
|
|
84
93
|
onSome: (content) => {
|
|
85
|
-
const found = searchText(decodeText(content).text, pattern, { lineChars: config.grepLineChars, context, cap: Math.min(perFile, limit - state.matches) });
|
|
94
|
+
const found = searchText(decodeText(content).text, Option.getOrThrow(pattern), { lineChars: config.grepLineChars, context, cap: Math.min(perFile, limit - state.matches) });
|
|
86
95
|
return {
|
|
96
|
+
...state,
|
|
87
97
|
lines: [...state.lines, ...found.lines.map((line) => ({ path: file.display, ...line }))],
|
|
88
98
|
matches: state.matches + found.matches,
|
|
89
99
|
files: state.files + (found.matches > 0 ? 1 : 0),
|
|
90
|
-
skipped: state.skipped,
|
|
91
100
|
truncated: state.truncated || found.capped
|
|
92
101
|
};
|
|
93
102
|
}
|
|
94
|
-
})));
|
|
103
|
+
})), Effect.catch((error) => Effect.succeed({ ...state, unreadable: [...state.unreadable, { path: file.display, reason: error.reason }] })));
|
|
95
104
|
});
|
|
96
|
-
return { pattern: params.pattern, path: target.display, ...searched };
|
|
105
|
+
return { pattern: params.pattern, path: target.display, ...searched, timedOut: !1 };
|
|
97
106
|
}),
|
|
98
|
-
view: { version: "
|
|
107
|
+
view: { version: "4", render: (result, params) => renderGrep(result, (params.context ?? 0) > 0, config.maxLines, config.maxBytes), compact: compactGrep },
|
|
99
108
|
annotations: { readOnly: !0 }
|
|
100
109
|
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Bash } from "@xandreed/core";
|
|
3
|
+
import type { WorkspaceAccess } from "./workspace-path.entity.functions.js";
|
|
4
|
+
/** A bounded, interruptible search on the same machine as the Bash/FileSystem pair. */
|
|
5
|
+
export declare const searchRipgrep: (access: WorkspaceAccess, target: {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly display: string;
|
|
8
|
+
}, directory: boolean, params: {
|
|
9
|
+
readonly pattern: string;
|
|
10
|
+
readonly path?: string;
|
|
11
|
+
readonly limit?: number;
|
|
12
|
+
readonly context?: number;
|
|
13
|
+
readonly literal?: boolean;
|
|
14
|
+
readonly glob?: string;
|
|
15
|
+
readonly ignoreCase?: boolean;
|
|
16
|
+
}, config: {
|
|
17
|
+
readonly tools: readonly ("find" | "write" | "read" | "grep" | "bash" | "ls" | "edit")[];
|
|
18
|
+
readonly always: readonly ("read" | "edit" | "shell")[];
|
|
19
|
+
readonly confine: boolean;
|
|
20
|
+
readonly workspaceAccess: "write" | "read";
|
|
21
|
+
readonly roots: readonly {
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly access: "write" | "read";
|
|
24
|
+
}[];
|
|
25
|
+
readonly protect: readonly string[];
|
|
26
|
+
readonly exclude: readonly string[];
|
|
27
|
+
readonly maxLines: number;
|
|
28
|
+
readonly maxBytes: number;
|
|
29
|
+
readonly lsEntries: number;
|
|
30
|
+
readonly findResults: number;
|
|
31
|
+
readonly grepMatches: number;
|
|
32
|
+
readonly grepLineChars: number;
|
|
33
|
+
readonly grepEngine: "auto" | "ripgrep" | "builtin";
|
|
34
|
+
readonly grepTimeoutMs: number;
|
|
35
|
+
readonly grepIgnoreFiles: boolean;
|
|
36
|
+
readonly maxFileBytes: number;
|
|
37
|
+
readonly readImages: boolean;
|
|
38
|
+
readonly imageMaxSide: number;
|
|
39
|
+
readonly fuzzyEdit: boolean;
|
|
40
|
+
readonly bashTimeoutMs: number;
|
|
41
|
+
readonly bashMaxTimeoutMs: number;
|
|
42
|
+
readonly bashEnv: {
|
|
43
|
+
readonly [x: string]: string;
|
|
44
|
+
};
|
|
45
|
+
readonly bashInheritEnv: boolean;
|
|
46
|
+
readonly bashEnvAllow: readonly string[];
|
|
47
|
+
readonly bashSessionEnv: boolean;
|
|
48
|
+
readonly spillDir: string | null;
|
|
49
|
+
readonly spillKeep: number;
|
|
50
|
+
readonly progressMs: number;
|
|
51
|
+
readonly environmentSection: boolean;
|
|
52
|
+
readonly sectionOrder: number;
|
|
53
|
+
}) => Effect.Effect<{
|
|
54
|
+
pattern: string;
|
|
55
|
+
path: string;
|
|
56
|
+
lines: readonly {
|
|
57
|
+
readonly path: string;
|
|
58
|
+
readonly line: number;
|
|
59
|
+
readonly text: string;
|
|
60
|
+
readonly match: boolean;
|
|
61
|
+
}[];
|
|
62
|
+
matches: number;
|
|
63
|
+
files: number;
|
|
64
|
+
skipped: number;
|
|
65
|
+
truncated: boolean;
|
|
66
|
+
unreadable: {
|
|
67
|
+
path: string;
|
|
68
|
+
reason: "Invalid" | "NotFound" | "Exists" | "IsDirectory" | "NotDirectory" | "NotEmpty" | "Permission" | "Io";
|
|
69
|
+
}[];
|
|
70
|
+
timedOut: boolean;
|
|
71
|
+
}, {
|
|
72
|
+
readonly error: string;
|
|
73
|
+
readonly message: string;
|
|
74
|
+
}, Bash>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Effect, Option, Stream } from "effect";
|
|
2
|
+
import { Bash, RunContext } from "@xandreed/core";
|
|
3
|
+
import { rgReasonOf } from "./local-system.entity.functions.js";
|
|
4
|
+
import { decodeRgMessage, emptyRgSearch, foldRgMessage, rgLines, rgRecords, ripgrepCommand } from "./ripgrep.entity.functions.js";
|
|
5
|
+
import { clipHead } from "./text-window.entity.functions.js";
|
|
6
|
+
import { displayOf, excludesBelow, isWithin } from "./workspace-path.entity.functions.js";
|
|
7
|
+
import { bashEnvironment } from "./workspace-tools.entity.functions.js";
|
|
8
|
+
const empty = { search: emptyRgSearch, buffer: "", stderr: "", exit: Option.none(), invalid: !1 }, failure = (error, message) => ({ error, message });
|
|
9
|
+
export const searchRipgrep = Effect.fn("workspace.grep")(function* (access, target, directory, params, config) {
|
|
10
|
+
const bash = yield* Bash, run = yield* Effect.serviceOption(RunContext), cwd = directory ? target.path : access.path.dirname(target.path), input = directory ? "." : access.path.basename(target.path), exclude = excludesBelow(config.exclude, isWithin(access.root, cwd) ? Option.some(access.path.relative(access.root, cwd)) : Option.none()), limit = params.limit ?? config.grepMatches, perFile = directory ? Math.max(1, Math.ceil(limit / 4)) : limit, options = { limit, perFile, directory, lineChars: config.grepLineChars, display: (path) => displayOf(access, access.path.resolve(cwd, path)) }, fold = (state, event) => {
|
|
11
|
+
if (event._tag === "BashExit")
|
|
12
|
+
return { ...state, exit: Option.some(event) };
|
|
13
|
+
if (event.stream === "stderr")
|
|
14
|
+
return { ...state, stderr: clipHead(state.stderr + event.text, 16384) };
|
|
15
|
+
const complete = state.buffer + event.text;
|
|
16
|
+
if (complete.split(`
|
|
17
|
+
`).some((line) => line.length > config.maxFileBytes * 6 + 65536))
|
|
18
|
+
return { ...state, invalid: !0 };
|
|
19
|
+
const parsed = rgRecords("", complete);
|
|
20
|
+
return { ...state, buffer: parsed.buffer, search: parsed.records.reduce((search, message) => search.stopped ? search : foldRgMessage(search, message, options), state.search) };
|
|
21
|
+
}, output = yield* bash.stream(ripgrepCommand(config, params, input, perFile, exclude), {
|
|
22
|
+
cwd,
|
|
23
|
+
stdin: params.pattern.length === 0 ? `
|
|
24
|
+
` : params.pattern,
|
|
25
|
+
timeoutMs: config.grepTimeoutMs,
|
|
26
|
+
...bashEnvironment(config, Option.map(run, (current) => ({ sessionId: String(current.session.id), runId: current.runId, workspace: access.root })))
|
|
27
|
+
}).pipe(Stream.mapAccum(() => empty, (state, event) => {
|
|
28
|
+
const next = fold(state, event);
|
|
29
|
+
return [next, [next]];
|
|
30
|
+
}), Stream.takeUntil((state) => state.search.stopped || state.invalid), Stream.runFold(() => empty, (_previous, state) => state), Effect.mapError((error) => failure(error.reason, error.message))), search = Option.match(decodeRgMessage(output.buffer), {
|
|
31
|
+
onNone: () => output.search,
|
|
32
|
+
onSome: (message) => foldRgMessage(output.search, message, options)
|
|
33
|
+
});
|
|
34
|
+
if (output.invalid)
|
|
35
|
+
return yield* Effect.fail(failure("SearchFailed", "The search output was too large to read; narrow the path or the pattern and search again"));
|
|
36
|
+
const timedOut = Option.exists(output.exit, (exit) => exit.termination === "timeout"), unreadable = output.stderr.split(`
|
|
37
|
+
`).flatMap((line) => {
|
|
38
|
+
const found = /^rg: (.*): .*\(os error (\d+)\)$/.exec(line);
|
|
39
|
+
return found === null ? [] : [{ path: options.display(found[1] ?? ""), reason: rgReasonOf(Number(found[2])) }];
|
|
40
|
+
});
|
|
41
|
+
if (Option.exists(output.exit, (exit) => Option.contains(exit.exitCode, 2)) && search.messages === 0 && unreadable.length === 0)
|
|
42
|
+
return yield* Effect.fail(failure("InvalidPattern", `The search pattern or glob could not be parsed: ${output.stderr.trim()}. Pass literal: true to search for the pattern as text`));
|
|
43
|
+
if (Option.exists(output.exit, (exit) => exit.termination === "signal" || exit.termination === "exit" && !Option.exists(exit.exitCode, (code) => code === 0 || code === 1 || code === 2)))
|
|
44
|
+
return yield* Effect.fail(failure("SearchFailed", "The file search could not finish; try a narrower path or tell the user the search is unavailable"));
|
|
45
|
+
return {
|
|
46
|
+
pattern: params.pattern,
|
|
47
|
+
path: target.display,
|
|
48
|
+
lines: rgLines(search, params.context ?? 0),
|
|
49
|
+
matches: [...search.counts.values()].reduce((sum, count) => sum + count, 0),
|
|
50
|
+
files: search.counts.size,
|
|
51
|
+
skipped: search.skipped,
|
|
52
|
+
truncated: search.truncated,
|
|
53
|
+
unreadable,
|
|
54
|
+
timedOut
|
|
55
|
+
};
|
|
56
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** ripgrep represents non-UTF-8 paths and lines as base64 bytes. */
|
|
3
|
+
export declare const RgText: Schema.Union<readonly [Schema.Struct<{
|
|
4
|
+
readonly text: Schema.String;
|
|
5
|
+
}>, Schema.Struct<{
|
|
6
|
+
readonly bytes: Schema.StringFromBase64;
|
|
7
|
+
}>]>;
|
|
8
|
+
export type RgText = typeof RgText.Type;
|
|
9
|
+
/** Only the fields the workspace search consumes from ripgrep's JSON stream. */
|
|
10
|
+
export declare const RgMessage: Schema.toTaggedUnion<"type", readonly [Schema.Struct<{
|
|
11
|
+
readonly type: Schema.Literal<"begin">;
|
|
12
|
+
readonly data: Schema.Struct<{
|
|
13
|
+
readonly path: Schema.Union<readonly [Schema.Struct<{
|
|
14
|
+
readonly text: Schema.String;
|
|
15
|
+
}>, Schema.Struct<{
|
|
16
|
+
readonly bytes: Schema.StringFromBase64;
|
|
17
|
+
}>]>;
|
|
18
|
+
}>;
|
|
19
|
+
}>, Schema.Struct<{
|
|
20
|
+
readonly type: Schema.Literal<"match">;
|
|
21
|
+
readonly data: Schema.Struct<{
|
|
22
|
+
readonly path: Schema.Union<readonly [Schema.Struct<{
|
|
23
|
+
readonly text: Schema.String;
|
|
24
|
+
}>, Schema.Struct<{
|
|
25
|
+
readonly bytes: Schema.StringFromBase64;
|
|
26
|
+
}>]>;
|
|
27
|
+
readonly lines: Schema.Union<readonly [Schema.Struct<{
|
|
28
|
+
readonly text: Schema.String;
|
|
29
|
+
}>, Schema.Struct<{
|
|
30
|
+
readonly bytes: Schema.StringFromBase64;
|
|
31
|
+
}>]>;
|
|
32
|
+
readonly line_number: Schema.Int;
|
|
33
|
+
}>;
|
|
34
|
+
}>, Schema.Struct<{
|
|
35
|
+
readonly type: Schema.Literal<"context">;
|
|
36
|
+
readonly data: Schema.Struct<{
|
|
37
|
+
readonly path: Schema.Union<readonly [Schema.Struct<{
|
|
38
|
+
readonly text: Schema.String;
|
|
39
|
+
}>, Schema.Struct<{
|
|
40
|
+
readonly bytes: Schema.StringFromBase64;
|
|
41
|
+
}>]>;
|
|
42
|
+
readonly lines: Schema.Union<readonly [Schema.Struct<{
|
|
43
|
+
readonly text: Schema.String;
|
|
44
|
+
}>, Schema.Struct<{
|
|
45
|
+
readonly bytes: Schema.StringFromBase64;
|
|
46
|
+
}>]>;
|
|
47
|
+
readonly line_number: Schema.Int;
|
|
48
|
+
}>;
|
|
49
|
+
}>, Schema.Struct<{
|
|
50
|
+
readonly type: Schema.Literal<"end">;
|
|
51
|
+
readonly data: Schema.Struct<{
|
|
52
|
+
readonly path: Schema.Union<readonly [Schema.Struct<{
|
|
53
|
+
readonly text: Schema.String;
|
|
54
|
+
}>, Schema.Struct<{
|
|
55
|
+
readonly bytes: Schema.StringFromBase64;
|
|
56
|
+
}>]>;
|
|
57
|
+
readonly binary_offset: Schema.NullOr<Schema.Int>;
|
|
58
|
+
}>;
|
|
59
|
+
}>, Schema.Struct<{
|
|
60
|
+
readonly type: Schema.Literal<"summary">;
|
|
61
|
+
readonly data: Schema.Struct<{}>;
|
|
62
|
+
}>]>;
|
|
63
|
+
export type RgMessage = typeof RgMessage.Type;
|