@abacus-ai/cli 2.0.0-canary.1 → 2.0.0-canary.3
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/dist/index.mjs +450 -422
- package/package.json +4 -1
- package/.oxlintrc.json +0 -8
- package/resources/abacus.ico +0 -0
- package/resources/entitlements.plist +0 -9
- package/src/__e2e__/README.md +0 -196
- package/src/__e2e__/agent-interactions.e2e.test.tsx +0 -61
- package/src/__e2e__/cli-commands.e2e.test.tsx +0 -77
- package/src/__e2e__/conversation-throttle.e2e.test.ts +0 -453
- package/src/__e2e__/conversation.e2e.test.tsx +0 -56
- package/src/__e2e__/diff-preview.e2e.test.tsx +0 -3399
- package/src/__e2e__/file-creation.e2e.test.tsx +0 -149
- package/src/__e2e__/helpers/test-helpers.ts +0 -449
- package/src/__e2e__/keyboard-navigation.e2e.test.tsx +0 -34
- package/src/__e2e__/llm-models.e2e.test.ts +0 -402
- package/src/__e2e__/mcp/mcp-callback-flow.e2e.test.tsx +0 -71
- package/src/__e2e__/mcp/mcp-full-app-ui.e2e.test.tsx +0 -167
- package/src/__e2e__/mcp/mcp-ui-rendering.e2e.test.tsx +0 -185
- package/src/__e2e__/repl.e2e.test.tsx +0 -78
- package/src/__e2e__/shell-compatibility.e2e.test.tsx +0 -76
- package/src/__e2e__/theme-mcp.e2e.test.tsx +0 -98
- package/src/__e2e__/tool-permissions.e2e.test.tsx +0 -66
- package/src/args.ts +0 -22
- package/src/components/__tests__/react-compiler.test.tsx +0 -78
- package/src/components/__tests__/status-indicator.test.tsx +0 -403
- package/src/components/composer/__tests__/bash-runner.test.tsx +0 -263
- package/src/components/composer/agent-mode-indicator.tsx +0 -63
- package/src/components/composer/bash-runner.tsx +0 -54
- package/src/components/composer/commands/default-commands.tsx +0 -615
- package/src/components/composer/commands/handler.tsx +0 -59
- package/src/components/composer/commands/picker.tsx +0 -273
- package/src/components/composer/commands/registry.ts +0 -233
- package/src/components/composer/commands/types.ts +0 -33
- package/src/components/composer/context.tsx +0 -88
- package/src/components/composer/file-mention-picker.tsx +0 -83
- package/src/components/composer/help.tsx +0 -44
- package/src/components/composer/index.tsx +0 -1007
- package/src/components/composer/mentions.ts +0 -57
- package/src/components/composer/message-queue.tsx +0 -70
- package/src/components/composer/mode-panel.tsx +0 -35
- package/src/components/composer/modes/__tests__/bash-handler.test.tsx +0 -755
- package/src/components/composer/modes/__tests__/bash-renderer.test.tsx +0 -1108
- package/src/components/composer/modes/bash-handler.tsx +0 -132
- package/src/components/composer/modes/bash-renderer.tsx +0 -175
- package/src/components/composer/modes/default-handlers.tsx +0 -33
- package/src/components/composer/modes/index.ts +0 -41
- package/src/components/composer/modes/types.ts +0 -21
- package/src/components/composer/persistent-shell.ts +0 -283
- package/src/components/composer/process.ts +0 -65
- package/src/components/composer/types.ts +0 -9
- package/src/components/composer/use-mention-search.ts +0 -68
- package/src/components/error-boundry.tsx +0 -60
- package/src/components/exit-message.tsx +0 -29
- package/src/components/expanded-view.tsx +0 -74
- package/src/components/file-completion.tsx +0 -127
- package/src/components/header.tsx +0 -47
- package/src/components/logo.tsx +0 -37
- package/src/components/segments.tsx +0 -356
- package/src/components/status-indicator.tsx +0 -306
- package/src/components/tool-group-summary.tsx +0 -263
- package/src/components/tool-permissions/ask-user-question-permission-ui.tsx +0 -319
- package/src/components/tool-permissions/diff-preview.tsx +0 -359
- package/src/components/tool-permissions/index.ts +0 -5
- package/src/components/tool-permissions/permission-options.tsx +0 -401
- package/src/components/tool-permissions/permission-preview-header.tsx +0 -57
- package/src/components/tool-permissions/tool-permission-ui.tsx +0 -420
- package/src/components/tools/agent/ask-user-question.tsx +0 -107
- package/src/components/tools/agent/enter-plan-mode.tsx +0 -55
- package/src/components/tools/agent/exit-plan-mode.tsx +0 -83
- package/src/components/tools/agent/handoff-to-main.tsx +0 -27
- package/src/components/tools/agent/subagent.tsx +0 -37
- package/src/components/tools/agent/todo-write.tsx +0 -104
- package/src/components/tools/browser/close-tab.tsx +0 -58
- package/src/components/tools/browser/computer.tsx +0 -70
- package/src/components/tools/browser/get-interactive-elements.tsx +0 -54
- package/src/components/tools/browser/get-tab-content.tsx +0 -51
- package/src/components/tools/browser/navigate-to.tsx +0 -59
- package/src/components/tools/browser/new-tab.tsx +0 -60
- package/src/components/tools/browser/perform-action.tsx +0 -63
- package/src/components/tools/browser/refresh-tab.tsx +0 -43
- package/src/components/tools/browser/switch-tab.tsx +0 -58
- package/src/components/tools/filesystem/delete-file.tsx +0 -104
- package/src/components/tools/filesystem/edit.tsx +0 -220
- package/src/components/tools/filesystem/list-dir.tsx +0 -78
- package/src/components/tools/filesystem/read-file.tsx +0 -180
- package/src/components/tools/filesystem/upload-image.tsx +0 -76
- package/src/components/tools/ide/ide-diagnostics.tsx +0 -62
- package/src/components/tools/index.ts +0 -91
- package/src/components/tools/mcp/mcp-tool.tsx +0 -158
- package/src/components/tools/search/fetch-url.tsx +0 -73
- package/src/components/tools/search/file-search.tsx +0 -78
- package/src/components/tools/search/grep.tsx +0 -90
- package/src/components/tools/search/semantic-search.tsx +0 -66
- package/src/components/tools/search/web-search.tsx +0 -71
- package/src/components/tools/shared/index.tsx +0 -48
- package/src/components/tools/shared/zod-coercion.ts +0 -35
- package/src/components/tools/terminal/bash-tool-output.tsx +0 -188
- package/src/components/tools/terminal/get-terminal-output.tsx +0 -91
- package/src/components/tools/terminal/run-in-terminal.tsx +0 -131
- package/src/components/tools/types.ts +0 -16
- package/src/components/tools.tsx +0 -68
- package/src/components/ui/__tests__/divider.test.tsx +0 -61
- package/src/components/ui/__tests__/gradient.test.tsx +0 -125
- package/src/components/ui/__tests__/input.test.tsx +0 -166
- package/src/components/ui/__tests__/select.test.tsx +0 -273
- package/src/components/ui/__tests__/shimmer.test.tsx +0 -99
- package/src/components/ui/blinking-indicator.tsx +0 -27
- package/src/components/ui/divider.tsx +0 -162
- package/src/components/ui/gradient.tsx +0 -56
- package/src/components/ui/input.tsx +0 -228
- package/src/components/ui/select.tsx +0 -151
- package/src/components/ui/shimmer.tsx +0 -76
- package/src/context/agent-mode.tsx +0 -95
- package/src/context/extension-file.tsx +0 -136
- package/src/context/network-activity.tsx +0 -45
- package/src/context/notification.tsx +0 -62
- package/src/context/shell-size.tsx +0 -49
- package/src/context/shell-title.tsx +0 -38
- package/src/entrypoints/print-mode.ts +0 -312
- package/src/entrypoints/repl.tsx +0 -389
- package/src/hooks/use-agent.ts +0 -15
- package/src/hooks/use-api-client.ts +0 -1
- package/src/hooks/use-available-height.ts +0 -8
- package/src/hooks/use-cleanup.ts +0 -29
- package/src/hooks/use-interrupt-manager.ts +0 -242
- package/src/hooks/use-models.ts +0 -22
- package/src/index.ts +0 -217
- package/src/lib/__tests__/ansi.test.ts +0 -255
- package/src/lib/__tests__/cli.test.ts +0 -122
- package/src/lib/__tests__/commands.test.ts +0 -325
- package/src/lib/__tests__/constants.test.ts +0 -15
- package/src/lib/__tests__/focusables.test.ts +0 -25
- package/src/lib/__tests__/fs.test.ts +0 -231
- package/src/lib/__tests__/markdown.test.tsx +0 -348
- package/src/lib/__tests__/mcpCommandHandler.test.ts +0 -173
- package/src/lib/__tests__/mcpManagement.test.ts +0 -38
- package/src/lib/__tests__/path-paste.test.ts +0 -144
- package/src/lib/__tests__/path.test.ts +0 -300
- package/src/lib/__tests__/queries.test.ts +0 -39
- package/src/lib/__tests__/standaloneMcpService.test.ts +0 -71
- package/src/lib/__tests__/text-buffer.test.ts +0 -328
- package/src/lib/__tests__/text-utils.test.ts +0 -32
- package/src/lib/__tests__/timing.test.ts +0 -78
- package/src/lib/__tests__/utils.test.ts +0 -238
- package/src/lib/__tests__/vim-buffer-actions.test.ts +0 -154
- package/src/lib/ansi.ts +0 -150
- package/src/lib/cli-push-server.ts +0 -112
- package/src/lib/cli.ts +0 -44
- package/src/lib/clipboard.ts +0 -226
- package/src/lib/command-utils.ts +0 -93
- package/src/lib/commands.ts +0 -270
- package/src/lib/constants.ts +0 -3
- package/src/lib/extension-connection.ts +0 -181
- package/src/lib/focusables.ts +0 -7
- package/src/lib/fs.ts +0 -533
- package/src/lib/markdown/code-block.tsx +0 -63
- package/src/lib/markdown/index.ts +0 -4
- package/src/lib/markdown/link.tsx +0 -19
- package/src/lib/markdown/markdown.tsx +0 -372
- package/src/lib/markdown/types.ts +0 -15
- package/src/lib/mcpCommandHandler.ts +0 -121
- package/src/lib/mcpManagement.ts +0 -44
- package/src/lib/path-paste.ts +0 -185
- package/src/lib/path.ts +0 -179
- package/src/lib/queries.ts +0 -15
- package/src/lib/standaloneMcpService.ts +0 -688
- package/src/lib/status-utils.ts +0 -237
- package/src/lib/test-utils.tsx +0 -72
- package/src/lib/text-buffer.ts +0 -2415
- package/src/lib/text-utils.ts +0 -272
- package/src/lib/timing.ts +0 -63
- package/src/lib/types.ts +0 -295
- package/src/lib/utils.ts +0 -182
- package/src/lib/vim-buffer-actions.ts +0 -732
- package/src/providers/agent.tsx +0 -1063
- package/src/providers/api-client.tsx +0 -43
- package/src/services/logger.ts +0 -85
- package/src/terminal/detection.ts +0 -187
- package/src/terminal/exit.ts +0 -279
- package/src/terminal/notification.ts +0 -83
- package/src/terminal/progress.ts +0 -201
- package/src/terminal/setup.ts +0 -797
- package/src/terminal/types.ts +0 -51
- package/src/theme/context.tsx +0 -57
- package/src/theme/index.ts +0 -4
- package/src/theme/themed.tsx +0 -35
- package/src/theme/themes.json +0 -546
- package/src/theme/types.ts +0 -110
- package/src/tools/types.ts +0 -59
- package/src/tools/utils/__tests__/zod-coercion.test.ts +0 -33
- package/src/tools/utils/tool-ui-components.tsx +0 -649
- package/src/tools/utils/zod-coercion.ts +0 -35
- package/tsconfig.json +0 -16
- package/tsconfig.node.json +0 -29
- package/tsconfig.test.json +0 -27
- package/tsdown.config.ts +0 -17
- package/vitest.config.ts +0 -76
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
// @vitest-environment node
|
|
2
|
-
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
|
|
5
|
-
import { clamp, deepClone, createDeferredPromise, getSegmentContent, isWindows } from "../utils.js";
|
|
6
|
-
|
|
7
|
-
describe.concurrent("utils", () => {
|
|
8
|
-
describe.concurrent("clamp", () => {
|
|
9
|
-
it("should return value when within range", () => {
|
|
10
|
-
expect(clamp(5, 0, 10)).toBe(5);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("should return min when value is less than min", () => {
|
|
14
|
-
expect(clamp(-5, 0, 10)).toBe(0);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("should return max when value is greater than max", () => {
|
|
18
|
-
expect(clamp(15, 0, 10)).toBe(10);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("should handle edge cases", () => {
|
|
22
|
-
expect(clamp(0, 0, 10)).toBe(0);
|
|
23
|
-
expect(clamp(10, 0, 10)).toBe(10);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("should handle negative ranges", () => {
|
|
27
|
-
expect(clamp(-15, -10, -5)).toBe(-10);
|
|
28
|
-
expect(clamp(-3, -10, -5)).toBe(-5);
|
|
29
|
-
expect(clamp(-7, -10, -5)).toBe(-7);
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe.concurrent("deepClone", () => {
|
|
34
|
-
it("should clone primitive values", () => {
|
|
35
|
-
expect(deepClone(5)).toBe(5);
|
|
36
|
-
expect(deepClone("test")).toBe("test");
|
|
37
|
-
expect(deepClone(true)).toBe(true);
|
|
38
|
-
expect(deepClone(null)).toBe(null);
|
|
39
|
-
expect(deepClone(undefined)).toBe(undefined);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("should clone arrays", () => {
|
|
43
|
-
const original = [1, 2, 3];
|
|
44
|
-
const cloned = deepClone(original);
|
|
45
|
-
expect(cloned).toEqual(original);
|
|
46
|
-
expect(cloned).not.toBe(original);
|
|
47
|
-
cloned.push(4);
|
|
48
|
-
expect(original).toHaveLength(3);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("should clone objects", () => {
|
|
52
|
-
const original: Record<string, number> = { a: 1, b: 2 };
|
|
53
|
-
const cloned = deepClone(original);
|
|
54
|
-
expect(cloned).toEqual(original);
|
|
55
|
-
expect(cloned).not.toBe(original);
|
|
56
|
-
cloned.c = 3;
|
|
57
|
-
expect(original).not.toHaveProperty("c");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("should deep clone nested objects", () => {
|
|
61
|
-
const original = { a: { b: { c: 1 } } };
|
|
62
|
-
const cloned = deepClone(original);
|
|
63
|
-
expect(cloned).toEqual(original);
|
|
64
|
-
expect(cloned.a).not.toBe(original.a);
|
|
65
|
-
expect(cloned.a.b).not.toBe(original.a.b);
|
|
66
|
-
cloned.a.b.c = 2;
|
|
67
|
-
expect(original.a.b.c).toBe(1);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("should deep clone nested arrays", () => {
|
|
71
|
-
const original = [
|
|
72
|
-
[1, 2],
|
|
73
|
-
[3, 4],
|
|
74
|
-
];
|
|
75
|
-
const cloned = deepClone(original);
|
|
76
|
-
expect(cloned).toEqual(original);
|
|
77
|
-
expect(cloned).not.toBe(original);
|
|
78
|
-
expect(cloned[0]).not.toBe(original[0]);
|
|
79
|
-
cloned[0].push(5);
|
|
80
|
-
expect(original[0]).toHaveLength(2);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("should handle mixed nested structures", () => {
|
|
84
|
-
const original = { a: [1, { b: 2 }], c: { d: [3, 4] } };
|
|
85
|
-
const cloned = deepClone(original);
|
|
86
|
-
expect(cloned).toEqual(original);
|
|
87
|
-
(cloned.a[1] as { b: number }).b = 5;
|
|
88
|
-
expect((original.a[1] as { b: number }).b).toBe(2);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("should return RegExp as-is", () => {
|
|
92
|
-
// TODO: LIMITATION - RegExp objects are returned as-is (not cloned).
|
|
93
|
-
// This means mutations to the cloned object's regex will affect the original.
|
|
94
|
-
// Consider: Whether this is intentional or if RegExp should be cloned.
|
|
95
|
-
const regex = /test/gi;
|
|
96
|
-
const cloned = deepClone(regex);
|
|
97
|
-
expect(cloned).toBe(regex);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("should handle empty objects and arrays", () => {
|
|
101
|
-
expect(deepClone({})).toEqual({});
|
|
102
|
-
expect(deepClone([])).toEqual([]);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("should handle Date objects", () => {
|
|
106
|
-
// TODO: BUG - deepClone does not properly handle Date objects.
|
|
107
|
-
// Dates will be cloned as plain objects, losing their Date prototype methods.
|
|
108
|
-
// Fix: Add Date handling: if (obj instanceof Date) return new Date(obj.getTime())
|
|
109
|
-
const date = new Date("2023-01-01");
|
|
110
|
-
const cloned = deepClone(date);
|
|
111
|
-
// Current behavior: cloned is a plain object, not a Date
|
|
112
|
-
expect(cloned).not.toBeInstanceOf(Date);
|
|
113
|
-
expect(typeof cloned).toBe("object");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("should handle circular references", () => {
|
|
117
|
-
// TODO: BUG - deepClone will cause infinite recursion with circular references.
|
|
118
|
-
// Fix: Add a WeakSet to track visited objects and skip circular references.
|
|
119
|
-
// This test documents the current broken behavior - it will stack overflow.
|
|
120
|
-
const obj: any = { a: 1 };
|
|
121
|
-
obj.self = obj; // Circular reference
|
|
122
|
-
// Note: This will cause a stack overflow in the current implementation
|
|
123
|
-
// Uncomment the expect below to verify the bug, but it will crash the test
|
|
124
|
-
// expect(() => deepClone(obj)).toThrow();
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("should handle Map and Set", () => {
|
|
128
|
-
// TODO: LIMITATION - deepClone does not handle Map or Set objects.
|
|
129
|
-
// They will be cloned as plain objects, losing their Map/Set behavior.
|
|
130
|
-
// Fix: Add Map/Set handling similar to arrays.
|
|
131
|
-
const map = new Map([["key", "value"]]);
|
|
132
|
-
const cloned = deepClone(map);
|
|
133
|
-
expect(cloned).not.toBeInstanceOf(Map);
|
|
134
|
-
expect(typeof cloned).toBe("object");
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe.concurrent("createDeferredPromise", () => {
|
|
139
|
-
it("should create a promise with resolve and reject functions", async () => {
|
|
140
|
-
const deferred = createDeferredPromise<string>();
|
|
141
|
-
expect(deferred).toHaveProperty("promise");
|
|
142
|
-
expect(deferred).toHaveProperty("resolve");
|
|
143
|
-
expect(deferred).toHaveProperty("reject");
|
|
144
|
-
expect(typeof deferred.resolve).toBe("function");
|
|
145
|
-
expect(typeof deferred.reject).toBe("function");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("should resolve the promise when resolve is called", async () => {
|
|
149
|
-
const deferred = createDeferredPromise<string>();
|
|
150
|
-
deferred.resolve("test");
|
|
151
|
-
await expect(deferred.promise).resolves.toBe("test");
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("should reject the promise when reject is called", async () => {
|
|
155
|
-
const deferred = createDeferredPromise<string>();
|
|
156
|
-
const error = new Error("test error");
|
|
157
|
-
deferred.reject(error);
|
|
158
|
-
await expect(deferred.promise).rejects.toBe(error);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("should work with void type", async () => {
|
|
162
|
-
const deferred = createDeferredPromise<void>();
|
|
163
|
-
deferred.resolve();
|
|
164
|
-
await expect(deferred.promise).resolves.toBeUndefined();
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("should work with object types", async () => {
|
|
168
|
-
const deferred = createDeferredPromise<{ value: number }>();
|
|
169
|
-
deferred.resolve({ value: 42 });
|
|
170
|
-
await expect(deferred.promise).resolves.toEqual({ value: 42 });
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe.concurrent("getSegmentContent", () => {
|
|
175
|
-
it("should return string when segment is a string", () => {
|
|
176
|
-
expect(getSegmentContent("test")).toBe("test");
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it("should return segment property when segment is an object", () => {
|
|
180
|
-
expect(getSegmentContent({ segment: "test" })).toBe("test");
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it("should return null when segment is undefined", () => {
|
|
184
|
-
expect(getSegmentContent(undefined)).toBe(null);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("should return null when segment is null", () => {
|
|
188
|
-
expect(getSegmentContent(null as unknown as undefined)).toBe(null);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("should return null when segment is an object without segment property", () => {
|
|
192
|
-
expect(getSegmentContent({ other: "value" } as unknown as { segment: string })).toBe(null);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("should handle empty strings", () => {
|
|
196
|
-
expect(getSegmentContent("")).toBe("");
|
|
197
|
-
expect(getSegmentContent({ segment: "" })).toBe("");
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe.concurrent("isWindows", () => {
|
|
202
|
-
it("should return true on Windows platform", () => {
|
|
203
|
-
const originalPlatform = process.platform;
|
|
204
|
-
Object.defineProperty(process, "platform", {
|
|
205
|
-
value: "win32",
|
|
206
|
-
writable: true,
|
|
207
|
-
configurable: true,
|
|
208
|
-
});
|
|
209
|
-
expect(isWindows()).toBe(true);
|
|
210
|
-
Object.defineProperty(process, "platform", {
|
|
211
|
-
value: originalPlatform,
|
|
212
|
-
writable: true,
|
|
213
|
-
configurable: true,
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it("should return false on non-Windows platforms", () => {
|
|
218
|
-
const originalPlatform = process.platform;
|
|
219
|
-
Object.defineProperty(process, "platform", {
|
|
220
|
-
value: "linux",
|
|
221
|
-
writable: true,
|
|
222
|
-
configurable: true,
|
|
223
|
-
});
|
|
224
|
-
expect(isWindows()).toBe(false);
|
|
225
|
-
Object.defineProperty(process, "platform", {
|
|
226
|
-
value: "darwin",
|
|
227
|
-
writable: true,
|
|
228
|
-
configurable: true,
|
|
229
|
-
});
|
|
230
|
-
expect(isWindows()).toBe(false);
|
|
231
|
-
Object.defineProperty(process, "platform", {
|
|
232
|
-
value: originalPlatform,
|
|
233
|
-
writable: true,
|
|
234
|
-
configurable: true,
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
});
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
// @vitest-environment node
|
|
2
|
-
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
|
|
5
|
-
import type { TextBufferState } from "../text-buffer.js";
|
|
6
|
-
|
|
7
|
-
import { handleVimAction, assumeExhaustive } from "../vim-buffer-actions.js";
|
|
8
|
-
|
|
9
|
-
describe.concurrent("vim-buffer-actions", () => {
|
|
10
|
-
const createState = (
|
|
11
|
-
lines: string[] = ["hello world"],
|
|
12
|
-
cursorRow = 0,
|
|
13
|
-
cursorCol = 0,
|
|
14
|
-
): TextBufferState => ({
|
|
15
|
-
lines,
|
|
16
|
-
cursorRow,
|
|
17
|
-
cursorCol,
|
|
18
|
-
preferredCol: null,
|
|
19
|
-
undoStack: [],
|
|
20
|
-
redoStack: [],
|
|
21
|
-
clipboard: null,
|
|
22
|
-
selectionAnchor: null,
|
|
23
|
-
viewportWidth: 80,
|
|
24
|
-
viewportHeight: 24,
|
|
25
|
-
visualLayout: {
|
|
26
|
-
visualLines: lines,
|
|
27
|
-
logicalToVisualMap: lines.map((_, i) => [[i, 0]]),
|
|
28
|
-
visualToLogicalMap: lines.map((_, i) => [i, 0]),
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe.concurrent("assumeExhaustive", () => {
|
|
33
|
-
it("should be a function", () => {
|
|
34
|
-
expect(typeof assumeExhaustive).toBe("function");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("should not throw when called", () => {
|
|
38
|
-
expect(() => assumeExhaustive(null as never)).not.toThrow();
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe.concurrent("handleVimAction", () => {
|
|
43
|
-
it("should handle vim_move_left", () => {
|
|
44
|
-
const state = createState(["hello"], 0, 5);
|
|
45
|
-
const newState = handleVimAction(state, { type: "vim_move_left", payload: { count: 1 } });
|
|
46
|
-
expect(newState.cursorCol).toBe(4);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("should handle vim_move_right", () => {
|
|
50
|
-
const state = createState(["hello"], 0, 0);
|
|
51
|
-
const newState = handleVimAction(state, { type: "vim_move_right", payload: { count: 1 } });
|
|
52
|
-
expect(newState.cursorCol).toBe(1);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should handle vim_move_to_line_start", () => {
|
|
56
|
-
const state = createState(["hello world"], 0, 5);
|
|
57
|
-
const newState = handleVimAction(state, { type: "vim_move_to_line_start" });
|
|
58
|
-
expect(newState.cursorCol).toBe(0);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("should handle vim_move_to_line_end", () => {
|
|
62
|
-
// TODO: IMPROVEMENT - Test should verify exact position. For "hello" (5 chars),
|
|
63
|
-
// cursor should be at position 4 (last character), not just > 0.
|
|
64
|
-
// Current implementation: lineLength > 0 ? lineLength - 1 : 0
|
|
65
|
-
const state = createState(["hello"], 0, 0);
|
|
66
|
-
const newState = handleVimAction(state, { type: "vim_move_to_line_end" });
|
|
67
|
-
expect(newState.cursorCol).toBeGreaterThan(0);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("should handle vim_move_to_first_nonwhitespace", () => {
|
|
71
|
-
const state = createState([" hello"], 0, 0);
|
|
72
|
-
const newState = handleVimAction(state, { type: "vim_move_to_first_nonwhitespace" });
|
|
73
|
-
expect(newState.cursorCol).toBe(3);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("should handle vim_move_to_first_line", () => {
|
|
77
|
-
const state = createState(["line1", "line2", "line3"], 2, 0);
|
|
78
|
-
const newState = handleVimAction(state, { type: "vim_move_to_first_line" });
|
|
79
|
-
expect(newState.cursorRow).toBe(0);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("should handle vim_move_to_last_line", () => {
|
|
83
|
-
const state = createState(["line1", "line2", "line3"], 0, 0);
|
|
84
|
-
const newState = handleVimAction(state, { type: "vim_move_to_last_line" });
|
|
85
|
-
expect(newState.cursorRow).toBe(2);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should handle vim_move_to_line", () => {
|
|
89
|
-
// TODO: CLARIFICATION - The API is confusing: lineNumber is 1-indexed (user-facing)
|
|
90
|
-
// but internally converted to 0-indexed. Consider documenting this clearly or
|
|
91
|
-
// using a more explicit parameter name like `lineNumberOneIndexed`.
|
|
92
|
-
// Current behavior: lineNumber 2 -> row index 1 (second line)
|
|
93
|
-
const state = createState(["line1", "line2", "line3"], 0, 0);
|
|
94
|
-
const newState = handleVimAction(state, {
|
|
95
|
-
type: "vim_move_to_line",
|
|
96
|
-
payload: { lineNumber: 2 },
|
|
97
|
-
});
|
|
98
|
-
expect(newState.cursorRow).toBe(1); // 0-indexed
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should handle vim_delete_char", () => {
|
|
102
|
-
const state = createState(["hello"], 0, 2);
|
|
103
|
-
const newState = handleVimAction(state, { type: "vim_delete_char", payload: { count: 1 } });
|
|
104
|
-
expect(newState.lines[0].length).toBeLessThan(state.lines[0].length);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("should handle vim_move_word_forward", () => {
|
|
108
|
-
const state = createState(["hello world test"], 0, 0);
|
|
109
|
-
const newState = handleVimAction(state, {
|
|
110
|
-
type: "vim_move_word_forward",
|
|
111
|
-
payload: { count: 1 },
|
|
112
|
-
});
|
|
113
|
-
expect(newState.cursorCol).toBeGreaterThan(0);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("should handle vim_move_word_backward", () => {
|
|
117
|
-
const state = createState(["hello world"], 0, 11);
|
|
118
|
-
const newState = handleVimAction(state, {
|
|
119
|
-
type: "vim_move_word_backward",
|
|
120
|
-
payload: { count: 1 },
|
|
121
|
-
});
|
|
122
|
-
expect(newState.cursorCol).toBeLessThan(11);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("should handle vim_delete_word_forward", () => {
|
|
126
|
-
const state = createState(["hello world"], 0, 0);
|
|
127
|
-
const newState = handleVimAction(state, {
|
|
128
|
-
type: "vim_delete_word_forward",
|
|
129
|
-
payload: { count: 1 },
|
|
130
|
-
});
|
|
131
|
-
expect(newState.lines[0]).not.toContain("hello");
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("should handle vim_delete_to_end_of_line", () => {
|
|
135
|
-
const state = createState(["hello world"], 0, 5);
|
|
136
|
-
const newState = handleVimAction(state, { type: "vim_delete_to_end_of_line" });
|
|
137
|
-
expect(newState.lines[0]).toBe("hello");
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("should handle vim_open_line_below", () => {
|
|
141
|
-
const state = createState(["line1"], 0, 5);
|
|
142
|
-
const newState = handleVimAction(state, { type: "vim_open_line_below" });
|
|
143
|
-
expect(newState.lines.length).toBe(2);
|
|
144
|
-
expect(newState.cursorRow).toBe(1);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it("should handle vim_open_line_above", () => {
|
|
148
|
-
const state = createState(["line1"], 0, 5);
|
|
149
|
-
const newState = handleVimAction(state, { type: "vim_open_line_above" });
|
|
150
|
-
expect(newState.lines.length).toBe(2);
|
|
151
|
-
expect(newState.cursorRow).toBe(0);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
});
|
package/src/lib/ansi.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
export enum ANSI {
|
|
2
|
-
ESC = "\x1b",
|
|
3
|
-
BEL = "\x07",
|
|
4
|
-
ST = "\x1b\\",
|
|
5
|
-
ENABLE_FOCUS_REPORTING = `${ANSI.ESC}[?1004h`,
|
|
6
|
-
DISABLE_FOCUS_REPORTING = `${ANSI.ESC}[?1004l`,
|
|
7
|
-
FOCUS_IN = `${ANSI.ESC}[I`,
|
|
8
|
-
FOCUS_OUT = `${ANSI.ESC}[O`,
|
|
9
|
-
SHOW_CURSOR = `${ANSI.ESC}[?25h`,
|
|
10
|
-
HIDE_CURSOR = `${ANSI.ESC}[?25l`,
|
|
11
|
-
SET_TITLE = `${ANSI.ESC}]2;`,
|
|
12
|
-
RESET_SGR = `${ANSI.ESC}[0m`,
|
|
13
|
-
SOFT_RESET = `${ANSI.ESC}[!p`,
|
|
14
|
-
HARD_RESET = `${ANSI.ESC}c`,
|
|
15
|
-
ENABLE_BRACKETED_PASTE = `${ANSI.ESC}[?2004h`,
|
|
16
|
-
DISABLE_BRACKETED_PASTE = `${ANSI.ESC}[?2004l`,
|
|
17
|
-
PASTE_MODE_PREFIX = `${ANSI.ESC}[200~`,
|
|
18
|
-
PASTE_MODE_SUFFIX = `${ANSI.ESC}[201~`,
|
|
19
|
-
DIM = `${ANSI.ESC}[2m`,
|
|
20
|
-
BOLD = `${ANSI.ESC}[1m`,
|
|
21
|
-
RESET_BOLD = `${ANSI.ESC}[22m`,
|
|
22
|
-
ITALIC = `${ANSI.ESC}[3m`,
|
|
23
|
-
RESET_ITALIC = `${ANSI.ESC}[23m`,
|
|
24
|
-
STRIKETHROUGH = `${ANSI.ESC}[9m`,
|
|
25
|
-
RESET_STRIKETHROUGH = `${ANSI.ESC}[29m`,
|
|
26
|
-
UNDERLINE = `${ANSI.ESC}[4m`,
|
|
27
|
-
RESET_UNDERLINE = `${ANSI.ESC}[24m`,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* OSC 9 - iTerm2 notification
|
|
32
|
-
* Format: ESC ] 9 ; message BEL
|
|
33
|
-
*/
|
|
34
|
-
export function osc9(message: string): string {
|
|
35
|
-
return `${ANSI.ESC}]9;\n\n${message}${ANSI.BEL}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* OSC 777 - Ghostty/urxvt notification
|
|
40
|
-
* Format: ESC ] 777 ; notify ; title ; body BEL
|
|
41
|
-
*/
|
|
42
|
-
export function osc777(title: string, body: string): string {
|
|
43
|
-
return `${ANSI.ESC}]777;notify;${title};${body}${ANSI.BEL}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* OSC 99 - Kitty desktop notification
|
|
48
|
-
* Format: ESC ] 99 ; metadata ; payload ST
|
|
49
|
-
*/
|
|
50
|
-
export function osc99(title: string, body: string): string {
|
|
51
|
-
const notificationId = Math.floor(Math.random() * 10000);
|
|
52
|
-
const titlePart = `${ANSI.ESC}]99;i=${notificationId}:d=0:p=title;${title}${ANSI.ST}`;
|
|
53
|
-
const bodyPart = `${ANSI.ESC}]99;i=${notificationId}:p=body;${body}${ANSI.ST}`;
|
|
54
|
-
const donePart = `${ANSI.ESC}]99;i=${notificationId}:d=1:a=focus;${ANSI.ST}`;
|
|
55
|
-
return titlePart + bodyPart + donePart;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export type ProgressState = 0 | 1 | 2 | 3 | 4;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* OSC 9;4 - Progress bar (ConEmu/Ghostty/iTerm2 style)
|
|
62
|
-
* Format: ESC ] 9 ; 4 ; state ; progress [BEL/ST]
|
|
63
|
-
* @param state - 0 (hide), 1 (default), 2 (error), 3 (indeterminate), 4 (warning)
|
|
64
|
-
* @param progress - Progress percentage (0-100), required when state is 1, 2, or 4
|
|
65
|
-
*/
|
|
66
|
-
export function osc9Progress(state: ProgressState, progress?: number): string {
|
|
67
|
-
const p = progress !== undefined ? Math.max(0, Math.min(100, Math.round(progress))) : 0;
|
|
68
|
-
const payload = `9;4;${state};${p}`;
|
|
69
|
-
// Emit both terminators for maximum compatibility (iTerm2 prefers BEL, Ghostty prefers ST)
|
|
70
|
-
return `${ANSI.ESC}]${payload}${ANSI.BEL}${ANSI.ESC}]${payload}${ANSI.ST}`;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* OSC 1337 - iTerm2 Progress indicator
|
|
75
|
-
*/
|
|
76
|
-
function sanitizeItermValue(value: string): string {
|
|
77
|
-
return value
|
|
78
|
-
.replaceAll("\x1b", "")
|
|
79
|
-
.replaceAll("\x07", "")
|
|
80
|
-
.replaceAll("\n", " ")
|
|
81
|
-
.replaceAll("\r", " ")
|
|
82
|
-
.replaceAll(";", ":")
|
|
83
|
-
.trim()
|
|
84
|
-
.slice(0, 120);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function itermOsc(payload: string): string {
|
|
88
|
-
// Emit both terminators for maximum compatibility: BEL and ST.
|
|
89
|
-
return `${ANSI.ESC}]${payload}${ANSI.BEL}${ANSI.ESC}]${payload}${ANSI.ST}`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function osc1337ProgressClear(): string {
|
|
93
|
-
return itermOsc("1337;Progress=");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function osc1337ProgressState(state: 0 | 1 | 2): string {
|
|
97
|
-
return itermOsc(`1337;Progress=${state}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function osc1337ProgressPercent(percent: number, message?: string): string {
|
|
101
|
-
const progressValue = Math.max(0, Math.min(100, Math.round(percent)));
|
|
102
|
-
const msg = message ? sanitizeItermValue(message) : "";
|
|
103
|
-
|
|
104
|
-
if (msg) {
|
|
105
|
-
return (
|
|
106
|
-
itermOsc(`1337;Progress=${progressValue};${msg}`) +
|
|
107
|
-
itermOsc(`1337;Progress=message=${msg};percent=${progressValue}`)
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return itermOsc(`1337;Progress=${progressValue}`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Creates a regex pattern that matches ANSI escape sequences
|
|
116
|
-
* @param options.onlyFirst - If true, matches only the first occurrence
|
|
117
|
-
* @returns Regular expression for matching ANSI sequences
|
|
118
|
-
*/
|
|
119
|
-
export function ansiRegex({ onlyFirst = false } = {}): RegExp {
|
|
120
|
-
// Valid string terminator sequences are BEL, ESC\, and 0x9c
|
|
121
|
-
const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
|
|
122
|
-
|
|
123
|
-
// OSC sequences only: ESC ] ... ST (non-greedy until the first ST)
|
|
124
|
-
const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
|
|
125
|
-
|
|
126
|
-
// CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte
|
|
127
|
-
const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
|
|
128
|
-
|
|
129
|
-
const pattern = `${osc}|${csi}`;
|
|
130
|
-
|
|
131
|
-
return new RegExp(pattern, onlyFirst ? undefined : "g");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const stripAnsiRegex = ansiRegex();
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Strip ANSI escape codes from a string
|
|
138
|
-
* @param string - The string to strip ANSI codes from
|
|
139
|
-
* @returns The string with ANSI codes removed
|
|
140
|
-
*/
|
|
141
|
-
export function stripAnsi(string: string): string {
|
|
142
|
-
if (typeof string !== "string") {
|
|
143
|
-
throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Even though the regex is global, we don't need to reset the `.lastIndex`
|
|
147
|
-
// because unlike `.exec()` and `.test()`, `.replace()` does it automatically
|
|
148
|
-
// and doing it manually has a performance penalty.
|
|
149
|
-
return string.replace(stripAnsiRegex, "");
|
|
150
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { SocketServer } from "@codellm/comms";
|
|
2
|
-
|
|
3
|
-
import { cliContract, createSocketServer } from "@codellm/comms";
|
|
4
|
-
import { product } from "@codellm/product";
|
|
5
|
-
import { implement } from "@orpc/server";
|
|
6
|
-
import { StandardRPCHandler } from "@orpc/server/standard";
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
|
-
import * as os from "node:os";
|
|
9
|
-
import * as path from "node:path";
|
|
10
|
-
|
|
11
|
-
export interface EditorState {
|
|
12
|
-
file: string | undefined;
|
|
13
|
-
startLine: number | undefined;
|
|
14
|
-
endLine: number | undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type ToolDecisionValue = "accept" | "reject" | "allowAlways";
|
|
18
|
-
|
|
19
|
-
export class CliPushServer {
|
|
20
|
-
private socketServer: SocketServer | undefined;
|
|
21
|
-
readonly socketPath: string;
|
|
22
|
-
private editorState: EditorState = {
|
|
23
|
-
file: undefined,
|
|
24
|
-
startLine: undefined,
|
|
25
|
-
endLine: undefined,
|
|
26
|
-
};
|
|
27
|
-
private onEditorStateCallback: ((state: EditorState) => void) | null = null;
|
|
28
|
-
private pendingDecisions = new Map<
|
|
29
|
-
string,
|
|
30
|
-
{ resolve: (d: ToolDecisionValue) => void; cleanup: () => void }
|
|
31
|
-
>();
|
|
32
|
-
|
|
33
|
-
constructor() {
|
|
34
|
-
const socketDir = path.join(os.homedir(), product.configDirName, "cli");
|
|
35
|
-
this.socketPath = path.join(socketDir, `${process.pid}.sock`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
getEditorState(): EditorState {
|
|
39
|
-
return { ...this.editorState };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
onEditorChange(callback: (state: EditorState) => void): void {
|
|
43
|
-
this.onEditorStateCallback = callback;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
waitForDecision(toolId: string, signal?: AbortSignal): Promise<ToolDecisionValue> {
|
|
47
|
-
return new Promise<ToolDecisionValue>((resolve, reject) => {
|
|
48
|
-
if (signal?.aborted) {
|
|
49
|
-
reject(new Error("Aborted"));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const cleanup = () => {
|
|
54
|
-
signal?.removeEventListener("abort", onAbort);
|
|
55
|
-
this.pendingDecisions.delete(toolId);
|
|
56
|
-
};
|
|
57
|
-
const onAbort = () => {
|
|
58
|
-
cleanup();
|
|
59
|
-
reject(new Error("Aborted"));
|
|
60
|
-
};
|
|
61
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
62
|
-
|
|
63
|
-
this.pendingDecisions.set(toolId, {
|
|
64
|
-
resolve: (d) => {
|
|
65
|
-
cleanup();
|
|
66
|
-
resolve(d);
|
|
67
|
-
},
|
|
68
|
-
cleanup,
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async start(): Promise<void> {
|
|
74
|
-
const socketDir = path.dirname(this.socketPath);
|
|
75
|
-
if (!fs.existsSync(socketDir)) {
|
|
76
|
-
fs.mkdirSync(socketDir, { recursive: true });
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const cc = implement(cliContract);
|
|
80
|
-
|
|
81
|
-
type EditorStateInput = { file: string | null; startLine?: number; endLine?: number };
|
|
82
|
-
type ToolDecisionInput = { toolId: string; decision: "accept" | "reject" | "allowAlways" };
|
|
83
|
-
|
|
84
|
-
const router = cc.router({
|
|
85
|
-
editorStateChanged: cc.editorStateChanged.handler(
|
|
86
|
-
async ({ input }: { input: EditorStateInput }) => {
|
|
87
|
-
this.editorState = {
|
|
88
|
-
file: input.file ?? undefined,
|
|
89
|
-
startLine: input.startLine,
|
|
90
|
-
endLine: input.endLine,
|
|
91
|
-
};
|
|
92
|
-
this.onEditorStateCallback?.(this.editorState);
|
|
93
|
-
return { ok: true };
|
|
94
|
-
},
|
|
95
|
-
),
|
|
96
|
-
toolDecision: cc.toolDecision.handler(async ({ input }: { input: ToolDecisionInput }) => {
|
|
97
|
-
const pending = this.pendingDecisions.get(input.toolId);
|
|
98
|
-
if (pending) pending.resolve(input.decision);
|
|
99
|
-
return { ok: true };
|
|
100
|
-
}),
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
this.socketServer = await createSocketServer(this.socketPath, new StandardRPCHandler(router));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
dispose(): void {
|
|
107
|
-
for (const [, entry] of this.pendingDecisions) entry.cleanup();
|
|
108
|
-
this.pendingDecisions.clear();
|
|
109
|
-
this.socketServer?.dispose();
|
|
110
|
-
this.socketServer = undefined;
|
|
111
|
-
}
|
|
112
|
-
}
|
package/src/lib/cli.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { spawn } from "child_process";
|
|
2
|
-
|
|
3
|
-
export async function* runCli(command: string, args: string[]): AsyncGenerator<string> {
|
|
4
|
-
const proc = spawn(command, args, {
|
|
5
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
6
|
-
cwd: process.cwd(),
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
const streams = [proc.stdout, proc.stderr];
|
|
10
|
-
|
|
11
|
-
const readers = streams.map((stream) => stream[Symbol.asyncIterator]());
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
while (true) {
|
|
15
|
-
const results = await Promise.all(readers.map((reader) => reader.next()));
|
|
16
|
-
let doneCount = 0;
|
|
17
|
-
for (let i = 0; i < results.length; i++) {
|
|
18
|
-
const { value, done } = results[i];
|
|
19
|
-
if (done) {
|
|
20
|
-
doneCount++;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (value) {
|
|
24
|
-
yield value.toString();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
if (doneCount === readers.length) {
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
} finally {
|
|
32
|
-
// Attempt graceful shutdown
|
|
33
|
-
proc.kill("SIGTERM");
|
|
34
|
-
|
|
35
|
-
const timeout = setTimeout(() => {
|
|
36
|
-
// Force termination if still running
|
|
37
|
-
if (!proc.killed) {
|
|
38
|
-
proc.kill("SIGKILL");
|
|
39
|
-
}
|
|
40
|
-
}, 5000);
|
|
41
|
-
|
|
42
|
-
proc.on("close", () => clearTimeout(timeout));
|
|
43
|
-
}
|
|
44
|
-
}
|