@github/copilot-sdk 1.0.7-preview.0 → 1.0.7-preview.2
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/cjs/client.js +162 -36
- package/dist/cjs/copilotRequestHandler.js +9 -0
- package/dist/cjs/ffiRuntimeHost.js +282 -0
- package/dist/cjs/generated/rpc.js +32 -3
- package/dist/cjs/types.js +12 -0
- package/dist/client.d.ts +49 -0
- package/dist/client.js +152 -36
- package/dist/copilotRequestHandler.d.ts +3 -0
- package/dist/copilotRequestHandler.js +9 -0
- package/dist/ffiRuntimeHost.d.ts +40 -0
- package/dist/ffiRuntimeHost.js +248 -0
- package/dist/generated/rpc.d.ts +325 -9
- package/dist/generated/rpc.js +32 -3
- package/dist/generated/session-events.d.ts +100 -1
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +53 -1
- package/dist/types.js +12 -0
- package/package.json +3 -2
package/dist/cjs/client.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
var client_exports = {};
|
|
20
30
|
__export(client_exports, {
|
|
@@ -246,6 +256,7 @@ class TeardownResilientStreamMessageWriter extends import_node.StreamMessageWrit
|
|
|
246
256
|
class CopilotClient {
|
|
247
257
|
cliStartTimeout = null;
|
|
248
258
|
cliProcess = null;
|
|
259
|
+
ffiHost = null;
|
|
249
260
|
connection = null;
|
|
250
261
|
messageWriter = null;
|
|
251
262
|
socket = null;
|
|
@@ -322,6 +333,29 @@ class CopilotClient {
|
|
|
322
333
|
`);
|
|
323
334
|
}
|
|
324
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Environment variable that overrides the transport when the caller does not set
|
|
338
|
+
* {@link CopilotClientOptions.connection}. Accepts `"inprocess"` or `"stdio"`
|
|
339
|
+
* (case-insensitive); unset preserves the default stdio transport. Any other value
|
|
340
|
+
* is an error.
|
|
341
|
+
*/
|
|
342
|
+
static DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION";
|
|
343
|
+
/**
|
|
344
|
+
* Resolves the default {@link RuntimeConnection} for the no-connection case,
|
|
345
|
+
* honoring {@link CopilotClient.DEFAULT_CONNECTION_ENV_VAR}.
|
|
346
|
+
*/
|
|
347
|
+
static resolveDefaultConnection() {
|
|
348
|
+
const value = process.env[CopilotClient.DEFAULT_CONNECTION_ENV_VAR];
|
|
349
|
+
if (!value || value.toLowerCase() === "stdio") {
|
|
350
|
+
return { kind: "stdio" };
|
|
351
|
+
}
|
|
352
|
+
if (value.toLowerCase() === "inprocess") {
|
|
353
|
+
return { kind: "inprocess" };
|
|
354
|
+
}
|
|
355
|
+
throw new Error(
|
|
356
|
+
`Invalid ${CopilotClient.DEFAULT_CONNECTION_ENV_VAR} value '${value}'. Expected 'inprocess', 'stdio', or unset.`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
325
359
|
/**
|
|
326
360
|
* Creates a new CopilotClient instance.
|
|
327
361
|
*
|
|
@@ -350,12 +384,17 @@ class CopilotClient {
|
|
|
350
384
|
* ```
|
|
351
385
|
*/
|
|
352
386
|
constructor(options = {}) {
|
|
353
|
-
const conn = options._internalConnection ?? options.connection ??
|
|
387
|
+
const conn = options._internalConnection ?? options.connection ?? CopilotClient.resolveDefaultConnection();
|
|
354
388
|
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
355
389
|
throw new Error(
|
|
356
390
|
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
357
391
|
);
|
|
358
392
|
}
|
|
393
|
+
if (conn.kind === "inprocess" && options.workingDirectory !== void 0) {
|
|
394
|
+
throw new Error(
|
|
395
|
+
"workingDirectory is not supported with RuntimeConnection.forInProcess(): the in-process transport hosts the runtime in this process, so honoring it would require mutating the shared process-global cwd. Change the host process's working directory before constructing the client instead."
|
|
396
|
+
);
|
|
397
|
+
}
|
|
359
398
|
if (conn.kind === "tcp" && conn.connectionToken !== void 0) {
|
|
360
399
|
if (typeof conn.connectionToken !== "string" || conn.connectionToken.length === 0) {
|
|
361
400
|
throw new Error("connectionToken must be a non-empty string");
|
|
@@ -511,7 +550,9 @@ class CopilotClient {
|
|
|
511
550
|
}
|
|
512
551
|
this.state = "connecting";
|
|
513
552
|
try {
|
|
514
|
-
if (
|
|
553
|
+
if (this.connectionConfig.kind === "inprocess") {
|
|
554
|
+
await this.startInProcessFfi();
|
|
555
|
+
} else if (!this.isExternalServer) {
|
|
515
556
|
await this.startCLIServer();
|
|
516
557
|
}
|
|
517
558
|
await this.connectToServer();
|
|
@@ -560,6 +601,9 @@ class CopilotClient {
|
|
|
560
601
|
async stop() {
|
|
561
602
|
const errors = [];
|
|
562
603
|
const activeSessions = [...this.sessions.values()];
|
|
604
|
+
if (this.connectionConfig.kind === "inprocess") {
|
|
605
|
+
await Promise.allSettled(activeSessions.map((session) => session.abort()));
|
|
606
|
+
}
|
|
563
607
|
for (const session of activeSessions) {
|
|
564
608
|
const sessionId = session.sessionId;
|
|
565
609
|
let lastError = null;
|
|
@@ -588,7 +632,7 @@ class CopilotClient {
|
|
|
588
632
|
session._markDisconnected();
|
|
589
633
|
}
|
|
590
634
|
this.sessions.clear();
|
|
591
|
-
if (this.connection && this.cliProcess && !this.isExternalServer) {
|
|
635
|
+
if (this.connection && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
|
|
592
636
|
const runtimeShutdownStart = Date.now();
|
|
593
637
|
const shutdownPromise = this.rpc.runtime.shutdown();
|
|
594
638
|
void shutdownPromise.catch(() => void 0);
|
|
@@ -673,6 +717,19 @@ class CopilotClient {
|
|
|
673
717
|
);
|
|
674
718
|
}
|
|
675
719
|
}
|
|
720
|
+
if (this.ffiHost) {
|
|
721
|
+
const host = this.ffiHost;
|
|
722
|
+
this.ffiHost = null;
|
|
723
|
+
try {
|
|
724
|
+
host.dispose();
|
|
725
|
+
} catch (error) {
|
|
726
|
+
errors.push(
|
|
727
|
+
new Error(
|
|
728
|
+
`Failed to dispose in-process runtime host: ${error instanceof Error ? error.message : String(error)}`
|
|
729
|
+
)
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
676
733
|
if (this.cliStartTimeout) {
|
|
677
734
|
clearTimeout(this.cliStartTimeout);
|
|
678
735
|
this.cliStartTimeout = null;
|
|
@@ -757,6 +814,13 @@ class CopilotClient {
|
|
|
757
814
|
}
|
|
758
815
|
this.cliProcess = null;
|
|
759
816
|
}
|
|
817
|
+
if (this.ffiHost) {
|
|
818
|
+
try {
|
|
819
|
+
this.ffiHost.dispose();
|
|
820
|
+
} catch {
|
|
821
|
+
}
|
|
822
|
+
this.ffiHost = null;
|
|
823
|
+
}
|
|
760
824
|
if (this.cliStartTimeout) {
|
|
761
825
|
clearTimeout(this.cliStartTimeout);
|
|
762
826
|
this.cliStartTimeout = null;
|
|
@@ -1001,6 +1065,7 @@ class CopilotClient {
|
|
|
1001
1065
|
requestExtensions: config.requestExtensions,
|
|
1002
1066
|
extensionSdkPath: config.extensionSdkPath,
|
|
1003
1067
|
extensionInfo: config.extensionInfo,
|
|
1068
|
+
canvasProvider: config.canvasProvider,
|
|
1004
1069
|
commands: config.commands?.map((cmd) => ({
|
|
1005
1070
|
name: cmd.name,
|
|
1006
1071
|
description: cmd.description
|
|
@@ -1198,6 +1263,7 @@ class CopilotClient {
|
|
|
1198
1263
|
requestExtensions: config.requestExtensions,
|
|
1199
1264
|
extensionSdkPath: config.extensionSdkPath,
|
|
1200
1265
|
extensionInfo: config.extensionInfo,
|
|
1266
|
+
canvasProvider: config.canvasProvider,
|
|
1201
1267
|
commands: config.commands?.map((cmd) => ({
|
|
1202
1268
|
name: cmd.name,
|
|
1203
1269
|
description: cmd.description
|
|
@@ -1596,6 +1662,41 @@ class CopilotClient {
|
|
|
1596
1662
|
this.sessionLifecycleHandlers.delete(wildcardHandler);
|
|
1597
1663
|
};
|
|
1598
1664
|
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Builds the environment for the spawned runtime child process (stdio/TCP): applies
|
|
1667
|
+
* the auth token, connection token, `COPILOT_HOME`, keychain setting, and telemetry
|
|
1668
|
+
* variables on top of the effective env. Not used by the in-process (FFI) transport,
|
|
1669
|
+
* whose worker inherits the host process's ambient environment
|
|
1670
|
+
* (see {@link CopilotClient.startInProcessFfi}).
|
|
1671
|
+
*/
|
|
1672
|
+
buildRuntimeEnv() {
|
|
1673
|
+
const env = { ...this.resolvedEnv };
|
|
1674
|
+
delete env.NODE_DEBUG;
|
|
1675
|
+
if (this.options.gitHubToken) {
|
|
1676
|
+
env.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1677
|
+
}
|
|
1678
|
+
if (this.effectiveConnectionToken) {
|
|
1679
|
+
env.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1680
|
+
}
|
|
1681
|
+
if (this.options.baseDirectory) {
|
|
1682
|
+
env.COPILOT_HOME = this.options.baseDirectory;
|
|
1683
|
+
}
|
|
1684
|
+
if (this.options.mode === "empty") {
|
|
1685
|
+
env.COPILOT_DISABLE_KEYTAR = "1";
|
|
1686
|
+
}
|
|
1687
|
+
if (this.options.telemetry) {
|
|
1688
|
+
const t = this.options.telemetry;
|
|
1689
|
+
env.COPILOT_OTEL_ENABLED = "true";
|
|
1690
|
+
if (t.otlpEndpoint !== void 0) env.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
|
|
1691
|
+
if (t.otlpProtocol !== void 0) env.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
|
|
1692
|
+
if (t.filePath !== void 0) env.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
|
|
1693
|
+
if (t.exporterType !== void 0) env.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
|
|
1694
|
+
if (t.sourceName !== void 0) env.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
|
|
1695
|
+
if (t.captureContent !== void 0)
|
|
1696
|
+
env.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(t.captureContent);
|
|
1697
|
+
}
|
|
1698
|
+
return env;
|
|
1699
|
+
}
|
|
1599
1700
|
/**
|
|
1600
1701
|
* Start the CLI server process
|
|
1601
1702
|
*/
|
|
@@ -1629,43 +1730,12 @@ class CopilotClient {
|
|
|
1629
1730
|
if (this.options.enableRemoteSessions) {
|
|
1630
1731
|
args.push("--remote");
|
|
1631
1732
|
}
|
|
1632
|
-
const envWithoutNodeDebug =
|
|
1633
|
-
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
1634
|
-
if (this.options.gitHubToken) {
|
|
1635
|
-
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
1636
|
-
}
|
|
1637
|
-
if (this.effectiveConnectionToken) {
|
|
1638
|
-
envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken;
|
|
1639
|
-
}
|
|
1640
|
-
if (this.options.baseDirectory) {
|
|
1641
|
-
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1642
|
-
}
|
|
1643
|
-
if (this.options.mode === "empty") {
|
|
1644
|
-
envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
|
|
1645
|
-
}
|
|
1733
|
+
const envWithoutNodeDebug = this.buildRuntimeEnv();
|
|
1646
1734
|
if (!this.resolvedCliPath) {
|
|
1647
1735
|
throw new Error(
|
|
1648
1736
|
"Path to Copilot CLI is required. Please supply it via `RuntimeConnection.forStdio({ path })` or `RuntimeConnection.forTcp({ path })`, set the COPILOT_CLI_PATH environment variable, or use `RuntimeConnection.forUri(...)` to connect to an already-running runtime."
|
|
1649
1737
|
);
|
|
1650
1738
|
}
|
|
1651
|
-
if (this.options.telemetry) {
|
|
1652
|
-
const t = this.options.telemetry;
|
|
1653
|
-
envWithoutNodeDebug.COPILOT_OTEL_ENABLED = "true";
|
|
1654
|
-
if (t.otlpEndpoint !== void 0)
|
|
1655
|
-
envWithoutNodeDebug.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
|
|
1656
|
-
if (t.otlpProtocol !== void 0)
|
|
1657
|
-
envWithoutNodeDebug.OTEL_EXPORTER_OTLP_PROTOCOL = t.otlpProtocol;
|
|
1658
|
-
if (t.filePath !== void 0)
|
|
1659
|
-
envWithoutNodeDebug.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
|
|
1660
|
-
if (t.exporterType !== void 0)
|
|
1661
|
-
envWithoutNodeDebug.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
|
|
1662
|
-
if (t.sourceName !== void 0)
|
|
1663
|
-
envWithoutNodeDebug.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
|
|
1664
|
-
if (t.captureContent !== void 0)
|
|
1665
|
-
envWithoutNodeDebug.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(
|
|
1666
|
-
t.captureContent
|
|
1667
|
-
);
|
|
1668
|
-
}
|
|
1669
1739
|
if (!(0, import_node_fs.existsSync)(this.resolvedCliPath)) {
|
|
1670
1740
|
throw new Error(
|
|
1671
1741
|
`Copilot CLI not found at ${this.resolvedCliPath}. Ensure @github/copilot is installed.`
|
|
@@ -1784,11 +1854,66 @@ stderr: ${stderrOutput}`
|
|
|
1784
1854
|
return this.connectToParentProcessViaStdio();
|
|
1785
1855
|
case "stdio":
|
|
1786
1856
|
return this.connectToChildProcessViaStdio();
|
|
1857
|
+
case "inprocess":
|
|
1858
|
+
return this.connectViaFfi();
|
|
1787
1859
|
case "tcp":
|
|
1788
1860
|
case "uri":
|
|
1789
1861
|
return this.connectViaTcp();
|
|
1790
1862
|
}
|
|
1791
1863
|
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Start the in-process FFI runtime host: resolve the CLI entrypoint and native
|
|
1866
|
+
* runtime library, then let the native host spawn the CLI worker.
|
|
1867
|
+
*
|
|
1868
|
+
* The worker inherits this host process's ambient environment; per-client options
|
|
1869
|
+
* that lower to environment variables (`env`, `telemetry`, `gitHubToken`,
|
|
1870
|
+
* `baseDirectory`) are intentionally not applied here, because the native runtime
|
|
1871
|
+
* loads into the shared host process and a single env block cannot carry per-client
|
|
1872
|
+
* values. Configure the in-process runtime via the host process environment instead.
|
|
1873
|
+
* See https://github.com/github/copilot-sdk/issues/1934.
|
|
1874
|
+
*/
|
|
1875
|
+
async startInProcessFfi() {
|
|
1876
|
+
const entrypoint = this.resolveCliPathForFfi();
|
|
1877
|
+
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
1878
|
+
const host = FfiRuntimeHost.create(entrypoint, CopilotClient.getNapiPrebuildsFolder());
|
|
1879
|
+
this.ffiHost = host;
|
|
1880
|
+
await host.start();
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Connect to the in-process FFI runtime host over its receive/send streams,
|
|
1884
|
+
* reusing the same `vscode-jsonrpc` framing as the stdio transport.
|
|
1885
|
+
*/
|
|
1886
|
+
async connectViaFfi() {
|
|
1887
|
+
if (!this.ffiHost) {
|
|
1888
|
+
throw new Error("In-process FFI runtime host not started");
|
|
1889
|
+
}
|
|
1890
|
+
this.messageWriter = new TeardownResilientStreamMessageWriter(this.ffiHost.sendStream);
|
|
1891
|
+
this.connection = (0, import_node.createMessageConnection)(
|
|
1892
|
+
new import_node.StreamMessageReader(this.ffiHost.receiveStream),
|
|
1893
|
+
this.messageWriter
|
|
1894
|
+
);
|
|
1895
|
+
this.attachConnectionHandlers();
|
|
1896
|
+
this.connection.listen();
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Resolves the CLI entrypoint used for in-process FFI hosting: `COPILOT_CLI_PATH`
|
|
1900
|
+
* when set, otherwise the bundled platform-package entrypoint.
|
|
1901
|
+
*/
|
|
1902
|
+
resolveCliPathForFfi() {
|
|
1903
|
+
return this.resolvedEnv.COPILOT_CLI_PATH ?? getBundledCliPath();
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Returns the napi prebuilds folder name for the current host — the
|
|
1907
|
+
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
1908
|
+
* `linux-x64`) under which the runtime ships `prebuilds/<folder>/runtime.node`.
|
|
1909
|
+
*/
|
|
1910
|
+
static getNapiPrebuildsFolder() {
|
|
1911
|
+
const arch = process.arch;
|
|
1912
|
+
if (arch !== "x64" && arch !== "arm64") {
|
|
1913
|
+
throw new Error(`Unsupported architecture '${arch}' for in-process FFI hosting.`);
|
|
1914
|
+
}
|
|
1915
|
+
return `${process.platform}-${arch}`;
|
|
1916
|
+
}
|
|
1792
1917
|
/**
|
|
1793
1918
|
* Connect to child via stdio pipes
|
|
1794
1919
|
*/
|
|
@@ -1910,8 +2035,9 @@ stderr: ${stderrOutput}`
|
|
|
1910
2035
|
return;
|
|
1911
2036
|
}
|
|
1912
2037
|
const session = this.sessions.get(notification.sessionId);
|
|
2038
|
+
const event = notification.event;
|
|
1913
2039
|
if (session) {
|
|
1914
|
-
session._dispatchEvent(
|
|
2040
|
+
session._dispatchEvent(event);
|
|
1915
2041
|
}
|
|
1916
2042
|
}
|
|
1917
2043
|
handleSessionLifecycleNotification(notification) {
|
|
@@ -180,6 +180,9 @@ class CopilotRequestHandler {
|
|
|
180
180
|
const ctx = {
|
|
181
181
|
requestId: exchange.requestId,
|
|
182
182
|
sessionId: exchange.sessionId,
|
|
183
|
+
agentId: exchange.agentId,
|
|
184
|
+
parentAgentId: exchange.parentAgentId,
|
|
185
|
+
interactionType: exchange.interactionType,
|
|
183
186
|
transport: exchange.transport,
|
|
184
187
|
url: exchange.url,
|
|
185
188
|
headers: exchange.headers,
|
|
@@ -305,6 +308,9 @@ function routeChunk(exchange, params) {
|
|
|
305
308
|
class CopilotRequestExchange {
|
|
306
309
|
requestId;
|
|
307
310
|
sessionId;
|
|
311
|
+
agentId;
|
|
312
|
+
parentAgentId;
|
|
313
|
+
interactionType;
|
|
308
314
|
method = "GET";
|
|
309
315
|
url = "";
|
|
310
316
|
headers = {};
|
|
@@ -324,6 +330,9 @@ class CopilotRequestExchange {
|
|
|
324
330
|
/** Fill in the request context once the matching start frame arrives. */
|
|
325
331
|
setContext(params) {
|
|
326
332
|
this.sessionId = params.sessionId;
|
|
333
|
+
this.agentId = params.agentId;
|
|
334
|
+
this.parentAgentId = params.parentAgentId;
|
|
335
|
+
this.interactionType = params.interactionType;
|
|
327
336
|
this.method = params.method;
|
|
328
337
|
this.url = params.url;
|
|
329
338
|
this.headers = params.headers;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var ffiRuntimeHost_exports = {};
|
|
30
|
+
__export(ffiRuntimeHost_exports, {
|
|
31
|
+
FfiRuntimeHost: () => FfiRuntimeHost
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(ffiRuntimeHost_exports);
|
|
34
|
+
var import_node_fs = require("node:fs");
|
|
35
|
+
var import_koffi = __toESM(require("koffi"), 1);
|
|
36
|
+
var import_node_path = require("node:path");
|
|
37
|
+
var import_node_stream = require("node:stream");
|
|
38
|
+
const SYMBOL_PREFIX = "copilot_runtime_";
|
|
39
|
+
const KEEP_ALIVE_INTERVAL_MS = 1 << 30;
|
|
40
|
+
let loadedLibraryPath;
|
|
41
|
+
let loadedLibrary;
|
|
42
|
+
function loadLibrary(libraryPath) {
|
|
43
|
+
if (loadedLibrary) {
|
|
44
|
+
if (loadedLibraryPath !== libraryPath) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`An in-process FFI runtime library is already loaded from '${loadedLibraryPath}'; loading a different library from '${libraryPath}' in the same process is not supported.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return loadedLibrary;
|
|
50
|
+
}
|
|
51
|
+
const lib = import_koffi.default.load(libraryPath);
|
|
52
|
+
const outboundCallbackType = import_koffi.default.pointer(
|
|
53
|
+
import_koffi.default.proto(
|
|
54
|
+
`void ${SYMBOL_PREFIX}outbound(void *userData, uint8 *bytesPtr, size_t bytesLen)`
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
loadedLibrary = {
|
|
58
|
+
hostStart: lib.func(`${SYMBOL_PREFIX}host_start`, "uint32", [
|
|
59
|
+
"uint8*",
|
|
60
|
+
"size_t",
|
|
61
|
+
"uint8*",
|
|
62
|
+
"size_t"
|
|
63
|
+
]),
|
|
64
|
+
hostShutdown: lib.func(`${SYMBOL_PREFIX}host_shutdown`, "bool", ["uint32"]),
|
|
65
|
+
connectionOpen: lib.func(`${SYMBOL_PREFIX}connection_open`, "uint32", [
|
|
66
|
+
"uint32",
|
|
67
|
+
outboundCallbackType,
|
|
68
|
+
"void*",
|
|
69
|
+
"uint8*",
|
|
70
|
+
"size_t",
|
|
71
|
+
"uint8*",
|
|
72
|
+
"size_t",
|
|
73
|
+
"uint8*",
|
|
74
|
+
"size_t"
|
|
75
|
+
]),
|
|
76
|
+
connectionWrite: lib.func(`${SYMBOL_PREFIX}connection_write`, "bool", [
|
|
77
|
+
"uint32",
|
|
78
|
+
"uint8*",
|
|
79
|
+
"size_t"
|
|
80
|
+
]),
|
|
81
|
+
connectionClose: lib.func(`${SYMBOL_PREFIX}connection_close`, "bool", ["uint32"]),
|
|
82
|
+
outboundCallbackType
|
|
83
|
+
};
|
|
84
|
+
loadedLibraryPath = libraryPath;
|
|
85
|
+
return loadedLibrary;
|
|
86
|
+
}
|
|
87
|
+
function buildArgvJson(cliEntrypoint) {
|
|
88
|
+
const argv = cliEntrypoint.toLowerCase().endsWith(".js") ? ["node", cliEntrypoint, "--embedded-host", "--no-auto-update"] : [cliEntrypoint, "--embedded-host", "--no-auto-update"];
|
|
89
|
+
return Buffer.from(JSON.stringify(argv), "utf8");
|
|
90
|
+
}
|
|
91
|
+
function buildEnvJson(environment) {
|
|
92
|
+
if (!environment) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const obj = {};
|
|
96
|
+
for (const [key, value] of Object.entries(environment)) {
|
|
97
|
+
if (value !== void 0) {
|
|
98
|
+
obj[key] = value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (Object.keys(obj).length === 0) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return Buffer.from(JSON.stringify(obj), "utf8");
|
|
105
|
+
}
|
|
106
|
+
class FfiRuntimeHost {
|
|
107
|
+
constructor(libraryPath, cliEntrypoint, environment) {
|
|
108
|
+
this.libraryPath = libraryPath;
|
|
109
|
+
this.cliEntrypoint = cliEntrypoint;
|
|
110
|
+
this.environment = environment;
|
|
111
|
+
this.lib = loadLibrary(libraryPath);
|
|
112
|
+
this.receiveStream = new import_node_stream.PassThrough();
|
|
113
|
+
this.sendStream = new import_node_stream.Writable({
|
|
114
|
+
// connection_write enqueues the frame into the runtime's inbound channel and
|
|
115
|
+
// returns immediately, so a synchronous FFI call is sufficient here.
|
|
116
|
+
write: (chunk, _encoding, callback) => {
|
|
117
|
+
try {
|
|
118
|
+
this.writeFrame(chunk);
|
|
119
|
+
callback();
|
|
120
|
+
} catch (error) {
|
|
121
|
+
callback(error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
libraryPath;
|
|
127
|
+
cliEntrypoint;
|
|
128
|
+
environment;
|
|
129
|
+
lib;
|
|
130
|
+
serverId = 0;
|
|
131
|
+
connectionId = 0;
|
|
132
|
+
disposed = false;
|
|
133
|
+
outboundCallback;
|
|
134
|
+
keepAliveTimer;
|
|
135
|
+
/** The stream JSON-RPC reads server→client frames from. */
|
|
136
|
+
receiveStream;
|
|
137
|
+
/** The stream JSON-RPC writes client→server frames to. */
|
|
138
|
+
sendStream;
|
|
139
|
+
/**
|
|
140
|
+
* Resolves the cdylib next to the given CLI entrypoint and prepares the FFI host.
|
|
141
|
+
* The cdylib is resolved as `prebuilds/<prebuildsFolder>/runtime.node` relative to
|
|
142
|
+
* the entrypoint directory (the napi-rs `<node-platform>-<arch>` layout, e.g.
|
|
143
|
+
* `linux-x64`). Throws if it cannot be found.
|
|
144
|
+
*/
|
|
145
|
+
static create(cliEntrypoint, prebuildsFolder, environment) {
|
|
146
|
+
const fullEntrypoint = (0, import_node_path.resolve)(cliEntrypoint);
|
|
147
|
+
const distDir = (0, import_node_path.dirname)(fullEntrypoint);
|
|
148
|
+
const libraryPath = (0, import_node_path.join)(distDir, "prebuilds", prebuildsFolder, "runtime.node");
|
|
149
|
+
if (!(0, import_node_fs.existsSync)(libraryPath)) {
|
|
150
|
+
throw new Error(`FFI runtime library not found. Looked for '${libraryPath}'.`);
|
|
151
|
+
}
|
|
152
|
+
return new FfiRuntimeHost(libraryPath, fullEntrypoint, environment);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Starts the in-process runtime: spawns the CLI worker via the native host,
|
|
156
|
+
* waits for readiness, and opens the FFI JSON-RPC connection.
|
|
157
|
+
*/
|
|
158
|
+
async start() {
|
|
159
|
+
const argvJson = buildArgvJson(this.cliEntrypoint);
|
|
160
|
+
const envJson = buildEnvJson(this.environment);
|
|
161
|
+
this.serverId = await new Promise((resolvePromise, rejectPromise) => {
|
|
162
|
+
this.lib.hostStart.async(
|
|
163
|
+
argvJson,
|
|
164
|
+
argvJson.length,
|
|
165
|
+
envJson,
|
|
166
|
+
envJson ? envJson.length : 0,
|
|
167
|
+
(error, result) => {
|
|
168
|
+
if (error) {
|
|
169
|
+
rejectPromise(error);
|
|
170
|
+
} else {
|
|
171
|
+
resolvePromise(result);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
if (!this.serverId) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`copilot_runtime_host_start failed (library '${this.libraryPath}', entrypoint '${this.cliEntrypoint}').`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
this.outboundCallback = import_koffi.default.register(
|
|
182
|
+
(_userData, bytesPtr, bytesLen) => this.feedInbound(bytesPtr, bytesLen),
|
|
183
|
+
this.lib.outboundCallbackType
|
|
184
|
+
);
|
|
185
|
+
this.connectionId = this.lib.connectionOpen(
|
|
186
|
+
this.serverId,
|
|
187
|
+
this.outboundCallback,
|
|
188
|
+
null,
|
|
189
|
+
null,
|
|
190
|
+
0,
|
|
191
|
+
null,
|
|
192
|
+
0,
|
|
193
|
+
null,
|
|
194
|
+
0
|
|
195
|
+
);
|
|
196
|
+
if (!this.connectionId) {
|
|
197
|
+
this.unregisterCallback();
|
|
198
|
+
this.lib.hostShutdown(this.serverId);
|
|
199
|
+
this.serverId = 0;
|
|
200
|
+
throw new Error("copilot_runtime_connection_open failed.");
|
|
201
|
+
}
|
|
202
|
+
this.keepAliveTimer = setInterval(() => {
|
|
203
|
+
}, KEEP_ALIVE_INTERVAL_MS);
|
|
204
|
+
}
|
|
205
|
+
writeFrame(frame) {
|
|
206
|
+
if (this.disposed || !this.connectionId) {
|
|
207
|
+
throw new Error("The in-process runtime connection is closed.");
|
|
208
|
+
}
|
|
209
|
+
const ok = this.lib.connectionWrite(this.connectionId, frame, frame.length);
|
|
210
|
+
if (!ok) {
|
|
211
|
+
throw new Error("Failed to write a frame to the in-process runtime connection.");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Native outbound (server→client) callback. koffi delivers it on the JS event loop
|
|
216
|
+
* via a threadsafe function, so the frame is decoded and written straight to
|
|
217
|
+
* {@link receiveStream}. The native pointer is only valid for this call, so the
|
|
218
|
+
* bytes are copied out before returning.
|
|
219
|
+
*/
|
|
220
|
+
feedInbound(bytesPtr, bytesLen) {
|
|
221
|
+
try {
|
|
222
|
+
if (this.disposed || this.receiveStream.writableEnded) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const length = Number(bytesLen);
|
|
226
|
+
if (!bytesPtr || length <= 0) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const bytes = import_koffi.default.decode(
|
|
230
|
+
bytesPtr,
|
|
231
|
+
import_koffi.default.array("uint8", length, "Typed")
|
|
232
|
+
);
|
|
233
|
+
this.receiveStream.write(Buffer.from(bytes));
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error(
|
|
236
|
+
`In-process FFI inbound callback failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
unregisterCallback() {
|
|
241
|
+
if (this.outboundCallback === void 0) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const callback = this.outboundCallback;
|
|
245
|
+
this.outboundCallback = void 0;
|
|
246
|
+
try {
|
|
247
|
+
import_koffi.default.unregister(callback);
|
|
248
|
+
} catch {
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** Closes the FFI connection, shuts down the native host, and releases resources. */
|
|
252
|
+
dispose() {
|
|
253
|
+
if (this.disposed) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
this.disposed = true;
|
|
257
|
+
if (this.keepAliveTimer !== void 0) {
|
|
258
|
+
clearInterval(this.keepAliveTimer);
|
|
259
|
+
this.keepAliveTimer = void 0;
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
if (this.connectionId) {
|
|
263
|
+
this.lib.connectionClose(this.connectionId);
|
|
264
|
+
this.connectionId = 0;
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
if (this.serverId) {
|
|
270
|
+
this.lib.hostShutdown(this.serverId);
|
|
271
|
+
this.serverId = 0;
|
|
272
|
+
}
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
this.receiveStream.end();
|
|
276
|
+
this.unregisterCallback();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
280
|
+
0 && (module.exports = {
|
|
281
|
+
FfiRuntimeHost
|
|
282
|
+
});
|
|
@@ -1231,11 +1231,13 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1231
1231
|
/** @experimental */
|
|
1232
1232
|
apps: {
|
|
1233
1233
|
/**
|
|
1234
|
-
*
|
|
1234
|
+
* Deprecated/obsolete alias for `session.mcp.resources.read`; retained for backwards compatibility with earlier MCP Apps host integrations.
|
|
1235
1235
|
*
|
|
1236
|
-
* @param params MCP
|
|
1236
|
+
* @param params Deprecated/obsolete MCP Apps alias for `McpResourcesReadRequest`; use `session.mcp.resources.read` instead.
|
|
1237
1237
|
*
|
|
1238
|
-
* @returns
|
|
1238
|
+
* @returns Deprecated/obsolete MCP Apps alias for `McpResourcesReadResult`; use `session.mcp.resources.read` instead.
|
|
1239
|
+
*
|
|
1240
|
+
* @deprecated
|
|
1239
1241
|
*/
|
|
1240
1242
|
readResource: async (params) => connection.sendRequest("session.mcp.apps.readResource", { sessionId, ...params }),
|
|
1241
1243
|
/**
|
|
@@ -1274,6 +1276,33 @@ function createSessionRpc(connection, sessionId) {
|
|
|
1274
1276
|
* @returns Diagnostic snapshot of MCP Apps wiring for the named server.
|
|
1275
1277
|
*/
|
|
1276
1278
|
diagnose: async (params) => connection.sendRequest("session.mcp.apps.diagnose", { sessionId, ...params })
|
|
1279
|
+
},
|
|
1280
|
+
/** @experimental */
|
|
1281
|
+
resources: {
|
|
1282
|
+
/**
|
|
1283
|
+
* Fetch an MCP resource from a connected server by URI (proxies MCP `resources/read`).
|
|
1284
|
+
*
|
|
1285
|
+
* @param params MCP server and resource URI to fetch.
|
|
1286
|
+
*
|
|
1287
|
+
* @returns Resource contents returned by the MCP server.
|
|
1288
|
+
*/
|
|
1289
|
+
read: async (params) => connection.sendRequest("session.mcp.resources.read", { sessionId, ...params }),
|
|
1290
|
+
/**
|
|
1291
|
+
* Enumerate one page of resources a connected MCP server exposes (proxies MCP `resources/list`). Pass `cursor` to continue from a prior result's `nextCursor`.
|
|
1292
|
+
*
|
|
1293
|
+
* @param params MCP server whose resources to enumerate.
|
|
1294
|
+
*
|
|
1295
|
+
* @returns One page of resources advertised by the named MCP server.
|
|
1296
|
+
*/
|
|
1297
|
+
list: async (params) => connection.sendRequest("session.mcp.resources.list", { sessionId, ...params }),
|
|
1298
|
+
/**
|
|
1299
|
+
* Enumerate one page of resource templates a connected MCP server exposes (proxies MCP `resources/templates/list`). Pass `cursor` to continue from a prior result's `nextCursor`.
|
|
1300
|
+
*
|
|
1301
|
+
* @param params MCP server whose resource templates to enumerate.
|
|
1302
|
+
*
|
|
1303
|
+
* @returns One page of resource templates advertised by the named MCP server.
|
|
1304
|
+
*/
|
|
1305
|
+
listTemplates: async (params) => connection.sendRequest("session.mcp.resources.listTemplates", { sessionId, ...params })
|
|
1277
1306
|
}
|
|
1278
1307
|
},
|
|
1279
1308
|
/** @experimental */
|