ds-harness-remote 0.4.13 → 0.4.14
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/acp.d.ts +75 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +61 -0
- package/dist/acp.js.map +1 -0
- package/dist/client-runtime.d.ts +6 -0
- package/dist/client-runtime.d.ts.map +1 -1
- package/dist/client-runtime.js.map +1 -1
- package/dist/client.github.js +224 -9
- package/dist/client.js +224 -9
- package/dist/client.js.map +1 -1
- package/dist/codex/method-policy.d.ts +10 -10
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/connection-controller.d.ts +5 -0
- package/dist/connection-controller.d.ts.map +1 -1
- package/dist/connection-controller.js +16 -1
- package/dist/connection-controller.js.map +1 -1
- package/dist/control-runtime.d.ts +4 -0
- package/dist/control-runtime.d.ts.map +1 -1
- package/dist/control-runtime.js +55 -0
- package/dist/control-runtime.js.map +1 -1
- package/dist/harness-api-bridge.d.ts.map +1 -1
- package/dist/harness-api-bridge.js +29 -8
- package/dist/harness-api-bridge.js.map +1 -1
- package/dist/harness-remote-bridge.d.ts +2 -1
- package/dist/harness-remote-bridge.d.ts.map +1 -1
- package/dist/harness-remote-bridge.js +44 -2
- package/dist/harness-remote-bridge.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +328 -23
- package/dist/index.js.map +4 -4
- package/dist/rpc-router.d.ts +5 -2
- package/dist/rpc-router.d.ts.map +1 -1
- package/dist/rpc-router.js +18 -1
- package/dist/rpc-router.js.map +1 -1
- package/dist/service.d.ts +9 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +31 -2
- package/dist/service.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dsh-plugin.json +1 -1
- package/package.json +21 -21
package/dist/index.js
CHANGED
|
@@ -4066,6 +4066,8 @@ var MAX_RPC_TEXT_INPUT_BYTES = 64 * 1024;
|
|
|
4066
4066
|
var MAX_FILEVIEWER_RANGE_BYTES = 512 * 1024;
|
|
4067
4067
|
var MIN_REPLAY_WINDOW_MS = 15 * 6e4;
|
|
4068
4068
|
var MAX_ALPHA_STREAMS_PER_CONNECTION = 16;
|
|
4069
|
+
var MAX_ACP_PROMPT_BYTES = 64 * 1024;
|
|
4070
|
+
var MAX_ACP_UPDATE_BYTES = 512 * 1024;
|
|
4069
4071
|
var SECURE_FRAGMENT_MAGIC = new Uint8Array([68, 83, 72, 70]);
|
|
4070
4072
|
var SECURE_FRAGMENT_VERSION = 1;
|
|
4071
4073
|
var SECURE_FRAGMENT_HEADER_BYTES = 17;
|
|
@@ -4121,7 +4123,14 @@ var rpcMethods = [
|
|
|
4121
4123
|
"codex.app.transfer.chunk",
|
|
4122
4124
|
"codex.app.transfer.commit",
|
|
4123
4125
|
"codex.app.transfer.read",
|
|
4124
|
-
"codex.app.transfer.close"
|
|
4126
|
+
"codex.app.transfer.close",
|
|
4127
|
+
"acp.initialize",
|
|
4128
|
+
"acp.session.new",
|
|
4129
|
+
"acp.session.load",
|
|
4130
|
+
"acp.session.prompt",
|
|
4131
|
+
"acp.session.respond_permission",
|
|
4132
|
+
"acp.session.cancel",
|
|
4133
|
+
"acp.session.set_mode"
|
|
4125
4134
|
];
|
|
4126
4135
|
var errorCodes = [
|
|
4127
4136
|
// Protocol / Version
|
|
@@ -17422,7 +17431,8 @@ var Config = s.object({
|
|
|
17422
17431
|
codex: s.object({
|
|
17423
17432
|
enabled: s.boolean(),
|
|
17424
17433
|
binary: s.string()
|
|
17425
|
-
})
|
|
17434
|
+
}),
|
|
17435
|
+
acp: s.object({ enabled: s.boolean(), backends: s.array(s.object({ id: s.string(), enabled: s.boolean(), command: s.string(), args: s.array(s.string()), cwd: s.string() })) })
|
|
17426
17436
|
});
|
|
17427
17437
|
var reconnectSchema = external_exports.union([
|
|
17428
17438
|
external_exports.boolean(),
|
|
@@ -17443,7 +17453,8 @@ var configSchema = external_exports.object({
|
|
|
17443
17453
|
codex: external_exports.object({
|
|
17444
17454
|
enabled: external_exports.boolean().optional(),
|
|
17445
17455
|
binary: external_exports.string().trim().min(1).max(4096).optional()
|
|
17446
|
-
}).strict().optional()
|
|
17456
|
+
}).strict().optional(),
|
|
17457
|
+
acp: external_exports.object({ enabled: external_exports.boolean().optional(), backends: external_exports.array(external_exports.object({ id: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,31}$/i), enabled: external_exports.boolean().optional(), command: external_exports.string().trim().min(1).max(4096).optional(), args: external_exports.array(external_exports.string().max(4096)).max(32).optional(), cwd: external_exports.string().max(4096).optional() }).strict()).max(12).optional(), backend: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,31}$/i).optional(), command: external_exports.string().trim().min(1).max(4096).optional(), args: external_exports.array(external_exports.string().max(4096)).max(32).optional(), cwd: external_exports.string().max(4096).optional() }).strict().optional()
|
|
17447
17458
|
}).strict();
|
|
17448
17459
|
function resolveConfig(input2 = {}, env = process.env) {
|
|
17449
17460
|
const parsed = configSchema.parse(input2);
|
|
@@ -17471,7 +17482,12 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
17471
17482
|
codex: {
|
|
17472
17483
|
enabled: parsed.codex?.enabled ?? true,
|
|
17473
17484
|
binary: parsed.codex?.binary ?? "codex"
|
|
17474
|
-
}
|
|
17485
|
+
},
|
|
17486
|
+
acp: { enabled: parsed.acp?.enabled ?? true, backends: [.../* @__PURE__ */ new Set(["codex", "cursor", "kimi", ...parsed.acp?.backends?.map((item) => item.id) ?? []])].map((id2) => {
|
|
17487
|
+
const d = parsed.acp?.backends?.find((x) => x.id === id2);
|
|
17488
|
+
const legacy = parsed.acp?.backend === id2 ? parsed.acp : void 0;
|
|
17489
|
+
return { id: id2, enabled: d?.enabled ?? legacy?.enabled ?? true, command: d?.command ?? legacy?.command ?? { codex: "codex", cursor: "agent", kimi: "kimi" }[id2] ?? id2, args: d?.args ?? legacy?.args ?? ["acp"], ...d?.cwd ?? legacy?.cwd ? { cwd: d?.cwd ?? legacy?.cwd } : {} };
|
|
17490
|
+
}) }
|
|
17475
17491
|
};
|
|
17476
17492
|
}
|
|
17477
17493
|
function normalizeServerUrl(value) {
|
|
@@ -17493,7 +17509,7 @@ function normalizeServerUrl(value) {
|
|
|
17493
17509
|
}
|
|
17494
17510
|
|
|
17495
17511
|
// src/version.ts
|
|
17496
|
-
var PLUGIN_VERSION = "0.4.
|
|
17512
|
+
var PLUGIN_VERSION = "0.4.14";
|
|
17497
17513
|
|
|
17498
17514
|
// src/server-api.ts
|
|
17499
17515
|
var TERMINAL_CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
|
|
@@ -20182,6 +20198,7 @@ function iceServersForAttempt(attempt, iceServers) {
|
|
|
20182
20198
|
|
|
20183
20199
|
// src/control-runtime.ts
|
|
20184
20200
|
import { hostname as hostname2 } from "node:os";
|
|
20201
|
+
import { execFileSync } from "node:child_process";
|
|
20185
20202
|
|
|
20186
20203
|
// src/identity-store.ts
|
|
20187
20204
|
import { createHash } from "node:crypto";
|
|
@@ -20446,6 +20463,9 @@ var PluginControlRuntime = class {
|
|
|
20446
20463
|
if (endpoint === "settings.server.set") return ok3(await this.setServer(payload));
|
|
20447
20464
|
if (endpoint === "settings.role.set") return ok3(await this.setRole(payload));
|
|
20448
20465
|
if (endpoint === "settings.codex.set") return ok3(await this.setCodex(payload));
|
|
20466
|
+
if (endpoint === "settings.acp.set") return ok3(await this.setAcp(payload));
|
|
20467
|
+
if (endpoint === "settings.acp.add") return ok3(await this.addAcp(payload));
|
|
20468
|
+
if (endpoint === "settings.acp.remove") return ok3(await this.removeAcp(payload));
|
|
20449
20469
|
if (endpoint === "settings.logout") return ok3(await this.logout());
|
|
20450
20470
|
if (endpoint === "host.reconnect") {
|
|
20451
20471
|
if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
|
|
@@ -20557,6 +20577,37 @@ var PluginControlRuntime = class {
|
|
|
20557
20577
|
await this.settings.replace(editableConfig(next));
|
|
20558
20578
|
return this.settingsView();
|
|
20559
20579
|
}
|
|
20580
|
+
async setAcp(payload) {
|
|
20581
|
+
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
20582
|
+
const value = record4(payload);
|
|
20583
|
+
if (typeof value.backend !== "string" || typeof value.enabled !== "boolean") throw new ClientModeError("INVALID_MESSAGE", "ACP backend and enabled are required.");
|
|
20584
|
+
const current = resolveConfig(this.settings.get());
|
|
20585
|
+
const backends = current.acp?.backends.map((item) => item.id === value.backend ? { ...item, enabled: value.enabled } : item) ?? [];
|
|
20586
|
+
await this.settings.replace({ ...editableConfig(current), acp: { enabled: current.acp?.enabled ?? true, backends } });
|
|
20587
|
+
return this.settingsView();
|
|
20588
|
+
}
|
|
20589
|
+
async addAcp(payload) {
|
|
20590
|
+
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
20591
|
+
const value = record4(payload);
|
|
20592
|
+
if (typeof value.id !== "string" || typeof value.command !== "string" || !Array.isArray(value.args) || !value.args.every((item) => typeof item === "string")) {
|
|
20593
|
+
throw new ClientModeError("INVALID_MESSAGE", "ACP name, command, and arguments are required.");
|
|
20594
|
+
}
|
|
20595
|
+
const current = resolveConfig(this.settings.get());
|
|
20596
|
+
if (current.acp?.backends.some((item) => item.id === value.id)) throw new ClientModeError("INVALID_MESSAGE", "ACP backend already exists.");
|
|
20597
|
+
const backends = [...current.acp?.backends ?? [], { id: value.id, command: value.command, args: value.args, enabled: false }];
|
|
20598
|
+
const next = resolveConfig({ ...editableConfig(current), acp: { enabled: current.acp?.enabled ?? true, backends } });
|
|
20599
|
+
await this.settings.replace(editableConfig(next));
|
|
20600
|
+
return this.settingsView();
|
|
20601
|
+
}
|
|
20602
|
+
async removeAcp(payload) {
|
|
20603
|
+
if (this.settings === void 0) throw new ClientModeError("SETTINGS_UNAVAILABLE", "DSH user settings are unavailable in this profile.");
|
|
20604
|
+
const id2 = record4(payload).id;
|
|
20605
|
+
if (typeof id2 !== "string" || ["codex", "cursor", "kimi"].includes(id2)) throw new ClientModeError("INVALID_MESSAGE", "Only custom ACP backends can be removed.");
|
|
20606
|
+
const current = resolveConfig(this.settings.get());
|
|
20607
|
+
const backends = (current.acp?.backends ?? []).filter((item) => item.id !== id2);
|
|
20608
|
+
await this.settings.replace(editableConfig({ ...current, acp: { enabled: current.acp?.enabled ?? true, backends } }));
|
|
20609
|
+
return this.settingsView();
|
|
20610
|
+
}
|
|
20560
20611
|
async authorizeOwnedRole(serverUrl, sourceRole, targetRole) {
|
|
20561
20612
|
const sourceDirectory = serverStorageDirectory(this.identityDirectory, serverUrl, sourceRole);
|
|
20562
20613
|
const sourceIdentity = await new IdentityStore({ directory: sourceDirectory }).loadOrCreate(hostname2());
|
|
@@ -20597,6 +20648,7 @@ var PluginControlRuntime = class {
|
|
|
20597
20648
|
writable: this.settings !== void 0,
|
|
20598
20649
|
applies: "restart",
|
|
20599
20650
|
associations,
|
|
20651
|
+
acpAvailability: Object.fromEntries((config.acp?.backends ?? []).map((item) => [item.id, commandAvailable(item.command ?? "")])),
|
|
20600
20652
|
...association === void 0 ? {} : { association }
|
|
20601
20653
|
};
|
|
20602
20654
|
}
|
|
@@ -20633,6 +20685,14 @@ var PluginControlRuntime = class {
|
|
|
20633
20685
|
};
|
|
20634
20686
|
}
|
|
20635
20687
|
};
|
|
20688
|
+
function commandAvailable(command) {
|
|
20689
|
+
try {
|
|
20690
|
+
execFileSync(process.platform === "win32" ? "where" : "which", [command], { stdio: "ignore" });
|
|
20691
|
+
return true;
|
|
20692
|
+
} catch {
|
|
20693
|
+
return false;
|
|
20694
|
+
}
|
|
20695
|
+
}
|
|
20636
20696
|
function editableConfig(config) {
|
|
20637
20697
|
return {
|
|
20638
20698
|
enabled: config.enabled,
|
|
@@ -20648,7 +20708,8 @@ function editableConfig(config) {
|
|
|
20648
20708
|
codex: {
|
|
20649
20709
|
enabled: config.codex.enabled,
|
|
20650
20710
|
binary: config.codex.binary
|
|
20651
|
-
}
|
|
20711
|
+
},
|
|
20712
|
+
...config.acp === void 0 ? {} : { acp: { enabled: config.acp.enabled, backends: config.acp.backends } }
|
|
20652
20713
|
};
|
|
20653
20714
|
}
|
|
20654
20715
|
function isRecord8(value) {
|
|
@@ -20779,7 +20840,21 @@ var ConnectionController = class {
|
|
|
20779
20840
|
return this.active.size;
|
|
20780
20841
|
}
|
|
20781
20842
|
peerDeviceIds() {
|
|
20782
|
-
return
|
|
20843
|
+
return this.connectedPeers().map((peer) => peer.deviceId);
|
|
20844
|
+
}
|
|
20845
|
+
connectedPeers() {
|
|
20846
|
+
const peers = [];
|
|
20847
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20848
|
+
for (const connection of this.active.values()) {
|
|
20849
|
+
const deviceId = connection.channel.peerDeviceId;
|
|
20850
|
+
if (seen.has(deviceId)) continue;
|
|
20851
|
+
seen.add(deviceId);
|
|
20852
|
+
peers.push({
|
|
20853
|
+
deviceId,
|
|
20854
|
+
...connection.channel.mode === void 0 ? {} : { mode: connection.channel.mode }
|
|
20855
|
+
});
|
|
20856
|
+
}
|
|
20857
|
+
return peers;
|
|
20783
20858
|
}
|
|
20784
20859
|
peerDeviceId() {
|
|
20785
20860
|
const peers = this.peerDeviceIds();
|
|
@@ -20924,7 +20999,14 @@ var apiMethods = /* @__PURE__ */ new Set([
|
|
|
20924
20999
|
"codex.app.transfer.chunk",
|
|
20925
21000
|
"codex.app.transfer.commit",
|
|
20926
21001
|
"codex.app.transfer.read",
|
|
20927
|
-
"codex.app.transfer.close"
|
|
21002
|
+
"codex.app.transfer.close",
|
|
21003
|
+
"acp.initialize",
|
|
21004
|
+
"acp.session.new",
|
|
21005
|
+
"acp.session.load",
|
|
21006
|
+
"acp.session.prompt",
|
|
21007
|
+
"acp.session.respond_permission",
|
|
21008
|
+
"acp.session.cancel",
|
|
21009
|
+
"acp.session.set_mode"
|
|
20928
21010
|
]);
|
|
20929
21011
|
var HOST_CAPABILITIES = [
|
|
20930
21012
|
"harness.api.v1",
|
|
@@ -20933,10 +21015,11 @@ var HOST_CAPABILITIES = [
|
|
|
20933
21015
|
"harness.remote.transfer.v1",
|
|
20934
21016
|
"fileviewer.read.v1",
|
|
20935
21017
|
"codex.appserver.v1",
|
|
20936
|
-
"codex.appserver.transfer.v1"
|
|
21018
|
+
"codex.appserver.transfer.v1",
|
|
21019
|
+
"agent.acp.v1"
|
|
20937
21020
|
];
|
|
20938
21021
|
var RpcRouter = class {
|
|
20939
|
-
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex) {
|
|
21022
|
+
constructor(harnessApi, maxPending = 128, logger, fileViewer, harnessRemote, capabilities = () => HOST_CAPABILITIES, codex, acp) {
|
|
20940
21023
|
this.harnessApi = harnessApi;
|
|
20941
21024
|
this.maxPending = maxPending;
|
|
20942
21025
|
this.logger = logger;
|
|
@@ -20944,6 +21027,7 @@ var RpcRouter = class {
|
|
|
20944
21027
|
this.harnessRemote = harnessRemote;
|
|
20945
21028
|
this.capabilities = capabilities;
|
|
20946
21029
|
this.codex = codex;
|
|
21030
|
+
this.acp = acp;
|
|
20947
21031
|
}
|
|
20948
21032
|
active = 0;
|
|
20949
21033
|
async closePeerStreams() {
|
|
@@ -21052,6 +21136,20 @@ var RpcRouter = class {
|
|
|
21052
21136
|
return this.requireCodex().readTransfer(params);
|
|
21053
21137
|
case "codex.app.transfer.close":
|
|
21054
21138
|
return this.requireCodex().closeTransfer(params);
|
|
21139
|
+
case "acp.initialize":
|
|
21140
|
+
return this.requireAcp().initialize(params);
|
|
21141
|
+
case "acp.session.new":
|
|
21142
|
+
return this.requireAcp().sessionNew(params);
|
|
21143
|
+
case "acp.session.load":
|
|
21144
|
+
return this.requireAcp().sessionLoad(params);
|
|
21145
|
+
case "acp.session.prompt":
|
|
21146
|
+
return this.requireAcp().prompt(params, async () => void 0);
|
|
21147
|
+
case "acp.session.respond_permission":
|
|
21148
|
+
return this.requireAcp().respondPermission(params);
|
|
21149
|
+
case "acp.session.cancel":
|
|
21150
|
+
return this.requireAcp().cancel(params);
|
|
21151
|
+
case "acp.session.set_mode":
|
|
21152
|
+
return this.requireAcp().setMode(params);
|
|
21055
21153
|
default:
|
|
21056
21154
|
throw new RpcError("METHOD_NOT_FOUND", "The requested method does not exist.");
|
|
21057
21155
|
}
|
|
@@ -21068,6 +21166,10 @@ var RpcRouter = class {
|
|
|
21068
21166
|
}
|
|
21069
21167
|
return this.harnessRemote;
|
|
21070
21168
|
}
|
|
21169
|
+
requireAcp() {
|
|
21170
|
+
if (!this.acp) throw new RpcError("CAPABILITY_NOT_SUPPORTED", "ACP is not configured on this Host.");
|
|
21171
|
+
return this.acp;
|
|
21172
|
+
}
|
|
21071
21173
|
requireCodex() {
|
|
21072
21174
|
if (this.codex === void 0) {
|
|
21073
21175
|
throw new RpcError("FEATURE_NOT_SUPPORTED", "Codex Remote is disabled or unavailable on this Host.");
|
|
@@ -22154,11 +22256,22 @@ var transferCloseSchema = external_exports.object({ transferId: transferIdSchema
|
|
|
22154
22256
|
var commandExecuteSchema = external_exports.object({
|
|
22155
22257
|
agentId: external_exports.string().min(1).max(128),
|
|
22156
22258
|
line: external_exports.string().min(1).max(2048),
|
|
22157
|
-
//
|
|
22158
|
-
//
|
|
22159
|
-
//
|
|
22160
|
-
|
|
22161
|
-
|
|
22259
|
+
// dsh-commands <= 0.1.2 calls this field `images`; dsh-commands 0.1.5
|
|
22260
|
+
// renamed it to `submittedAttachments` and expanded its element type to
|
|
22261
|
+
// include staged file receipts. Remote command execution keeps attachments
|
|
22262
|
+
// out of scope, so either compatibility field is restricted to an empty
|
|
22263
|
+
// list. The dispatcher below chooses the matching Host descriptor.
|
|
22264
|
+
images: external_exports.array(external_exports.never()).length(0).optional(),
|
|
22265
|
+
submittedAttachments: external_exports.array(external_exports.never()).length(0).optional()
|
|
22266
|
+
}).strict().superRefine((value, context) => {
|
|
22267
|
+
if (value.images !== void 0 && value.submittedAttachments !== void 0) {
|
|
22268
|
+
context.addIssue({
|
|
22269
|
+
code: external_exports.ZodIssueCode.custom,
|
|
22270
|
+
message: "Use either images or submittedAttachments, not both.",
|
|
22271
|
+
path: ["submittedAttachments"]
|
|
22272
|
+
});
|
|
22273
|
+
}
|
|
22274
|
+
});
|
|
22162
22275
|
var commandListSchema = external_exports.object({
|
|
22163
22276
|
agentId: external_exports.string().min(1).max(128)
|
|
22164
22277
|
}).strict();
|
|
@@ -22279,7 +22392,7 @@ var HarnessApiBridge = class {
|
|
|
22279
22392
|
this.maxStreams = maxStreams;
|
|
22280
22393
|
this.logger = logger;
|
|
22281
22394
|
this.harnessVersion = harnessVersion;
|
|
22282
|
-
this.methods = createMethodMap(api, typertGateway);
|
|
22395
|
+
this.methods = createMethodMap(api, typertGateway, harnessVersion);
|
|
22283
22396
|
this.mux = api.events.mux.bind(api.events);
|
|
22284
22397
|
this.host = api.events.host.bind(api.events);
|
|
22285
22398
|
this.answer = api.respond.bind(api);
|
|
@@ -22600,7 +22713,7 @@ function normalizeHostDescribe(response, harnessVersion, canListDirectory) {
|
|
|
22600
22713
|
}
|
|
22601
22714
|
};
|
|
22602
22715
|
}
|
|
22603
|
-
function createMethodMap(api, typertGateway) {
|
|
22716
|
+
function createMethodMap(api, typertGateway, harnessVersion) {
|
|
22604
22717
|
const domains = api;
|
|
22605
22718
|
const methods = /* @__PURE__ */ new Map();
|
|
22606
22719
|
for (const method of HARNESS_API_ALLOWLIST) {
|
|
@@ -22610,7 +22723,17 @@ function createMethodMap(api, typertGateway) {
|
|
|
22610
22723
|
const implementation2 = async (request, signal) => {
|
|
22611
22724
|
if (commandMethod === "execute") {
|
|
22612
22725
|
const payload = commandExecuteSchema.parse(request.payload);
|
|
22613
|
-
const
|
|
22726
|
+
const legacyArgs = {
|
|
22727
|
+
agentId: payload.agentId,
|
|
22728
|
+
line: payload.line,
|
|
22729
|
+
images: payload.images ?? []
|
|
22730
|
+
};
|
|
22731
|
+
const currentArgs = {
|
|
22732
|
+
agentId: payload.agentId,
|
|
22733
|
+
line: payload.line,
|
|
22734
|
+
submittedAttachments: payload.submittedAttachments ?? []
|
|
22735
|
+
};
|
|
22736
|
+
const args2 = harnessSessionGeneration(harnessVersion) === "v3" ? currentArgs : legacyArgs;
|
|
22614
22737
|
const value2 = await typertGateway.invoke({
|
|
22615
22738
|
namespace,
|
|
22616
22739
|
method: "execute",
|
|
@@ -22912,6 +23035,24 @@ var streamOpenSchema2 = external_exports.object({
|
|
|
22912
23035
|
payload: external_exports.unknown()
|
|
22913
23036
|
}).strict();
|
|
22914
23037
|
var streamCloseSchema2 = external_exports.object({ streamId: external_exports.string().min(1).max(128) }).strict();
|
|
23038
|
+
var commandExecuteArgsSchema = external_exports.object({
|
|
23039
|
+
agentId: external_exports.string().min(1).max(128),
|
|
23040
|
+
line: external_exports.string().min(1).max(2048),
|
|
23041
|
+
// dsh-commands <= 0.1.2 calls this field `images`; dsh-commands 0.1.5
|
|
23042
|
+
// renamed it to `submittedAttachments`. Only empty arrays are exposed by
|
|
23043
|
+
// the Remote bridge; command attachments are deliberately out of scope.
|
|
23044
|
+
images: external_exports.array(external_exports.never()).length(0).optional(),
|
|
23045
|
+
submittedAttachments: external_exports.array(external_exports.never()).length(0).optional()
|
|
23046
|
+
}).strict().superRefine((value, context) => {
|
|
23047
|
+
if (value.images !== void 0 && value.submittedAttachments !== void 0) {
|
|
23048
|
+
context.addIssue({
|
|
23049
|
+
code: external_exports.ZodIssueCode.custom,
|
|
23050
|
+
message: "Use either images or submittedAttachments, not both.",
|
|
23051
|
+
path: ["submittedAttachments"]
|
|
23052
|
+
});
|
|
23053
|
+
}
|
|
23054
|
+
});
|
|
23055
|
+
var commandExecutePayloadSchema = external_exports.object({ args: commandExecuteArgsSchema }).strict();
|
|
22915
23056
|
var transferOpenSchema2 = external_exports.object({
|
|
22916
23057
|
transferId: external_exports.string().uuid(),
|
|
22917
23058
|
totalBytes: external_exports.number().int().positive().max(MAX_HARNESS_API_TRANSFER_BYTES),
|
|
@@ -22993,10 +23134,11 @@ var HARNESS_REMOTE_ALLOWLIST = [
|
|
|
22993
23134
|
];
|
|
22994
23135
|
var allowedEndpoints = new Set(HARNESS_REMOTE_ALLOWLIST);
|
|
22995
23136
|
var HarnessRemoteBridge = class {
|
|
22996
|
-
constructor(gateway, publish, logger) {
|
|
23137
|
+
constructor(gateway, publish, logger, harnessVersion) {
|
|
22997
23138
|
this.gateway = gateway;
|
|
22998
23139
|
this.publish = publish;
|
|
22999
23140
|
this.logger = logger;
|
|
23141
|
+
this.harnessVersion = harnessVersion;
|
|
23000
23142
|
}
|
|
23001
23143
|
streams = /* @__PURE__ */ new Map();
|
|
23002
23144
|
incomingTransfers = /* @__PURE__ */ new Map();
|
|
@@ -23010,7 +23152,7 @@ var HarnessRemoteBridge = class {
|
|
|
23010
23152
|
const startedAt = performance.now();
|
|
23011
23153
|
const signal = AbortSignal.timeout(6e4);
|
|
23012
23154
|
try {
|
|
23013
|
-
const nativeResult = await this.gateway.dispatch(params.endpoint, params.payload, signal);
|
|
23155
|
+
const nativeResult = params.endpoint === "commands/execute" ? await dispatchCommandForHost(this.gateway, params.payload, signal, this.harnessVersion) : await this.gateway.dispatch(params.endpoint, params.payload, signal);
|
|
23014
23156
|
const result = params.endpoint === "directoryPicker/list" && needsDirectoryFallback(nativeResult) ? await this.directoryList(params.payload, signal) : nativeResult;
|
|
23015
23157
|
this.logger?.debug("harness remote call ok", {
|
|
23016
23158
|
endpoint: params.endpoint,
|
|
@@ -23209,6 +23351,29 @@ var HarnessRemoteBridge = class {
|
|
|
23209
23351
|
for (const [id2, transfer] of this.outgoingTransfers) if (transfer.touchedAt < cutoff) this.outgoingTransfers.delete(id2);
|
|
23210
23352
|
}
|
|
23211
23353
|
};
|
|
23354
|
+
async function dispatchCommandForHost(gateway, payload, signal, harnessVersion) {
|
|
23355
|
+
const parsed = commandExecutePayloadSchema.parse(payload);
|
|
23356
|
+
const legacyPayload = {
|
|
23357
|
+
args: {
|
|
23358
|
+
agentId: parsed.args.agentId,
|
|
23359
|
+
line: parsed.args.line,
|
|
23360
|
+
images: parsed.args.images ?? []
|
|
23361
|
+
}
|
|
23362
|
+
};
|
|
23363
|
+
const currentPayload = {
|
|
23364
|
+
args: {
|
|
23365
|
+
agentId: parsed.args.agentId,
|
|
23366
|
+
line: parsed.args.line,
|
|
23367
|
+
submittedAttachments: parsed.args.submittedAttachments ?? []
|
|
23368
|
+
}
|
|
23369
|
+
};
|
|
23370
|
+
const args = harnessSessionGeneration(harnessVersion) === "v3" ? currentPayload : legacyPayload;
|
|
23371
|
+
return gateway.dispatch(
|
|
23372
|
+
"commands/execute",
|
|
23373
|
+
args,
|
|
23374
|
+
signal
|
|
23375
|
+
);
|
|
23376
|
+
}
|
|
23212
23377
|
function decodeCanonicalBase642(value) {
|
|
23213
23378
|
const bytes = Buffer.from(value, "base64");
|
|
23214
23379
|
if (bytes.toString("base64") !== value) throw new RpcError("INVALID_MESSAGE", "The Harness Remote transfer chunk is invalid.");
|
|
@@ -24912,7 +25077,119 @@ function isActiveWriterMessage(message) {
|
|
|
24912
25077
|
return message.toLowerCase().includes("active writer");
|
|
24913
25078
|
}
|
|
24914
25079
|
|
|
25080
|
+
// src/acp.ts
|
|
25081
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
25082
|
+
var AcpGateway = class {
|
|
25083
|
+
constructor(adapters) {
|
|
25084
|
+
this.adapters = adapters;
|
|
25085
|
+
}
|
|
25086
|
+
get(p) {
|
|
25087
|
+
const list = this.adapters instanceof Object && "backend" in this.adapters ? [this.adapters] : [...this.adapters];
|
|
25088
|
+
const a = list.find((x) => !p.backend || x.backend === p.backend);
|
|
25089
|
+
if (!a) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
25090
|
+
return a;
|
|
25091
|
+
}
|
|
25092
|
+
initialize(p) {
|
|
25093
|
+
return this.get(p).initialize(p);
|
|
25094
|
+
}
|
|
25095
|
+
sessionNew(p) {
|
|
25096
|
+
return this.get(p).sessionNew(p);
|
|
25097
|
+
}
|
|
25098
|
+
sessionLoad(p) {
|
|
25099
|
+
const a = this.get(p);
|
|
25100
|
+
if (!a.sessionLoad) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
25101
|
+
return a.sessionLoad(p);
|
|
25102
|
+
}
|
|
25103
|
+
prompt(p, emit) {
|
|
25104
|
+
return this.get(p).prompt(p, emit);
|
|
25105
|
+
}
|
|
25106
|
+
respondPermission(p) {
|
|
25107
|
+
const a = this.get(p);
|
|
25108
|
+
if (!a.respondPermission) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
25109
|
+
return a.respondPermission(p);
|
|
25110
|
+
}
|
|
25111
|
+
cancel(p) {
|
|
25112
|
+
const a = this.get(p);
|
|
25113
|
+
if (!a.cancel) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
25114
|
+
return a.cancel(p);
|
|
25115
|
+
}
|
|
25116
|
+
setMode(p) {
|
|
25117
|
+
const a = this.get(p);
|
|
25118
|
+
if (!a.setMode) throw new Error("CAPABILITY_NOT_SUPPORTED");
|
|
25119
|
+
return a.setMode(p);
|
|
25120
|
+
}
|
|
25121
|
+
};
|
|
25122
|
+
var StdioAcpAdapter = class {
|
|
25123
|
+
constructor(config) {
|
|
25124
|
+
this.config = config;
|
|
25125
|
+
this.backend = config.id;
|
|
25126
|
+
}
|
|
25127
|
+
backend;
|
|
25128
|
+
child;
|
|
25129
|
+
nextId = 1;
|
|
25130
|
+
ensure() {
|
|
25131
|
+
if (!this.child) this.child = spawn3(this.config.command, this.config.args ?? [], { cwd: this.config.cwd, stdio: "pipe" });
|
|
25132
|
+
return this.child;
|
|
25133
|
+
}
|
|
25134
|
+
async initialize(params) {
|
|
25135
|
+
await this.call("initialize", params);
|
|
25136
|
+
return { protocolVersion: 1, capability: "agent.acp.v1", backend: this.backend, capabilities: ["session.new", "session.load", "session.prompt", "session.cancel"] };
|
|
25137
|
+
}
|
|
25138
|
+
async sessionNew(params) {
|
|
25139
|
+
const r = await this.call("session/new", params);
|
|
25140
|
+
if (!r.sessionId) throw new Error("INVALID_MESSAGE");
|
|
25141
|
+
return { sessionId: r.sessionId };
|
|
25142
|
+
}
|
|
25143
|
+
async sessionLoad(params) {
|
|
25144
|
+
const r = await this.call("session/load", params);
|
|
25145
|
+
if (!r.sessionId) throw new Error("INVALID_MESSAGE");
|
|
25146
|
+
return { sessionId: r.sessionId };
|
|
25147
|
+
}
|
|
25148
|
+
async prompt(params, emit) {
|
|
25149
|
+
await this.call("session/prompt", params, async (n) => emit({ sessionId: params.sessionId, update: n, seq: this.nextId++ }));
|
|
25150
|
+
}
|
|
25151
|
+
async cancel(p) {
|
|
25152
|
+
await this.call("session/cancel", p);
|
|
25153
|
+
}
|
|
25154
|
+
async setMode(p) {
|
|
25155
|
+
await this.call("session/set_mode", p);
|
|
25156
|
+
}
|
|
25157
|
+
async respondPermission(p) {
|
|
25158
|
+
await this.call("session/request_permission", p);
|
|
25159
|
+
}
|
|
25160
|
+
async close() {
|
|
25161
|
+
this.child?.kill();
|
|
25162
|
+
this.child = void 0;
|
|
25163
|
+
}
|
|
25164
|
+
call(method, params, onNotification) {
|
|
25165
|
+
const c = this.ensure();
|
|
25166
|
+
const id2 = this.nextId++;
|
|
25167
|
+
c.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: id2, method, params }) + "\n");
|
|
25168
|
+
return new Promise((resolve3, reject) => {
|
|
25169
|
+
let buf = "";
|
|
25170
|
+
const onData = async (d) => {
|
|
25171
|
+
buf += d;
|
|
25172
|
+
const lines = buf.split("\n");
|
|
25173
|
+
buf = lines.pop() ?? "";
|
|
25174
|
+
for (const l of lines) {
|
|
25175
|
+
try {
|
|
25176
|
+
const m = JSON.parse(l);
|
|
25177
|
+
if (m.id === id2) {
|
|
25178
|
+
c.stdout.off("data", onData);
|
|
25179
|
+
m.error ? reject(new Error(m.error.message ?? "ACP error")) : resolve3(m.result);
|
|
25180
|
+
} else if (m.method && onNotification) await onNotification(m.params);
|
|
25181
|
+
} catch {
|
|
25182
|
+
}
|
|
25183
|
+
}
|
|
25184
|
+
};
|
|
25185
|
+
c.stdout.on("data", onData);
|
|
25186
|
+
c.once("error", reject);
|
|
25187
|
+
});
|
|
25188
|
+
}
|
|
25189
|
+
};
|
|
25190
|
+
|
|
24915
25191
|
// src/service.ts
|
|
25192
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
24916
25193
|
var HostPluginRuntime = class {
|
|
24917
25194
|
constructor(config, identities, apiProxy, logger, localGateway, fileViewerHost) {
|
|
24918
25195
|
this.config = config;
|
|
@@ -24934,7 +25211,8 @@ var HostPluginRuntime = class {
|
|
|
24934
25211
|
const harnessRemote = this.localGateway?.supportsCarrier === true ? new HarnessRemoteBridge(
|
|
24935
25212
|
this.localGateway,
|
|
24936
25213
|
(event, data) => send(createEvent(event, data)),
|
|
24937
|
-
this.logger
|
|
25214
|
+
this.logger,
|
|
25215
|
+
this.harnessVersion
|
|
24938
25216
|
) : void 0;
|
|
24939
25217
|
const fileViewer = new RemoteFileViewerBridge(
|
|
24940
25218
|
() => this.fileViewerHost?.(),
|
|
@@ -24944,6 +25222,8 @@ var HostPluginRuntime = class {
|
|
|
24944
25222
|
context,
|
|
24945
25223
|
(event, data) => send(createEvent(event, data))
|
|
24946
25224
|
);
|
|
25225
|
+
const adapters = (config.acp?.backends ?? []).filter((item) => item.enabled && this.acpAvailable(item.command)).map((item) => new StdioAcpAdapter(item));
|
|
25226
|
+
const acp = config.acp?.enabled && adapters.length > 0 ? new AcpGateway(adapters) : void 0;
|
|
24947
25227
|
return new RpcRouter(
|
|
24948
25228
|
harnessApi,
|
|
24949
25229
|
void 0,
|
|
@@ -24951,7 +25231,8 @@ var HostPluginRuntime = class {
|
|
|
24951
25231
|
fileViewer,
|
|
24952
25232
|
harnessRemote,
|
|
24953
25233
|
() => this.hostCapabilities(),
|
|
24954
|
-
codex
|
|
25234
|
+
codex,
|
|
25235
|
+
acp
|
|
24955
25236
|
);
|
|
24956
25237
|
}, this.logger);
|
|
24957
25238
|
if (config.serverUrl !== void 0) {
|
|
@@ -25004,9 +25285,21 @@ var HostPluginRuntime = class {
|
|
|
25004
25285
|
...error === void 0 ? {} : { error },
|
|
25005
25286
|
...authorization?.account === void 0 ? {} : { account: authorization.account },
|
|
25006
25287
|
authorized: authorization !== void 0,
|
|
25007
|
-
accountRequired: error === "ACCOUNT_AUTH_REQUIRED" || error === "AUTH_INVALID" || error === "TOKEN_EXPIRED"
|
|
25288
|
+
accountRequired: error === "ACCOUNT_AUTH_REQUIRED" || error === "AUTH_INVALID" || error === "TOKEN_EXPIRED",
|
|
25289
|
+
connectedClients: this.listConnectedClients()
|
|
25008
25290
|
};
|
|
25009
25291
|
}
|
|
25292
|
+
listConnectedClients() {
|
|
25293
|
+
return this.connections.connectedPeers().map((peer) => {
|
|
25294
|
+
const trusted = this.identities.trustedPeer(peer.deviceId);
|
|
25295
|
+
return {
|
|
25296
|
+
deviceId: peer.deviceId,
|
|
25297
|
+
name: trusted?.name.trim() ?? "",
|
|
25298
|
+
...trusted === void 0 ? {} : { platform: trusted.platform },
|
|
25299
|
+
...peer.mode === void 0 ? {} : { mode: peer.mode }
|
|
25300
|
+
};
|
|
25301
|
+
});
|
|
25302
|
+
}
|
|
25010
25303
|
localHarnessVersion() {
|
|
25011
25304
|
return this.harnessVersion;
|
|
25012
25305
|
}
|
|
@@ -25190,8 +25483,19 @@ var HostPluginRuntime = class {
|
|
|
25190
25483
|
}
|
|
25191
25484
|
if (this.fileViewerHost?.() !== void 0) capabilities.push("fileviewer.read.v1");
|
|
25192
25485
|
if (this.codex.isAvailable()) capabilities.push("codex.appserver.v1", "codex.appserver.transfer.v1");
|
|
25486
|
+
if (this.config.acp?.enabled) {
|
|
25487
|
+
for (const item of this.config.acp.backends) if (item.enabled && this.acpAvailable(item.command)) capabilities.push(`agent.acp.v1.${item.id}`);
|
|
25488
|
+
}
|
|
25193
25489
|
return capabilities;
|
|
25194
25490
|
}
|
|
25491
|
+
acpAvailable(command) {
|
|
25492
|
+
try {
|
|
25493
|
+
execFileSync2(process.platform === "win32" ? "where" : "which", [command], { stdio: "ignore" });
|
|
25494
|
+
return true;
|
|
25495
|
+
} catch {
|
|
25496
|
+
return false;
|
|
25497
|
+
}
|
|
25498
|
+
}
|
|
25195
25499
|
requireLocalCodexPeer() {
|
|
25196
25500
|
if (!this.codex.isAvailable()) {
|
|
25197
25501
|
throw new RpcError("CODEX_UNAVAILABLE", "Local CodeX is disabled or unavailable on this Host.");
|
|
@@ -26108,6 +26412,7 @@ function isPlainRecord2(value) {
|
|
|
26108
26412
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26109
26413
|
}
|
|
26110
26414
|
export {
|
|
26415
|
+
AcpGateway,
|
|
26111
26416
|
ApiProxySwitch,
|
|
26112
26417
|
CODEX_APP_ALLOWLIST,
|
|
26113
26418
|
ClientModeError,
|