@cortexkit/aft-opencode 0.15.3 → 0.15.5
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/bridge.d.ts +5 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/downloader.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +424 -103
- package/dist/onnx-runtime.d.ts.map +1 -1
- package/dist/pool.d.ts.map +1 -1
- package/dist/resolver.d.ts.map +1 -1
- package/dist/shared/rpc-client.d.ts +2 -0
- package/dist/shared/rpc-client.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts +1 -0
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/url-fetch.d.ts +7 -1
- package/dist/shared/url-fetch.d.ts.map +1 -1
- package/dist/tools/hoisted-internals.d.ts +9 -0
- package/dist/tools/hoisted-internals.d.ts.map +1 -0
- package/dist/tools/hoisted.d.ts +2 -0
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tui.js +89 -15
- package/package.json +6 -6
- package/src/shared/rpc-client.ts +33 -14
- package/src/shared/rpc-server.ts +15 -3
- package/src/shared/url-fetch.ts +206 -23
- package/src/tui/index.tsx +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onnx-runtime.d.ts","sourceRoot":"","sources":["../src/onnx-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0DH,4DAA4D;AAC5D,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BlF;
|
|
1
|
+
{"version":3,"file":"onnx-runtime.d.ts","sourceRoot":"","sources":["../src/onnx-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0DH,4DAA4D;AAC5D,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BlF;AA6LD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3D"}
|
package/dist/pool.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,YAAY,CAA+C;gBAGjE,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,WAAgB,EACzB,eAAe,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAmB/C;;;;;;;OAOG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgB5D;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY;IAmB5C,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAUf,yDAAyD;IACzD,OAAO,CAAC,QAAQ;IAgBhB,wDAAwD;IAClD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,YAAY,CAA+C;gBAGjE,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,WAAgB,EACzB,eAAe,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAmB/C;;;;;;;OAOG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgB5D;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY;IAmB5C,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAUf,yDAAyD;IACzD,OAAO,CAAC,QAAQ;IAgBhB,wDAAwD;IAClD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
package/dist/resolver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAoDA;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,QAAQ,GAAE,MAAyB,EACnC,IAAI,GAAE,MAAqB,GAC1B,MAAM,CAgBR;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAiD9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAiClD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare class AftRpcClient {
|
|
2
2
|
private port;
|
|
3
|
+
private token;
|
|
3
4
|
private portFilePath;
|
|
4
5
|
private healthChecked;
|
|
5
6
|
constructor(storageDir: string, directory: string);
|
|
@@ -8,6 +9,7 @@ export declare class AftRpcClient {
|
|
|
8
9
|
/** Check if the RPC server is reachable. */
|
|
9
10
|
isAvailable(): Promise<boolean>;
|
|
10
11
|
private resolvePort;
|
|
12
|
+
private resolvePortInfo;
|
|
11
13
|
private readPortFile;
|
|
12
14
|
private healthCheck;
|
|
13
15
|
private fetchWithTimeout;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-client.ts"],"names":[],"mappings":"AAQA,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAS;gBAElB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAIjD,iFAAiF;IAC3E,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,CAAC,CAAC;IAoBb,4CAA4C;IACtC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;YASvB,WAAW;YAIX,eAAe;IAoC7B,OAAO,CAAC,YAAY;YAuBN,WAAW;YAWX,gBAAgB;IAU9B,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"AAOA,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAExF,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAAS;gBAEjB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAIjD,sCAAsC;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIjD,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAuC9B,8CAA8C;IAC9C,IAAI,IAAI,IAAI;IAaZ,OAAO,CAAC,QAAQ;CAmEjB"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
interface FetchUrlOptions {
|
|
2
|
+
allowPrivate?: boolean;
|
|
3
|
+
}
|
|
4
|
+
/** Exported for unit tests. */
|
|
5
|
+
export declare function _isPrivateIpv4(address: string): boolean;
|
|
1
6
|
/**
|
|
2
7
|
* Fetch a URL to a cached temp file. Uses disk cache with 1-day TTL.
|
|
3
8
|
* Returns the cached file path the Rust outline/zoom command can read.
|
|
4
9
|
* Throws on errors (invalid URL, network failure, unsupported content type, oversized body).
|
|
5
10
|
*/
|
|
6
|
-
export declare function fetchUrlToTempFile(url: string, storageDir: string): Promise<string>;
|
|
11
|
+
export declare function fetchUrlToTempFile(url: string, storageDir: string, options?: FetchUrlOptions): Promise<string>;
|
|
7
12
|
/**
|
|
8
13
|
* Remove cache entries older than TTL. Called periodically at plugin startup.
|
|
9
14
|
*/
|
|
10
15
|
export declare function cleanupUrlCache(storageDir: string): void;
|
|
16
|
+
export {};
|
|
11
17
|
//# sourceMappingURL=url-fetch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-fetch.d.ts","sourceRoot":"","sources":["../../src/shared/url-fetch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"url-fetch.d.ts","sourceRoot":"","sources":["../../src/shared/url-fetch.ts"],"names":[],"mappings":"AAsBA,UAAU,eAAe;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAyBD,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAavD;AAoND;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAgGjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAkCxD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-only re-exports of internal helpers from hoisted.ts.
|
|
3
|
+
*
|
|
4
|
+
* Kept in a separate file so the production import surface of hoisted.ts
|
|
5
|
+
* stays focused. Bun's test runner imports this directly; the bundled
|
|
6
|
+
* plugin output never references this module.
|
|
7
|
+
*/
|
|
8
|
+
export { _buildUnifiedDiffForTest } from "./hoisted.js";
|
|
9
|
+
//# sourceMappingURL=hoisted-internals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hoisted-internals.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted-internals.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/tools/hoisted.d.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
12
12
|
import type { PluginContext } from "../types.js";
|
|
13
|
+
/** Test-only export. Production code uses buildUnifiedDiff directly. */
|
|
14
|
+
export declare const _buildUnifiedDiffForTest: (fp: string, before: string, after: string) => string;
|
|
13
15
|
/**
|
|
14
16
|
* Creates the simple read tool. Registers as "read" when hoisted, "aft_read" when not.
|
|
15
17
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAcjD,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,MAChD,CAAC;AA2OtC;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA4IjE;AAkxBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAS/E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAuBnF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reading.d.ts","sourceRoot":"","sources":["../../src/tools/reading.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA2CjD;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"reading.d.ts","sourceRoot":"","sources":["../../src/tools/reading.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA2CjD;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAuK/E"}
|
package/dist/tui.js
CHANGED
|
@@ -2,16 +2,73 @@
|
|
|
2
2
|
// src/shared/rpc-client.ts
|
|
3
3
|
import { readFileSync } from "fs";
|
|
4
4
|
|
|
5
|
+
// src/logger.ts
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
var TAG = "[aft-plugin]";
|
|
10
|
+
var logFile = path.join(os.tmpdir(), "aft-plugin.log");
|
|
11
|
+
var useStderr = process.env.AFT_LOG_STDERR === "1";
|
|
12
|
+
var buffer = [];
|
|
13
|
+
var flushTimer = null;
|
|
14
|
+
var FLUSH_INTERVAL_MS = 500;
|
|
15
|
+
var BUFFER_SIZE_LIMIT = 50;
|
|
16
|
+
function flush() {
|
|
17
|
+
if (buffer.length === 0)
|
|
18
|
+
return;
|
|
19
|
+
const data = buffer.join("");
|
|
20
|
+
buffer = [];
|
|
21
|
+
try {
|
|
22
|
+
if (useStderr) {
|
|
23
|
+
process.stderr.write(data);
|
|
24
|
+
} else {
|
|
25
|
+
fs.appendFileSync(logFile, data);
|
|
26
|
+
}
|
|
27
|
+
} catch {}
|
|
28
|
+
}
|
|
29
|
+
function scheduleFlush() {
|
|
30
|
+
if (flushTimer)
|
|
31
|
+
return;
|
|
32
|
+
flushTimer = setTimeout(() => {
|
|
33
|
+
flushTimer = null;
|
|
34
|
+
flush();
|
|
35
|
+
}, FLUSH_INTERVAL_MS);
|
|
36
|
+
if (flushTimer && typeof flushTimer === "object" && "unref" in flushTimer) {
|
|
37
|
+
flushTimer.unref();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function write(level, message, data) {
|
|
41
|
+
try {
|
|
42
|
+
const timestamp = new Date().toISOString();
|
|
43
|
+
const serialized = data === undefined ? "" : ` ${JSON.stringify(data)}`;
|
|
44
|
+
const line = `[${timestamp}] ${level} ${TAG} ${message}${serialized}
|
|
45
|
+
`;
|
|
46
|
+
if (useStderr) {
|
|
47
|
+
process.stderr.write(line);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
buffer.push(line);
|
|
51
|
+
if (buffer.length >= BUFFER_SIZE_LIMIT) {
|
|
52
|
+
flush();
|
|
53
|
+
} else {
|
|
54
|
+
scheduleFlush();
|
|
55
|
+
}
|
|
56
|
+
} catch {}
|
|
57
|
+
}
|
|
58
|
+
function warn(message, data) {
|
|
59
|
+
write("WARN", message, data);
|
|
60
|
+
}
|
|
61
|
+
|
|
5
62
|
// src/shared/rpc-utils.ts
|
|
6
63
|
import { createHash } from "crypto";
|
|
7
|
-
import { join } from "path";
|
|
64
|
+
import { join as join2 } from "path";
|
|
8
65
|
function projectHash(directory) {
|
|
9
66
|
const normalized = directory.replace(/\/+$/, "");
|
|
10
67
|
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
11
68
|
}
|
|
12
69
|
function rpcPortFilePath(storageDir, directory) {
|
|
13
70
|
const hash = projectHash(directory);
|
|
14
|
-
return
|
|
71
|
+
return join2(storageDir, "rpc", hash, "port");
|
|
15
72
|
}
|
|
16
73
|
|
|
17
74
|
// src/shared/rpc-client.ts
|
|
@@ -21,20 +78,21 @@ var REQUEST_TIMEOUT_MS = 5000;
|
|
|
21
78
|
|
|
22
79
|
class AftRpcClient {
|
|
23
80
|
port = null;
|
|
81
|
+
token = null;
|
|
24
82
|
portFilePath;
|
|
25
83
|
healthChecked = false;
|
|
26
84
|
constructor(storageDir, directory) {
|
|
27
85
|
this.portFilePath = rpcPortFilePath(storageDir, directory);
|
|
28
86
|
}
|
|
29
87
|
async call(method, params = {}) {
|
|
30
|
-
const
|
|
31
|
-
if (!
|
|
88
|
+
const info = await this.resolvePortInfo();
|
|
89
|
+
if (!info) {
|
|
32
90
|
throw new Error("AFT RPC server not available");
|
|
33
91
|
}
|
|
34
|
-
const response = await this.fetchWithTimeout(`http://127.0.0.1:${port}/rpc/${method}`, {
|
|
92
|
+
const response = await this.fetchWithTimeout(`http://127.0.0.1:${info.port}/rpc/${method}`, {
|
|
35
93
|
method: "POST",
|
|
36
94
|
headers: { "Content-Type": "application/json" },
|
|
37
|
-
body: JSON.stringify(params)
|
|
95
|
+
body: JSON.stringify({ ...params, token: info.token })
|
|
38
96
|
});
|
|
39
97
|
if (!response.ok) {
|
|
40
98
|
const text = await response.text();
|
|
@@ -51,26 +109,31 @@ class AftRpcClient {
|
|
|
51
109
|
}
|
|
52
110
|
}
|
|
53
111
|
async resolvePort() {
|
|
112
|
+
return (await this.resolvePortInfo())?.port ?? null;
|
|
113
|
+
}
|
|
114
|
+
async resolvePortInfo() {
|
|
54
115
|
if (this.port && this.healthChecked) {
|
|
55
|
-
return this.port;
|
|
116
|
+
return { port: this.port, token: this.token };
|
|
56
117
|
}
|
|
57
118
|
if (this.port) {
|
|
58
119
|
const alive = await this.healthCheck(this.port);
|
|
59
120
|
if (alive) {
|
|
60
121
|
this.healthChecked = true;
|
|
61
|
-
return this.port;
|
|
122
|
+
return { port: this.port, token: this.token };
|
|
62
123
|
}
|
|
63
124
|
this.port = null;
|
|
125
|
+
this.token = null;
|
|
64
126
|
this.healthChecked = false;
|
|
65
127
|
}
|
|
66
128
|
for (let attempt = 0;attempt < MAX_RETRIES; attempt++) {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
const alive = await this.healthCheck(port);
|
|
129
|
+
const info = this.readPortFile();
|
|
130
|
+
if (info) {
|
|
131
|
+
const alive = await this.healthCheck(info.port);
|
|
70
132
|
if (alive) {
|
|
71
|
-
this.port = port;
|
|
133
|
+
this.port = info.port;
|
|
134
|
+
this.token = info.token;
|
|
72
135
|
this.healthChecked = true;
|
|
73
|
-
return
|
|
136
|
+
return info;
|
|
74
137
|
}
|
|
75
138
|
}
|
|
76
139
|
if (attempt < MAX_RETRIES - 1) {
|
|
@@ -82,11 +145,21 @@ class AftRpcClient {
|
|
|
82
145
|
readPortFile() {
|
|
83
146
|
try {
|
|
84
147
|
const content = readFileSync(this.portFilePath, "utf-8").trim();
|
|
85
|
-
|
|
148
|
+
let port;
|
|
149
|
+
let token;
|
|
150
|
+
if (content.startsWith("{")) {
|
|
151
|
+
const parsed = JSON.parse(content);
|
|
152
|
+
port = typeof parsed.port === "number" ? parsed.port : Number.NaN;
|
|
153
|
+
token = typeof parsed.token === "string" ? parsed.token : null;
|
|
154
|
+
} else {
|
|
155
|
+
warn("RPC port file uses legacy integer format; unauthenticated RPC is deprecated");
|
|
156
|
+
port = Number.parseInt(content, 10);
|
|
157
|
+
token = null;
|
|
158
|
+
}
|
|
86
159
|
if (Number.isNaN(port) || port <= 0 || port > 65535) {
|
|
87
160
|
return null;
|
|
88
161
|
}
|
|
89
|
-
return port;
|
|
162
|
+
return { port, token };
|
|
90
163
|
} catch {
|
|
91
164
|
return null;
|
|
92
165
|
}
|
|
@@ -112,6 +185,7 @@ class AftRpcClient {
|
|
|
112
185
|
}
|
|
113
186
|
reset() {
|
|
114
187
|
this.port = null;
|
|
188
|
+
this.token = null;
|
|
115
189
|
this.healthChecked = false;
|
|
116
190
|
}
|
|
117
191
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/aft-opencode",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.5",
|
|
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",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"zod": "^4.1.8"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@cortexkit/aft-darwin-arm64": "0.15.
|
|
36
|
-
"@cortexkit/aft-darwin-x64": "0.15.
|
|
37
|
-
"@cortexkit/aft-linux-arm64": "0.15.
|
|
38
|
-
"@cortexkit/aft-linux-x64": "0.15.
|
|
39
|
-
"@cortexkit/aft-win32-x64": "0.15.
|
|
35
|
+
"@cortexkit/aft-darwin-arm64": "0.15.5",
|
|
36
|
+
"@cortexkit/aft-darwin-x64": "0.15.5",
|
|
37
|
+
"@cortexkit/aft-linux-arm64": "0.15.5",
|
|
38
|
+
"@cortexkit/aft-linux-x64": "0.15.5",
|
|
39
|
+
"@cortexkit/aft-win32-x64": "0.15.5"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^22.0.0",
|
package/src/shared/rpc-client.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
+
import { warn } from "../logger";
|
|
2
3
|
import { rpcPortFilePath } from "./rpc-utils";
|
|
3
4
|
|
|
4
5
|
const MAX_RETRIES = 10;
|
|
@@ -7,6 +8,7 @@ const REQUEST_TIMEOUT_MS = 5000;
|
|
|
7
8
|
|
|
8
9
|
export class AftRpcClient {
|
|
9
10
|
private port: number | null = null;
|
|
11
|
+
private token: string | null = null;
|
|
10
12
|
private portFilePath: string;
|
|
11
13
|
private healthChecked = false;
|
|
12
14
|
|
|
@@ -19,15 +21,15 @@ export class AftRpcClient {
|
|
|
19
21
|
method: string,
|
|
20
22
|
params: Record<string, unknown> = {},
|
|
21
23
|
): Promise<T> {
|
|
22
|
-
const
|
|
23
|
-
if (!
|
|
24
|
+
const info = await this.resolvePortInfo();
|
|
25
|
+
if (!info) {
|
|
24
26
|
throw new Error("AFT RPC server not available");
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
const response = await this.fetchWithTimeout(`http://127.0.0.1:${port}/rpc/${method}`, {
|
|
29
|
+
const response = await this.fetchWithTimeout(`http://127.0.0.1:${info.port}/rpc/${method}`, {
|
|
28
30
|
method: "POST",
|
|
29
31
|
headers: { "Content-Type": "application/json" },
|
|
30
|
-
body: JSON.stringify(params),
|
|
32
|
+
body: JSON.stringify({ ...params, token: info.token }),
|
|
31
33
|
});
|
|
32
34
|
|
|
33
35
|
if (!response.ok) {
|
|
@@ -49,28 +51,34 @@ export class AftRpcClient {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
private async resolvePort(): Promise<number | null> {
|
|
54
|
+
return (await this.resolvePortInfo())?.port ?? null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async resolvePortInfo(): Promise<{ port: number; token: string | null } | null> {
|
|
52
58
|
if (this.port && this.healthChecked) {
|
|
53
|
-
return this.port;
|
|
59
|
+
return { port: this.port, token: this.token };
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
if (this.port) {
|
|
57
63
|
const alive = await this.healthCheck(this.port);
|
|
58
64
|
if (alive) {
|
|
59
65
|
this.healthChecked = true;
|
|
60
|
-
return this.port;
|
|
66
|
+
return { port: this.port, token: this.token };
|
|
61
67
|
}
|
|
62
68
|
this.port = null;
|
|
69
|
+
this.token = null;
|
|
63
70
|
this.healthChecked = false;
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
const alive = await this.healthCheck(port);
|
|
74
|
+
const info = this.readPortFile();
|
|
75
|
+
if (info) {
|
|
76
|
+
const alive = await this.healthCheck(info.port);
|
|
70
77
|
if (alive) {
|
|
71
|
-
this.port = port;
|
|
78
|
+
this.port = info.port;
|
|
79
|
+
this.token = info.token;
|
|
72
80
|
this.healthChecked = true;
|
|
73
|
-
return
|
|
81
|
+
return info;
|
|
74
82
|
}
|
|
75
83
|
}
|
|
76
84
|
|
|
@@ -82,14 +90,24 @@ export class AftRpcClient {
|
|
|
82
90
|
return null;
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
private readPortFile(): number | null {
|
|
93
|
+
private readPortFile(): { port: number; token: string | null } | null {
|
|
86
94
|
try {
|
|
87
95
|
const content = readFileSync(this.portFilePath, "utf-8").trim();
|
|
88
|
-
|
|
96
|
+
let port: number;
|
|
97
|
+
let token: string | null;
|
|
98
|
+
if (content.startsWith("{")) {
|
|
99
|
+
const parsed = JSON.parse(content) as { port?: unknown; token?: unknown };
|
|
100
|
+
port = typeof parsed.port === "number" ? parsed.port : Number.NaN;
|
|
101
|
+
token = typeof parsed.token === "string" ? parsed.token : null;
|
|
102
|
+
} else {
|
|
103
|
+
warn("RPC port file uses legacy integer format; unauthenticated RPC is deprecated");
|
|
104
|
+
port = Number.parseInt(content, 10);
|
|
105
|
+
token = null;
|
|
106
|
+
}
|
|
89
107
|
if (Number.isNaN(port) || port <= 0 || port > 65535) {
|
|
90
108
|
return null;
|
|
91
109
|
}
|
|
92
|
-
return port;
|
|
110
|
+
return { port, token };
|
|
93
111
|
} catch {
|
|
94
112
|
return null;
|
|
95
113
|
}
|
|
@@ -118,6 +136,7 @@ export class AftRpcClient {
|
|
|
118
136
|
|
|
119
137
|
reset(): void {
|
|
120
138
|
this.port = null;
|
|
139
|
+
this.token = null;
|
|
121
140
|
this.healthChecked = false;
|
|
122
141
|
}
|
|
123
142
|
}
|
package/src/shared/rpc-server.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
1
2
|
import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
|
3
4
|
import { dirname } from "node:path";
|
|
@@ -9,6 +10,7 @@ type RpcHandler = (params: Record<string, unknown>) => Promise<Record<string, un
|
|
|
9
10
|
export class AftRpcServer {
|
|
10
11
|
private server: Server | null = null;
|
|
11
12
|
private port = 0;
|
|
13
|
+
private token: string | null = null;
|
|
12
14
|
private handlers = new Map<string, RpcHandler>();
|
|
13
15
|
private portFilePath: string;
|
|
14
16
|
|
|
@@ -38,6 +40,7 @@ export class AftRpcServer {
|
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
40
42
|
this.port = addr.port;
|
|
43
|
+
this.token = randomBytes(32).toString("hex");
|
|
41
44
|
this.server = server;
|
|
42
45
|
|
|
43
46
|
// Write port file atomically
|
|
@@ -45,7 +48,7 @@ export class AftRpcServer {
|
|
|
45
48
|
const dir = dirname(this.portFilePath);
|
|
46
49
|
mkdirSync(dir, { recursive: true });
|
|
47
50
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
48
|
-
writeFileSync(tmpPath,
|
|
51
|
+
writeFileSync(tmpPath, JSON.stringify({ port: this.port, token: this.token }), "utf-8");
|
|
49
52
|
renameSync(tmpPath, this.portFilePath);
|
|
50
53
|
log(`RPC server listening on 127.0.0.1:${this.port}`);
|
|
51
54
|
} catch (err) {
|
|
@@ -66,6 +69,7 @@ export class AftRpcServer {
|
|
|
66
69
|
this.server.close();
|
|
67
70
|
this.server = null;
|
|
68
71
|
}
|
|
72
|
+
this.token = null;
|
|
69
73
|
try {
|
|
70
74
|
unlinkSync(this.portFilePath);
|
|
71
75
|
} catch {
|
|
@@ -118,8 +122,16 @@ export class AftRpcServer {
|
|
|
118
122
|
return;
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
if (params.token !== this.token) {
|
|
126
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
127
|
+
res.end(JSON.stringify({ error: "Forbidden" }));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const { token: _token, ...handlerParams } = params;
|
|
132
|
+
|
|
133
|
+
log(`RPC call: ${method} params=${JSON.stringify(handlerParams).slice(0, 200)}`);
|
|
134
|
+
handler(handlerParams)
|
|
123
135
|
.then((result) => {
|
|
124
136
|
log(`RPC result: ${method} => ${JSON.stringify(result).slice(0, 200)}`);
|
|
125
137
|
res.writeHead(200, { "Content-Type": "application/json" });
|