@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.
@@ -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;AA8LD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3D"}
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"}
@@ -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;IAmBnD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAqDA;;;;;;;;;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
+ {"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":"AAOA,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAAuB;IACnC,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;IAkCzB,OAAO,CAAC,YAAY;YAaN,WAAW;YAWX,gBAAgB;IAU9B,KAAK,IAAI,IAAI;CAId"}
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"}
@@ -2,6 +2,7 @@ type RpcHandler = (params: Record<string, unknown>) => Promise<Record<string, un
2
2
  export declare class AftRpcServer {
3
3
  private server;
4
4
  private port;
5
+ private token;
5
6
  private handlers;
6
7
  private portFilePath;
7
8
  constructor(storageDir: string, directory: string);
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"AAMA,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,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;IAsC9B,8CAA8C;IAC9C,IAAI,IAAI,IAAI;IAYZ,OAAO,CAAC,QAAQ;CA2DjB"}
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":"AA4EA;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoHzF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAkCxD"}
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"}
@@ -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;AA6EjD;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA4IjE;AAutBD;;;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
+ {"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,CAmK/E"}
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 join(storageDir, "rpc", hash, "port");
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 port = await this.resolvePort();
31
- if (!port) {
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 port = this.readPortFile();
68
- if (port) {
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 port;
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
- const port = Number.parseInt(content, 10);
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",
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.3",
36
- "@cortexkit/aft-darwin-x64": "0.15.3",
37
- "@cortexkit/aft-linux-arm64": "0.15.3",
38
- "@cortexkit/aft-linux-x64": "0.15.3",
39
- "@cortexkit/aft-win32-x64": "0.15.3"
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",
@@ -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 port = await this.resolvePort();
23
- if (!port) {
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 port = this.readPortFile();
68
- if (port) {
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 port;
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
- const port = Number.parseInt(content, 10);
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
  }
@@ -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, String(this.port), "utf-8");
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
- log(`RPC call: ${method} params=${JSON.stringify(params).slice(0, 200)}`);
122
- handler(params)
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" });