@cortexkit/aft-opencode 0.42.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bg-notifications.d.ts +35 -0
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/config.d.ts +7 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/configure-warnings.d.ts +3 -3
- package/dist/configure-warnings.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6092 -9528
- package/dist/notifications.d.ts +2 -2
- package/dist/notifications.d.ts.map +1 -1
- package/dist/shared/rpc-client.d.ts +6 -0
- package/dist/shared/rpc-client.d.ts.map +1 -1
- package/dist/shared/rpc-notifications.d.ts +29 -15
- package/dist/shared/rpc-notifications.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts +10 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/subc-tool-schemas.d.ts +1 -1
- package/dist/subc-tool-schemas.d.ts.map +1 -1
- package/dist/tools/_shared.d.ts +12 -2
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash_watch.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/imports.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts +0 -1
- package/dist/tools/inspect.d.ts.map +1 -1
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/permissions.d.ts +23 -13
- package/dist/tools/permissions.d.ts.map +1 -1
- package/dist/tools/reading.d.ts +0 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/refactoring.d.ts.map +1 -1
- package/dist/tools/safety.d.ts.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/semantic.d.ts.map +1 -1
- package/dist/tui/notification-socket.d.ts +41 -0
- package/dist/tui/notification-socket.d.ts.map +1 -0
- package/dist/tui.js +2460 -187
- package/dist/types.d.ts +4 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -9
- package/src/shared/rpc-client.ts +9 -0
- package/src/shared/rpc-notifications.ts +97 -41
- package/src/shared/rpc-server.ts +286 -67
- package/src/tui/index.tsx +77 -125
- package/src/tui/notification-socket.ts +421 -0
- package/src/tui/sidebar.tsx +38 -54
- package/dist/patch-parser.d.ts +0 -33
- package/dist/patch-parser.d.ts.map +0 -1
- package/dist/shared/opencode-config-dir.d.ts +0 -16
- package/dist/shared/opencode-config-dir.d.ts.map +0 -1
- package/dist/shared/pty-cache.d.ts +0 -18
- package/dist/shared/pty-cache.d.ts.map +0 -1
- package/dist/shared/tui-config.d.ts +0 -2
- package/dist/shared/tui-config.d.ts.map +0 -1
- package/src/shared/opencode-config-dir.ts +0 -46
- package/src/shared/pty-cache.ts +0 -113
- package/src/shared/tui-config.ts +0 -58
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AftTransportPool } from "@cortexkit/aft-bridge";
|
|
2
2
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
3
3
|
import type { AftConfig } from "./config.js";
|
|
4
4
|
interface ShellEnvPluginHost {
|
|
@@ -19,12 +19,14 @@ interface ShellEnvPluginHost {
|
|
|
19
19
|
* Bundles the binary bridge, the OpenCode SDK client, and plugin config.
|
|
20
20
|
*/
|
|
21
21
|
export interface PluginContext {
|
|
22
|
-
pool:
|
|
22
|
+
pool: AftTransportPool;
|
|
23
23
|
client: PluginInput["client"];
|
|
24
24
|
plugin?: ShellEnvPluginHost;
|
|
25
25
|
config: AftConfig;
|
|
26
26
|
/** Absolute path to AFT's storage dir (e.g. ~/.local/share/opencode/storage/plugin/aft) */
|
|
27
27
|
storageDir: string;
|
|
28
|
+
/** Returns false when user and project config merge to `enabled: false` for this project. */
|
|
29
|
+
isProjectEnabled?: (projectRoot: string) => boolean;
|
|
28
30
|
}
|
|
29
31
|
export {};
|
|
30
32
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,CACR,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAC7D,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,KACnC,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,GAAG;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CACnF;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC;CACrD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/aft-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,22 +31,21 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@clack/prompts": "^1.6.0",
|
|
34
|
-
"@cortexkit/aft-bridge": "0.
|
|
34
|
+
"@cortexkit/aft-bridge": "0.43.0",
|
|
35
35
|
"@opentui/core": "^0.4.2",
|
|
36
36
|
"@opentui/solid": "^0.4.2",
|
|
37
|
-
"@xterm/headless": "^5.5.0",
|
|
38
37
|
"comment-json": "^4.6.2",
|
|
39
38
|
"solid-js": "1.9.12",
|
|
40
39
|
"undici": "^7.25.0",
|
|
41
40
|
"zod": "^4.4.3"
|
|
42
41
|
},
|
|
43
42
|
"optionalDependencies": {
|
|
44
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
45
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
46
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
47
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
48
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
49
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
43
|
+
"@cortexkit/aft-darwin-arm64": "0.43.0",
|
|
44
|
+
"@cortexkit/aft-darwin-x64": "0.43.0",
|
|
45
|
+
"@cortexkit/aft-linux-arm64": "0.43.0",
|
|
46
|
+
"@cortexkit/aft-linux-x64": "0.43.0",
|
|
47
|
+
"@cortexkit/aft-win32-arm64": "0.43.0",
|
|
48
|
+
"@cortexkit/aft-win32-x64": "0.43.0"
|
|
50
49
|
},
|
|
51
50
|
"devDependencies": {
|
|
52
51
|
"@opencode-ai/plugin": "^1.17.11",
|
package/src/shared/rpc-client.ts
CHANGED
|
@@ -10,6 +10,8 @@ type PortInfoSource = "instance" | "legacy";
|
|
|
10
10
|
type ParsedPortInfo = { port: number; token: string | null; pid?: number; started_at?: number };
|
|
11
11
|
type PortInfo = ParsedPortInfo & { source: PortInfoSource; path?: string };
|
|
12
12
|
|
|
13
|
+
export type AftRpcEndpoint = { port: number; token: string | null };
|
|
14
|
+
|
|
13
15
|
export interface AftRpcCallOptions {
|
|
14
16
|
signal?: AbortSignal;
|
|
15
17
|
/**
|
|
@@ -157,6 +159,13 @@ export class AftRpcClient {
|
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
/** Resolve the freshest live endpoint for the persistent TUI WebSocket. */
|
|
163
|
+
async resolveEndpoint(signal?: AbortSignal): Promise<AftRpcEndpoint | null> {
|
|
164
|
+
const [info] = await this.resolvePortInfos(signal);
|
|
165
|
+
if (!info) return null;
|
|
166
|
+
return { port: info.port, token: info.token };
|
|
167
|
+
}
|
|
168
|
+
|
|
160
169
|
/**
|
|
161
170
|
* Discover all live RPC port files for this project. Tries the per-instance
|
|
162
171
|
* directory first (v0.28.2+), then the single legacy `port` file (older
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* In-memory notification queue for server → TUI push.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The queue is the durable at-least-once buffer; live WebSocket sinks are only
|
|
5
|
+
* a low-latency delivery path. A notification is pruned only after the TUI acks
|
|
6
|
+
* it with the highest id it handled.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
export interface RpcNotification {
|
|
@@ -13,33 +13,80 @@ export interface RpcNotification {
|
|
|
13
13
|
sessionId?: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
export interface NotificationSink {
|
|
17
|
+
/** The TUI session that the sink's socket authenticated for. */
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
/** Deliver one queued notification over the live socket. */
|
|
20
|
+
send: (notification: RpcNotification) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface StatusChangeSink {
|
|
24
|
+
/** The TUI session that the sink's socket authenticated for. */
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
/** Deliver a lightweight status invalidation over the live socket. */
|
|
27
|
+
send: (event: RpcStatusChange) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RpcStatusChange {
|
|
31
|
+
sessionId?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
let queue: RpcNotification[] = [];
|
|
17
35
|
let nextNotificationId = 1;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// take the TUI-dialog path because session A's TUI is polling — queuing a
|
|
29
|
-
// B-scoped dialog action that A's poller correctly refuses to show, so B's
|
|
30
|
-
// notice is lost (it also suppressed B's non-TUI fallback). Tracking drains per
|
|
31
|
-
// session routes each producer to the right delivery path.
|
|
32
|
-
const lastDrainAtBySession = new Map<string, number>();
|
|
33
|
-
let lastDrainAtAny = 0;
|
|
34
|
-
const TUI_CONNECTED_WINDOW_MS = 3_000;
|
|
36
|
+
const notificationSinks = new Set<NotificationSink>();
|
|
37
|
+
const statusChangeSinks = new Set<StatusChangeSink>();
|
|
38
|
+
|
|
39
|
+
/** Register a live TUI notification sink. Returns an unregister function. */
|
|
40
|
+
export function registerNotificationSink(sink: NotificationSink): () => void {
|
|
41
|
+
notificationSinks.add(sink);
|
|
42
|
+
return () => {
|
|
43
|
+
notificationSinks.delete(sink);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
35
46
|
|
|
36
|
-
/**
|
|
47
|
+
/** Register a live TUI status-invalidation sink. Returns an unregister function. */
|
|
48
|
+
export function registerStatusChangeSink(sink: StatusChangeSink): () => void {
|
|
49
|
+
statusChangeSinks.add(sink);
|
|
50
|
+
return () => {
|
|
51
|
+
statusChangeSinks.delete(sink);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function notificationMatchesSink(
|
|
56
|
+
notification: RpcNotification,
|
|
57
|
+
sink: { sessionId?: string },
|
|
58
|
+
): boolean {
|
|
59
|
+
return (
|
|
60
|
+
notification.sessionId === undefined ||
|
|
61
|
+
sink.sessionId === undefined ||
|
|
62
|
+
notification.sessionId === sink.sessionId
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function statusChangeMatchesSink(event: RpcStatusChange, sink: { sessionId?: string }): boolean {
|
|
67
|
+
return (
|
|
68
|
+
event.sessionId === undefined ||
|
|
69
|
+
sink.sessionId === undefined ||
|
|
70
|
+
event.sessionId === sink.sessionId
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Push a notification for the TUI. Fans out to live sinks and keeps the queue. */
|
|
37
75
|
export function pushNotification(
|
|
38
76
|
type: string,
|
|
39
77
|
payload: Record<string, unknown>,
|
|
40
78
|
sessionId?: string,
|
|
41
79
|
): void {
|
|
42
|
-
|
|
80
|
+
const notification: RpcNotification = { id: nextNotificationId++, type, payload, sessionId };
|
|
81
|
+
queue.push(notification);
|
|
82
|
+
for (const sink of notificationSinks) {
|
|
83
|
+
if (!notificationMatchesSink(notification, sink)) continue;
|
|
84
|
+
try {
|
|
85
|
+
sink.send(notification);
|
|
86
|
+
} catch {
|
|
87
|
+
// A dead socket must not block other sinks; the queue replays on reconnect.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
43
90
|
// Cap queue size to prevent unbounded growth if a TUI is not draining.
|
|
44
91
|
// Session-fair eviction: a naive `slice(-50)` drops the globally-oldest
|
|
45
92
|
// items, so a noisy session could evict ANOTHER session's single unseen
|
|
@@ -64,8 +111,20 @@ export function pushNotification(
|
|
|
64
111
|
}
|
|
65
112
|
}
|
|
66
113
|
|
|
114
|
+
/** Push a lightweight status invalidation to matching live TUI sockets. */
|
|
115
|
+
export function pushStatusChange(sessionId?: string): void {
|
|
116
|
+
const event: RpcStatusChange = sessionId ? { sessionId } : {};
|
|
117
|
+
for (const sink of statusChangeSinks) {
|
|
118
|
+
if (!statusChangeMatchesSink(event, sink)) continue;
|
|
119
|
+
try {
|
|
120
|
+
sink.send(event);
|
|
121
|
+
} catch {
|
|
122
|
+
// A dead socket must not block other status listeners.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
67
127
|
/** Return pending notifications after acking the client's last received id.
|
|
68
|
-
* Updates lastDrainAt so isTuiConnected() reflects recent activity.
|
|
69
128
|
*
|
|
70
129
|
* Session scoping: when `sessionId` is provided, only notifications tagged for
|
|
71
130
|
* that session (or session-less/global ones) are returned and pruned — a
|
|
@@ -81,11 +140,9 @@ export function pushNotification(
|
|
|
81
140
|
*
|
|
82
141
|
* Delivery is at-least-once (non-destructive return + prune-on-ack): a returned
|
|
83
142
|
* notification stays queued until a later call acks it via a higher
|
|
84
|
-
* `lastReceivedId`, so a
|
|
143
|
+
* `lastReceivedId`, so a dropped WS socket re-delivers the backlog on reconnect
|
|
144
|
+
* (the client sends its `lastReceivedId` in the hello). */
|
|
85
145
|
export function drainNotifications(lastReceivedId = 0, sessionId?: string): RpcNotification[] {
|
|
86
|
-
const now = Date.now();
|
|
87
|
-
lastDrainAtAny = now;
|
|
88
|
-
if (sessionId !== undefined) lastDrainAtBySession.set(sessionId, now);
|
|
89
146
|
const matchesClient = (notification: RpcNotification): boolean =>
|
|
90
147
|
sessionId === undefined ||
|
|
91
148
|
notification.sessionId === undefined ||
|
|
@@ -102,20 +159,19 @@ export function drainNotifications(lastReceivedId = 0, sessionId?: string): RpcN
|
|
|
102
159
|
);
|
|
103
160
|
}
|
|
104
161
|
|
|
105
|
-
/** Whether a TUI client is
|
|
106
|
-
* Returns true only if a TUI has drained within the last 3 seconds.
|
|
107
|
-
*
|
|
108
|
-
* Pass `sessionId` (preferred) to ask whether a TUI is polling FOR THAT
|
|
109
|
-
* SESSION — this is what producers (`/aft-status`, configure warnings, etc.)
|
|
110
|
-
* must use to decide dialog-vs-message, so a TUI on a different session in the
|
|
111
|
-
* same process does not misroute their delivery. Omit it only for legacy/global
|
|
112
|
-
* callers that genuinely have no session context; they fall back to "any
|
|
113
|
-
* session recently drained" (the pre-per-session behavior). */
|
|
162
|
+
/** Whether a TUI client is connected via a live notification socket. */
|
|
114
163
|
export function isTuiConnected(sessionId?: string): boolean {
|
|
115
|
-
|
|
116
|
-
if (sessionId
|
|
117
|
-
|
|
118
|
-
|
|
164
|
+
if (notificationSinks.size === 0) return false;
|
|
165
|
+
if (sessionId === undefined) return true;
|
|
166
|
+
for (const sink of notificationSinks) {
|
|
167
|
+
if (sink.sessionId === undefined || sink.sessionId === sessionId) return true;
|
|
119
168
|
}
|
|
120
|
-
return
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function __resetRpcNotificationsForTest(): void {
|
|
173
|
+
queue = [];
|
|
174
|
+
nextNotificationId = 1;
|
|
175
|
+
notificationSinks.clear();
|
|
176
|
+
statusChangeSinks.clear();
|
|
121
177
|
}
|