@serviceme/devtools-cli 0.0.5 → 0.1.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/bridgeServer.js +538 -500
- package/dist/cli.js +13543 -11772
- package/package.json +6 -4
package/dist/bridgeServer.js
CHANGED
|
@@ -5,8 +5,8 @@ var readline = require('readline');
|
|
|
5
5
|
var child_process = require('child_process');
|
|
6
6
|
require('fs/promises');
|
|
7
7
|
require('path');
|
|
8
|
-
var crypto = require('crypto');
|
|
9
8
|
require('fs');
|
|
9
|
+
require('crypto');
|
|
10
10
|
|
|
11
11
|
function _interopNamespace(e) {
|
|
12
12
|
if (e && e.__esModule) return e;
|
|
@@ -7926,10 +7926,10 @@ var require_stringify = __commonJS({
|
|
|
7926
7926
|
replacer = null;
|
|
7927
7927
|
indent = EMPTY;
|
|
7928
7928
|
};
|
|
7929
|
-
var
|
|
7929
|
+
var join8 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + LF + gap : two ? two.trimRight() + LF + gap : EMPTY;
|
|
7930
7930
|
var join_content = (inside, value, gap) => {
|
|
7931
7931
|
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
|
|
7932
|
-
return
|
|
7932
|
+
return join8(comment, inside, gap);
|
|
7933
7933
|
};
|
|
7934
7934
|
var array_stringify = (value, gap) => {
|
|
7935
7935
|
const deeper_gap = gap + indent;
|
|
@@ -7940,7 +7940,7 @@ var require_stringify = __commonJS({
|
|
|
7940
7940
|
if (i !== 0) {
|
|
7941
7941
|
inside += COMMA;
|
|
7942
7942
|
}
|
|
7943
|
-
const before =
|
|
7943
|
+
const before = join8(
|
|
7944
7944
|
after_comma,
|
|
7945
7945
|
process_comments(value, BEFORE(i), deeper_gap),
|
|
7946
7946
|
deeper_gap
|
|
@@ -7950,7 +7950,7 @@ var require_stringify = __commonJS({
|
|
|
7950
7950
|
inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
|
|
7951
7951
|
after_comma = process_comments(value, AFTER(i), deeper_gap);
|
|
7952
7952
|
}
|
|
7953
|
-
inside +=
|
|
7953
|
+
inside += join8(
|
|
7954
7954
|
after_comma,
|
|
7955
7955
|
process_comments(value, PREFIX_AFTER, deeper_gap),
|
|
7956
7956
|
deeper_gap
|
|
@@ -7975,7 +7975,7 @@ var require_stringify = __commonJS({
|
|
|
7975
7975
|
inside += COMMA;
|
|
7976
7976
|
}
|
|
7977
7977
|
first = false;
|
|
7978
|
-
const before =
|
|
7978
|
+
const before = join8(
|
|
7979
7979
|
after_comma,
|
|
7980
7980
|
process_comments(value, BEFORE(key), deeper_gap),
|
|
7981
7981
|
deeper_gap
|
|
@@ -7985,7 +7985,7 @@ var require_stringify = __commonJS({
|
|
|
7985
7985
|
after_comma = process_comments(value, AFTER(key), deeper_gap);
|
|
7986
7986
|
};
|
|
7987
7987
|
keys.forEach(iteratee);
|
|
7988
|
-
inside +=
|
|
7988
|
+
inside += join8(
|
|
7989
7989
|
after_comma,
|
|
7990
7990
|
process_comments(value, PREFIX_AFTER, deeper_gap),
|
|
7991
7991
|
deeper_gap
|
|
@@ -8143,7 +8143,7 @@ var require_pend = __commonJS({
|
|
|
8143
8143
|
// ../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/fd-slicer.js
|
|
8144
8144
|
var require_fd_slicer = __commonJS({
|
|
8145
8145
|
"../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/fd-slicer.js"(exports$1) {
|
|
8146
|
-
var
|
|
8146
|
+
var fs8 = __require("fs");
|
|
8147
8147
|
var util = __require("util");
|
|
8148
8148
|
var stream = __require("stream");
|
|
8149
8149
|
var Readable = stream.Readable;
|
|
@@ -8168,7 +8168,7 @@ var require_fd_slicer = __commonJS({
|
|
|
8168
8168
|
FdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
|
|
8169
8169
|
var self = this;
|
|
8170
8170
|
self.pend.go(function(cb) {
|
|
8171
|
-
|
|
8171
|
+
fs8.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
|
|
8172
8172
|
cb();
|
|
8173
8173
|
callback(err, bytesRead, buffer2);
|
|
8174
8174
|
});
|
|
@@ -8177,7 +8177,7 @@ var require_fd_slicer = __commonJS({
|
|
|
8177
8177
|
FdSlicer.prototype.write = function(buffer, offset, length, position, callback) {
|
|
8178
8178
|
var self = this;
|
|
8179
8179
|
self.pend.go(function(cb) {
|
|
8180
|
-
|
|
8180
|
+
fs8.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
|
|
8181
8181
|
cb();
|
|
8182
8182
|
callback(err, written, buffer2);
|
|
8183
8183
|
});
|
|
@@ -8198,7 +8198,7 @@ var require_fd_slicer = __commonJS({
|
|
|
8198
8198
|
if (self.refCount > 0) return;
|
|
8199
8199
|
if (self.refCount < 0) throw new Error("invalid unref");
|
|
8200
8200
|
if (self.autoClose) {
|
|
8201
|
-
|
|
8201
|
+
fs8.close(self.fd, onCloseDone);
|
|
8202
8202
|
}
|
|
8203
8203
|
function onCloseDone(err) {
|
|
8204
8204
|
if (err) {
|
|
@@ -8235,7 +8235,7 @@ var require_fd_slicer = __commonJS({
|
|
|
8235
8235
|
self.context.pend.go(function(cb) {
|
|
8236
8236
|
if (self.destroyed) return cb();
|
|
8237
8237
|
var buffer = Buffer.allocUnsafe(toRead);
|
|
8238
|
-
|
|
8238
|
+
fs8.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
|
|
8239
8239
|
if (err) {
|
|
8240
8240
|
self.destroy(err);
|
|
8241
8241
|
} else if (bytesRead === 0) {
|
|
@@ -8282,7 +8282,7 @@ var require_fd_slicer = __commonJS({
|
|
|
8282
8282
|
}
|
|
8283
8283
|
self.context.pend.go(function(cb) {
|
|
8284
8284
|
if (self.destroyed) return cb();
|
|
8285
|
-
|
|
8285
|
+
fs8.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
|
|
8286
8286
|
if (err2) {
|
|
8287
8287
|
self.destroy();
|
|
8288
8288
|
cb();
|
|
@@ -8720,7 +8720,7 @@ var require_buffer_crc32 = __commonJS({
|
|
|
8720
8720
|
// ../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/index.js
|
|
8721
8721
|
var require_yauzl = __commonJS({
|
|
8722
8722
|
"../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/index.js"(exports$1) {
|
|
8723
|
-
var
|
|
8723
|
+
var fs8 = __require("fs");
|
|
8724
8724
|
var zlib = __require("zlib");
|
|
8725
8725
|
var fd_slicer = require_fd_slicer();
|
|
8726
8726
|
var crc32 = require_buffer_crc32();
|
|
@@ -8741,7 +8741,7 @@ var require_yauzl = __commonJS({
|
|
|
8741
8741
|
exports$1.Entry = Entry;
|
|
8742
8742
|
exports$1.LocalFileHeader = LocalFileHeader;
|
|
8743
8743
|
exports$1.RandomAccessReader = RandomAccessReader;
|
|
8744
|
-
function open2(
|
|
8744
|
+
function open2(path7, options2, callback) {
|
|
8745
8745
|
if (typeof options2 === "function") {
|
|
8746
8746
|
callback = options2;
|
|
8747
8747
|
options2 = null;
|
|
@@ -8753,10 +8753,10 @@ var require_yauzl = __commonJS({
|
|
|
8753
8753
|
if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
|
|
8754
8754
|
if (options2.strictFileNames == null) options2.strictFileNames = false;
|
|
8755
8755
|
if (callback == null) callback = defaultCallback;
|
|
8756
|
-
|
|
8756
|
+
fs8.open(path7, "r", function(err, fd) {
|
|
8757
8757
|
if (err) return callback(err);
|
|
8758
8758
|
fromFd(fd, options2, function(err2, zipfile) {
|
|
8759
|
-
if (err2)
|
|
8759
|
+
if (err2) fs8.close(fd, defaultCallback);
|
|
8760
8760
|
callback(err2, zipfile);
|
|
8761
8761
|
});
|
|
8762
8762
|
});
|
|
@@ -8773,7 +8773,7 @@ var require_yauzl = __commonJS({
|
|
|
8773
8773
|
if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
|
|
8774
8774
|
if (options2.strictFileNames == null) options2.strictFileNames = false;
|
|
8775
8775
|
if (callback == null) callback = defaultCallback;
|
|
8776
|
-
|
|
8776
|
+
fs8.fstat(fd, function(err, stats) {
|
|
8777
8777
|
if (err) return callback(err);
|
|
8778
8778
|
var reader = fd_slicer.createFromFd(fd, { autoClose: true });
|
|
8779
8779
|
fromRandomAccessReader(reader, stats.size, options2, callback);
|
|
@@ -9458,21 +9458,20 @@ var require_yauzl = __commonJS({
|
|
|
9458
9458
|
});
|
|
9459
9459
|
|
|
9460
9460
|
// ../../packages/serviceme-protocol/src/bridge.ts
|
|
9461
|
-
var SERVICEME_PROTOCOL_VERSION =
|
|
9461
|
+
var SERVICEME_PROTOCOL_VERSION = 2;
|
|
9462
9462
|
function isRecord(value) {
|
|
9463
9463
|
return typeof value === "object" && value !== null;
|
|
9464
9464
|
}
|
|
9465
|
+
function isValidProtocolVersion(value) {
|
|
9466
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= SERVICEME_PROTOCOL_VERSION;
|
|
9467
|
+
}
|
|
9465
9468
|
var BRIDGE_METHODS = [
|
|
9466
9469
|
"system.hello",
|
|
9467
9470
|
"system.ping",
|
|
9468
9471
|
"system.shutdown",
|
|
9469
|
-
"
|
|
9470
|
-
"
|
|
9471
|
-
"
|
|
9472
|
-
"opencode.session.prompt",
|
|
9473
|
-
"opencode.session.status",
|
|
9474
|
-
"opencode.session.abort",
|
|
9475
|
-
"opencode.session.dispose"
|
|
9472
|
+
"task.execute",
|
|
9473
|
+
"task.cancel",
|
|
9474
|
+
"task.list-running"
|
|
9476
9475
|
];
|
|
9477
9476
|
function isBridgeMethod(value) {
|
|
9478
9477
|
return typeof value === "string" && BRIDGE_METHODS.includes(value);
|
|
@@ -9481,7 +9480,7 @@ function isBridgeRequest(value) {
|
|
|
9481
9480
|
if (!isRecord(value)) {
|
|
9482
9481
|
return false;
|
|
9483
9482
|
}
|
|
9484
|
-
return value.protocolVersion
|
|
9483
|
+
return isValidProtocolVersion(value.protocolVersion) && value.kind === "request" && typeof value.id === "string" && isBridgeMethod(value.method) && "params" in value;
|
|
9485
9484
|
}
|
|
9486
9485
|
|
|
9487
9486
|
// ../../packages/serviceme-protocol/src/errors.ts
|
|
@@ -9497,8 +9496,22 @@ var SERVICEME_ERROR_CODES = [
|
|
|
9497
9496
|
"opencode_request_failed",
|
|
9498
9497
|
"opencode_backend_not_running",
|
|
9499
9498
|
"opencode_session_not_found",
|
|
9499
|
+
"copilot_not_installed",
|
|
9500
|
+
"copilot_auth_required",
|
|
9501
|
+
"copilot_execution_failed",
|
|
9502
|
+
"copilot_timeout",
|
|
9500
9503
|
"json_invalid_input",
|
|
9501
9504
|
"env_tool_not_found",
|
|
9505
|
+
"task_not_found",
|
|
9506
|
+
"task_already_exists",
|
|
9507
|
+
"invalid_schedule",
|
|
9508
|
+
"invalid_payload",
|
|
9509
|
+
"confirmation_required",
|
|
9510
|
+
"workspace_not_found",
|
|
9511
|
+
"daemon_not_running",
|
|
9512
|
+
"executor_timeout",
|
|
9513
|
+
"executor_error",
|
|
9514
|
+
"task_already_running",
|
|
9502
9515
|
"internal_error"
|
|
9503
9516
|
];
|
|
9504
9517
|
var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
@@ -9507,6 +9520,8 @@ var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
9507
9520
|
"opencode_startup_timeout",
|
|
9508
9521
|
"opencode_request_failed",
|
|
9509
9522
|
"opencode_backend_not_running",
|
|
9523
|
+
"copilot_timeout",
|
|
9524
|
+
"executor_timeout",
|
|
9510
9525
|
"internal_error"
|
|
9511
9526
|
]);
|
|
9512
9527
|
function isRecord2(value) {
|
|
@@ -9580,6 +9595,15 @@ function normalizeServicemeError(error, fallbackCode = "internal_error") {
|
|
|
9580
9595
|
retryable: isRetryableErrorCode(fallbackCode)
|
|
9581
9596
|
};
|
|
9582
9597
|
}
|
|
9598
|
+
|
|
9599
|
+
// src/version.ts
|
|
9600
|
+
var SERVICEME_CLI_VERSION = "0.1.2";
|
|
9601
|
+
|
|
9602
|
+
// src/bridge/ndjson.ts
|
|
9603
|
+
function writeBridgeMessage(message) {
|
|
9604
|
+
process.stdout.write(`${JSON.stringify(message)}
|
|
9605
|
+
`);
|
|
9606
|
+
}
|
|
9583
9607
|
function terminateCommandProcess(child) {
|
|
9584
9608
|
if (child.killed) {
|
|
9585
9609
|
return;
|
|
@@ -9673,12 +9697,14 @@ async function runCommand(command, options2 = {}) {
|
|
|
9673
9697
|
}
|
|
9674
9698
|
});
|
|
9675
9699
|
}
|
|
9676
|
-
|
|
9677
|
-
|
|
9700
|
+
|
|
9701
|
+
// ../../packages/serviceme-core/src/copilot/doctor.ts
|
|
9702
|
+
var GH_COMMAND = "gh";
|
|
9703
|
+
async function isCopilotAuthenticated() {
|
|
9678
9704
|
try {
|
|
9679
|
-
await runCommand(
|
|
9680
|
-
args: [
|
|
9681
|
-
timeoutMs:
|
|
9705
|
+
await runCommand(GH_COMMAND, {
|
|
9706
|
+
args: ["auth", "status"],
|
|
9707
|
+
timeoutMs: 1e4
|
|
9682
9708
|
});
|
|
9683
9709
|
return true;
|
|
9684
9710
|
} catch {
|
|
@@ -9689,423 +9715,511 @@ async function commandExists(command) {
|
|
|
9689
9715
|
// ../../packages/serviceme-core/src/json/jsonTools.ts
|
|
9690
9716
|
__toESM(require_src2());
|
|
9691
9717
|
|
|
9692
|
-
// ../../packages/serviceme-core/src/
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
startupPollIntervalMs: 200,
|
|
9713
|
-
idleTimeoutMs: 10 * 60 * 1e3,
|
|
9714
|
-
stdoutLogLimitBytes: 64 * 1024
|
|
9715
|
-
};
|
|
9716
|
-
var OpenCodeManager = class {
|
|
9717
|
-
constructor(options2 = {}) {
|
|
9718
|
-
this.stdoutBytes = 0;
|
|
9719
|
-
this.options = {
|
|
9720
|
-
...DEFAULT_OPTIONS,
|
|
9721
|
-
...options2
|
|
9722
|
-
};
|
|
9723
|
-
this.logger = options2.logger ?? noopLogger;
|
|
9724
|
-
}
|
|
9725
|
-
async isAvailable() {
|
|
9726
|
-
return commandExists(this.options.command);
|
|
9727
|
-
}
|
|
9728
|
-
isRunning() {
|
|
9729
|
-
return Boolean(this.childProcess && !this.childProcess.killed && this.port);
|
|
9730
|
-
}
|
|
9731
|
-
async ensureServer(runtime = {}) {
|
|
9732
|
-
await this.ensureServerStarted(runtime);
|
|
9733
|
-
return this.getServerState();
|
|
9734
|
-
}
|
|
9735
|
-
async getServerState() {
|
|
9736
|
-
const available = await this.isAvailable();
|
|
9737
|
-
const running = this.isRunning();
|
|
9738
|
-
const healthy = this.port ? await this.isServerHealthy(this.port) : false;
|
|
9739
|
-
return {
|
|
9740
|
-
available,
|
|
9741
|
-
running,
|
|
9742
|
-
healthy,
|
|
9743
|
-
installUrl: this.options.installUrl,
|
|
9744
|
-
pid: this.childProcess?.pid,
|
|
9745
|
-
port: this.port
|
|
9746
|
-
};
|
|
9747
|
-
}
|
|
9748
|
-
async createSession(runtime = {}) {
|
|
9749
|
-
await this.ensureServerStarted(runtime);
|
|
9750
|
-
const session = await this.request(
|
|
9751
|
-
this.options.sessionPath,
|
|
9752
|
-
{
|
|
9753
|
-
method: "POST",
|
|
9754
|
-
query: this.createQuery(runtime.workspaceRoot),
|
|
9755
|
-
body: {}
|
|
9756
|
-
}
|
|
9718
|
+
// ../../packages/serviceme-core/src/utils/fileUtils.ts
|
|
9719
|
+
__toESM(require_yauzl());
|
|
9720
|
+
var DEFAULT_TIMEOUT = 3e5;
|
|
9721
|
+
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
9722
|
+
var GithubCopilotCliExecutor = class {
|
|
9723
|
+
async execute(payload, abortSignal) {
|
|
9724
|
+
const authenticated = await isCopilotAuthenticated();
|
|
9725
|
+
if (!authenticated) {
|
|
9726
|
+
return {
|
|
9727
|
+
status: "failure",
|
|
9728
|
+
error: "GitHub Copilot CLI authentication failed. Please run `gh auth login` to re-authenticate."
|
|
9729
|
+
};
|
|
9730
|
+
}
|
|
9731
|
+
let output = "";
|
|
9732
|
+
const handle = this.executeStreaming(
|
|
9733
|
+
payload,
|
|
9734
|
+
(_stream, data) => {
|
|
9735
|
+
output += data;
|
|
9736
|
+
},
|
|
9737
|
+
abortSignal
|
|
9757
9738
|
);
|
|
9758
|
-
|
|
9759
|
-
return {
|
|
9760
|
-
sessionId: session.id,
|
|
9761
|
-
title: session.title,
|
|
9762
|
-
status: "running"
|
|
9763
|
-
};
|
|
9739
|
+
const result = await handle.result;
|
|
9740
|
+
return { ...result, output: output || result.output };
|
|
9764
9741
|
}
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
query: this.createQuery(input.workspaceRoot),
|
|
9772
|
-
body: {
|
|
9773
|
-
parts: [
|
|
9774
|
-
{
|
|
9775
|
-
type: "text",
|
|
9776
|
-
text: input.prompt
|
|
9777
|
-
}
|
|
9778
|
-
]
|
|
9779
|
-
},
|
|
9780
|
-
signal: input.signal
|
|
9781
|
-
}
|
|
9782
|
-
);
|
|
9783
|
-
const latestMessage = await this.getLatestAssistantMessage(
|
|
9784
|
-
input.sessionId,
|
|
9785
|
-
input.workspaceRoot,
|
|
9786
|
-
input.signal
|
|
9787
|
-
);
|
|
9788
|
-
input.onEvent?.({
|
|
9789
|
-
type: "status",
|
|
9790
|
-
status: "running"
|
|
9742
|
+
executeStreaming(payload, onOutput, abortSignal) {
|
|
9743
|
+
const p = payload;
|
|
9744
|
+
const timeout = (p.timeout != null ? p.timeout * 1e3 : null) ?? DEFAULT_TIMEOUT;
|
|
9745
|
+
let resolve;
|
|
9746
|
+
const resultPromise = new Promise((r) => {
|
|
9747
|
+
resolve = r;
|
|
9791
9748
|
});
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
9749
|
+
let settled = false;
|
|
9750
|
+
const settle = (result) => {
|
|
9751
|
+
if (settled) return;
|
|
9752
|
+
settled = true;
|
|
9753
|
+
clearTimeout(timer);
|
|
9754
|
+
resolve?.(result);
|
|
9755
|
+
};
|
|
9756
|
+
if (abortSignal?.aborted) {
|
|
9757
|
+
return {
|
|
9758
|
+
result: Promise.resolve({
|
|
9759
|
+
status: "cancelled",
|
|
9760
|
+
error: "Execution aborted"
|
|
9761
|
+
}),
|
|
9762
|
+
cancel: () => {
|
|
9763
|
+
}
|
|
9764
|
+
};
|
|
9765
|
+
}
|
|
9766
|
+
const args = ["copilot", "prompt", "--prompt", p.prompt];
|
|
9767
|
+
if (p.autopilot) {
|
|
9768
|
+
args.push("--autopilot");
|
|
9769
|
+
}
|
|
9770
|
+
if (p.allowTools && p.allowTools.length > 0) {
|
|
9771
|
+
args.push("--allow-tools", p.allowTools.join(","));
|
|
9772
|
+
}
|
|
9773
|
+
if (p.model) {
|
|
9774
|
+
args.push("--model", p.model);
|
|
9797
9775
|
}
|
|
9798
|
-
|
|
9799
|
-
|
|
9776
|
+
if (p.agent) {
|
|
9777
|
+
args.push("--agent", p.agent);
|
|
9778
|
+
}
|
|
9779
|
+
if (p.timeout != null) {
|
|
9780
|
+
args.push("--timeout", String(p.timeout));
|
|
9781
|
+
}
|
|
9782
|
+
const child = child_process.spawn("serviceme", args, {
|
|
9783
|
+
cwd: p.workspace,
|
|
9784
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
9800
9785
|
});
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9786
|
+
const timer = setTimeout(() => {
|
|
9787
|
+
child.kill("SIGTERM");
|
|
9788
|
+
setTimeout(() => {
|
|
9789
|
+
if (!child.killed) child.kill("SIGKILL");
|
|
9790
|
+
}, 5e3);
|
|
9791
|
+
settle({
|
|
9792
|
+
status: "timeout",
|
|
9793
|
+
error: `Copilot CLI execution timed out after ${timeout / 1e3}s`
|
|
9794
|
+
});
|
|
9795
|
+
}, timeout);
|
|
9796
|
+
let stdoutBuf = "";
|
|
9797
|
+
let stderrBuf = "";
|
|
9798
|
+
child.stdout.on("data", (chunk) => {
|
|
9799
|
+
const data = chunk.toString();
|
|
9800
|
+
stdoutBuf += data;
|
|
9801
|
+
if (stdoutBuf.length > MAX_OUTPUT_BYTES)
|
|
9802
|
+
stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES);
|
|
9803
|
+
onOutput("stdout", data);
|
|
9808
9804
|
});
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
await this.request(`${this.options.sessionPath}/${sessionId}/abort`, {
|
|
9816
|
-
method: "POST",
|
|
9817
|
-
query: this.createQuery(workspaceRoot)
|
|
9805
|
+
child.stderr.on("data", (chunk) => {
|
|
9806
|
+
const data = chunk.toString();
|
|
9807
|
+
stderrBuf += data;
|
|
9808
|
+
if (stderrBuf.length > MAX_OUTPUT_BYTES)
|
|
9809
|
+
stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES);
|
|
9810
|
+
onOutput("stderr", data);
|
|
9818
9811
|
});
|
|
9819
|
-
|
|
9812
|
+
child.on("close", (code) => {
|
|
9813
|
+
if (code === 0) {
|
|
9814
|
+
settle({ status: "success", output: stdoutBuf || void 0 });
|
|
9815
|
+
} else {
|
|
9816
|
+
settle({
|
|
9817
|
+
status: "failure",
|
|
9818
|
+
output: stdoutBuf || void 0,
|
|
9819
|
+
error: stderrBuf || `Process exited with code ${code}`
|
|
9820
|
+
});
|
|
9821
|
+
}
|
|
9822
|
+
});
|
|
9823
|
+
child.on("error", (err) => {
|
|
9824
|
+
settle({ status: "failure", error: err.message });
|
|
9825
|
+
});
|
|
9826
|
+
const cancelFn = () => {
|
|
9827
|
+
child.kill("SIGTERM");
|
|
9828
|
+
setTimeout(() => {
|
|
9829
|
+
if (!child.killed) child.kill("SIGKILL");
|
|
9830
|
+
}, 5e3);
|
|
9831
|
+
settle({ status: "cancelled", error: "Execution cancelled" });
|
|
9832
|
+
};
|
|
9833
|
+
abortSignal?.addEventListener("abort", () => cancelFn(), { once: true });
|
|
9834
|
+
return { result: resultPromise, cancel: cancelFn };
|
|
9820
9835
|
}
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9836
|
+
};
|
|
9837
|
+
|
|
9838
|
+
// ../../packages/serviceme-core/src/scheduled-tasks/executors/HttpRequestExecutor.ts
|
|
9839
|
+
var DEFAULT_TIMEOUT2 = 3e4;
|
|
9840
|
+
var HttpRequestExecutor = class {
|
|
9841
|
+
async execute(payload, abortSignal) {
|
|
9842
|
+
const p = payload;
|
|
9843
|
+
const timeout = (p.timeout != null ? p.timeout * 1e3 : null) ?? DEFAULT_TIMEOUT2;
|
|
9844
|
+
const ac = new AbortController();
|
|
9845
|
+
const timer = setTimeout(() => {
|
|
9846
|
+
ac.abort();
|
|
9847
|
+
}, timeout);
|
|
9848
|
+
if (abortSignal?.aborted) {
|
|
9849
|
+
clearTimeout(timer);
|
|
9850
|
+
return { status: "cancelled", error: "Execution aborted" };
|
|
9825
9851
|
}
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9852
|
+
let externalAbort = false;
|
|
9853
|
+
abortSignal?.addEventListener(
|
|
9854
|
+
"abort",
|
|
9855
|
+
() => {
|
|
9856
|
+
externalAbort = true;
|
|
9857
|
+
clearTimeout(timer);
|
|
9858
|
+
ac.abort();
|
|
9859
|
+
},
|
|
9860
|
+
{ once: true }
|
|
9861
|
+
);
|
|
9862
|
+
try {
|
|
9863
|
+
const response = await fetch(p.url, {
|
|
9864
|
+
method: p.method,
|
|
9865
|
+
headers: p.headers,
|
|
9866
|
+
body: p.body,
|
|
9867
|
+
signal: ac.signal
|
|
9868
|
+
});
|
|
9869
|
+
clearTimeout(timer);
|
|
9870
|
+
const body = await response.text();
|
|
9871
|
+
if (response.ok) {
|
|
9872
|
+
return {
|
|
9873
|
+
status: "success",
|
|
9874
|
+
output: `${response.status} ${response.statusText}
|
|
9875
|
+
${body}`.trim()
|
|
9876
|
+
};
|
|
9833
9877
|
}
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
}
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
this.startupPromise = this.startServerProcess(runtime);
|
|
9850
|
-
try {
|
|
9851
|
-
await this.startupPromise;
|
|
9852
|
-
return;
|
|
9853
|
-
} catch (error) {
|
|
9854
|
-
lastError = error;
|
|
9855
|
-
this.logger.warn(
|
|
9856
|
-
`OpenCode server start attempt ${String(attempt + 1)} failed, retrying...`
|
|
9857
|
-
);
|
|
9858
|
-
} finally {
|
|
9859
|
-
this.startupPromise = void 0;
|
|
9878
|
+
return {
|
|
9879
|
+
status: "failure",
|
|
9880
|
+
error: `HTTP ${response.status} ${response.statusText}
|
|
9881
|
+
${body}`.trim()
|
|
9882
|
+
};
|
|
9883
|
+
} catch (err) {
|
|
9884
|
+
clearTimeout(timer);
|
|
9885
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
9886
|
+
if (externalAbort) {
|
|
9887
|
+
return { status: "cancelled", error: "Execution cancelled" };
|
|
9888
|
+
}
|
|
9889
|
+
return {
|
|
9890
|
+
status: "timeout",
|
|
9891
|
+
error: `HTTP request timed out after ${timeout / 1e3}s`
|
|
9892
|
+
};
|
|
9860
9893
|
}
|
|
9894
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9895
|
+
return { status: "failure", error: message };
|
|
9861
9896
|
}
|
|
9862
|
-
throw lastError;
|
|
9863
9897
|
}
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9898
|
+
};
|
|
9899
|
+
var DEFAULT_TIMEOUT3 = 6e4;
|
|
9900
|
+
var MAX_OUTPUT_BYTES2 = 1024 * 1024;
|
|
9901
|
+
var ShellExecutor = class {
|
|
9902
|
+
async execute(payload, abortSignal) {
|
|
9903
|
+
let output = "";
|
|
9904
|
+
const handle = this.executeStreaming(
|
|
9905
|
+
payload,
|
|
9906
|
+
(_stream, data) => {
|
|
9907
|
+
output += data;
|
|
9872
9908
|
},
|
|
9909
|
+
abortSignal
|
|
9910
|
+
);
|
|
9911
|
+
const result = await handle.result;
|
|
9912
|
+
return { ...result, output: output || result.output };
|
|
9913
|
+
}
|
|
9914
|
+
executeStreaming(payload, onOutput, abortSignal) {
|
|
9915
|
+
const p = payload;
|
|
9916
|
+
const timeout = (p.timeout != null ? p.timeout * 1e3 : null) ?? DEFAULT_TIMEOUT3;
|
|
9917
|
+
let resolve;
|
|
9918
|
+
const resultPromise = new Promise((r) => {
|
|
9919
|
+
resolve = r;
|
|
9920
|
+
});
|
|
9921
|
+
let settled = false;
|
|
9922
|
+
const settle = (result) => {
|
|
9923
|
+
if (settled) return;
|
|
9924
|
+
settled = true;
|
|
9925
|
+
clearTimeout(timer);
|
|
9926
|
+
resolve?.(result);
|
|
9927
|
+
};
|
|
9928
|
+
if (abortSignal?.aborted) {
|
|
9929
|
+
return {
|
|
9930
|
+
result: Promise.resolve({
|
|
9931
|
+
status: "cancelled",
|
|
9932
|
+
error: "Execution aborted"
|
|
9933
|
+
}),
|
|
9934
|
+
cancel: () => {
|
|
9935
|
+
}
|
|
9936
|
+
};
|
|
9937
|
+
}
|
|
9938
|
+
const child = child_process.spawn("sh", ["-c", p.script], {
|
|
9939
|
+
cwd: p.cwd,
|
|
9873
9940
|
stdio: ["ignore", "pipe", "pipe"]
|
|
9874
9941
|
});
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9942
|
+
const timer = setTimeout(() => {
|
|
9943
|
+
child.kill("SIGTERM");
|
|
9944
|
+
setTimeout(() => {
|
|
9945
|
+
if (!child.killed) child.kill("SIGKILL");
|
|
9946
|
+
}, 5e3);
|
|
9947
|
+
settle({
|
|
9948
|
+
status: "timeout",
|
|
9949
|
+
error: `Shell execution timed out after ${timeout / 1e3}s`
|
|
9950
|
+
});
|
|
9951
|
+
}, timeout);
|
|
9952
|
+
let stdoutBuf = "";
|
|
9953
|
+
let stderrBuf = "";
|
|
9954
|
+
child.stdout.on("data", (chunk) => {
|
|
9955
|
+
const data = chunk.toString();
|
|
9956
|
+
stdoutBuf += data;
|
|
9957
|
+
if (stdoutBuf.length > MAX_OUTPUT_BYTES2)
|
|
9958
|
+
stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES2);
|
|
9959
|
+
onOutput("stdout", data);
|
|
9881
9960
|
});
|
|
9882
|
-
child.stderr
|
|
9883
|
-
|
|
9961
|
+
child.stderr.on("data", (chunk) => {
|
|
9962
|
+
const data = chunk.toString();
|
|
9963
|
+
stderrBuf += data;
|
|
9964
|
+
if (stderrBuf.length > MAX_OUTPUT_BYTES2)
|
|
9965
|
+
stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES2);
|
|
9966
|
+
onOutput("stderr", data);
|
|
9884
9967
|
});
|
|
9885
|
-
child.on("
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9968
|
+
child.on("close", (code) => {
|
|
9969
|
+
if (code === 0) {
|
|
9970
|
+
settle({ status: "success", output: stdoutBuf || void 0 });
|
|
9971
|
+
} else {
|
|
9972
|
+
settle({
|
|
9973
|
+
status: "failure",
|
|
9974
|
+
output: stdoutBuf || void 0,
|
|
9975
|
+
error: stderrBuf || `Process exited with code ${code}`
|
|
9976
|
+
});
|
|
9890
9977
|
}
|
|
9891
9978
|
});
|
|
9892
|
-
child.on("
|
|
9893
|
-
|
|
9894
|
-
this.childProcess = void 0;
|
|
9895
|
-
this.port = void 0;
|
|
9896
|
-
}
|
|
9979
|
+
child.on("error", (err) => {
|
|
9980
|
+
settle({ status: "failure", error: err.message });
|
|
9897
9981
|
});
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
if (!ready) {
|
|
9908
|
-
await this.terminateTrackedProcess("startup-timeout");
|
|
9909
|
-
throw createServicemeError(
|
|
9910
|
-
"opencode_startup_timeout",
|
|
9911
|
-
"OpenCode local server did not start in time."
|
|
9912
|
-
);
|
|
9913
|
-
}
|
|
9982
|
+
const cancelFn = () => {
|
|
9983
|
+
child.kill("SIGTERM");
|
|
9984
|
+
setTimeout(() => {
|
|
9985
|
+
if (!child.killed) child.kill("SIGKILL");
|
|
9986
|
+
}, 5e3);
|
|
9987
|
+
settle({ status: "cancelled", error: "Execution cancelled" });
|
|
9988
|
+
};
|
|
9989
|
+
abortSignal?.addEventListener("abort", () => cancelFn(), { once: true });
|
|
9990
|
+
return { result: resultPromise, cancel: cancelFn };
|
|
9914
9991
|
}
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9992
|
+
};
|
|
9993
|
+
|
|
9994
|
+
// ../../packages/serviceme-core/src/scheduled-tasks/executors/types.ts
|
|
9995
|
+
function isStreamingTaskExecutor(executor) {
|
|
9996
|
+
return "executeStreaming" in executor && typeof executor.executeStreaming === "function";
|
|
9997
|
+
}
|
|
9998
|
+
|
|
9999
|
+
// ../../packages/serviceme-core/src/scheduled-tasks/executors/index.ts
|
|
10000
|
+
var executors = {
|
|
10001
|
+
shell: new ShellExecutor(),
|
|
10002
|
+
http_request: new HttpRequestExecutor(),
|
|
10003
|
+
github_copilot_cli: new GithubCopilotCliExecutor()
|
|
10004
|
+
};
|
|
10005
|
+
function getExecutor(taskType) {
|
|
10006
|
+
return executors[taskType];
|
|
10007
|
+
}
|
|
10008
|
+
|
|
10009
|
+
// ../../packages/serviceme-core/src/scheduled-tasks/TaskExecutionEngine.ts
|
|
10010
|
+
var TaskExecutionEngine = class {
|
|
10011
|
+
constructor(getExecutor2) {
|
|
10012
|
+
this.getExecutor = getExecutor2;
|
|
10013
|
+
this.running = /* @__PURE__ */ new Map();
|
|
10014
|
+
this.taskExecutions = /* @__PURE__ */ new Map();
|
|
10015
|
+
this.listener = null;
|
|
9926
10016
|
}
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
const response = await fetch(
|
|
9930
|
-
`http://${this.options.host}:${port}${this.options.healthPath}`
|
|
9931
|
-
);
|
|
9932
|
-
return response.ok;
|
|
9933
|
-
} catch {
|
|
9934
|
-
return false;
|
|
9935
|
-
}
|
|
10017
|
+
setListener(listener) {
|
|
10018
|
+
this.listener = listener;
|
|
9936
10019
|
}
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
10020
|
+
async execute(snapshot) {
|
|
10021
|
+
const policy = snapshot.concurrencyPolicy ?? "reject";
|
|
10022
|
+
if (policy === "reject") {
|
|
10023
|
+
const existingIds = this.taskExecutions.get(snapshot.taskId);
|
|
10024
|
+
if (existingIds && existingIds.size > 0) {
|
|
10025
|
+
throw new Error(
|
|
10026
|
+
`TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`
|
|
10027
|
+
);
|
|
10028
|
+
}
|
|
9940
10029
|
}
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
9945
|
-
}
|
|
9946
|
-
async terminateTrackedProcess(reason) {
|
|
9947
|
-
const child = this.childProcess;
|
|
9948
|
-
this.childProcess = void 0;
|
|
9949
|
-
this.port = void 0;
|
|
9950
|
-
if (!child || child.killed) {
|
|
9951
|
-
return;
|
|
10030
|
+
const executor = this.getExecutor(snapshot.type);
|
|
10031
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10032
|
+
if (!this.taskExecutions.has(snapshot.taskId)) {
|
|
10033
|
+
this.taskExecutions.set(snapshot.taskId, /* @__PURE__ */ new Set());
|
|
9952
10034
|
}
|
|
9953
|
-
this.
|
|
9954
|
-
|
|
9955
|
-
|
|
10035
|
+
this.taskExecutions.get(snapshot.taskId)?.add(snapshot.executionId);
|
|
10036
|
+
const ac = new AbortController();
|
|
10037
|
+
const runningExec = {
|
|
10038
|
+
executionId: snapshot.executionId,
|
|
10039
|
+
taskId: snapshot.taskId,
|
|
10040
|
+
startedAt,
|
|
10041
|
+
cancel: () => ac.abort()
|
|
10042
|
+
};
|
|
10043
|
+
this.running.set(snapshot.executionId, runningExec);
|
|
10044
|
+
this.listener?.onStarted({
|
|
10045
|
+
executionId: snapshot.executionId,
|
|
10046
|
+
taskId: snapshot.taskId,
|
|
10047
|
+
startedAt
|
|
9956
10048
|
});
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
{
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
10049
|
+
try {
|
|
10050
|
+
let result;
|
|
10051
|
+
if (isStreamingTaskExecutor(executor)) {
|
|
10052
|
+
const handle = executor.executeStreaming(
|
|
10053
|
+
snapshot.payload,
|
|
10054
|
+
(stream, data) => {
|
|
10055
|
+
this.listener?.onOutput({
|
|
10056
|
+
executionId: snapshot.executionId,
|
|
10057
|
+
stream,
|
|
10058
|
+
data
|
|
10059
|
+
});
|
|
10060
|
+
},
|
|
10061
|
+
ac.signal
|
|
10062
|
+
);
|
|
10063
|
+
runningExec.cancel = () => {
|
|
10064
|
+
handle.cancel();
|
|
10065
|
+
ac.abort();
|
|
10066
|
+
};
|
|
10067
|
+
result = await handle.result;
|
|
10068
|
+
} else {
|
|
10069
|
+
result = await executor.execute(snapshot.payload, ac.signal);
|
|
10070
|
+
}
|
|
10071
|
+
const log = {
|
|
10072
|
+
id: snapshot.executionId,
|
|
10073
|
+
taskId: snapshot.taskId,
|
|
10074
|
+
taskName: snapshot.name,
|
|
10075
|
+
startedAt,
|
|
10076
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10077
|
+
status: result.status === "running" ? "failure" : result.status,
|
|
10078
|
+
output: result.output,
|
|
10079
|
+
error: result.error
|
|
10080
|
+
};
|
|
10081
|
+
switch (log.status) {
|
|
10082
|
+
case "success":
|
|
10083
|
+
this.listener?.onCompleted({
|
|
10084
|
+
executionId: snapshot.executionId,
|
|
10085
|
+
log
|
|
9972
10086
|
});
|
|
10087
|
+
break;
|
|
10088
|
+
case "cancelled":
|
|
10089
|
+
this.listener?.onCancelled({
|
|
10090
|
+
executionId: snapshot.executionId,
|
|
10091
|
+
log
|
|
10092
|
+
});
|
|
10093
|
+
break;
|
|
10094
|
+
case "timeout":
|
|
10095
|
+
this.listener?.onFailed({
|
|
10096
|
+
executionId: snapshot.executionId,
|
|
10097
|
+
status: "timeout",
|
|
10098
|
+
reason: "timeout",
|
|
10099
|
+
log
|
|
10100
|
+
});
|
|
10101
|
+
break;
|
|
10102
|
+
default:
|
|
10103
|
+
this.listener?.onFailed({
|
|
10104
|
+
executionId: snapshot.executionId,
|
|
10105
|
+
status: "failure",
|
|
10106
|
+
reason: "error",
|
|
10107
|
+
log
|
|
10108
|
+
});
|
|
10109
|
+
break;
|
|
10110
|
+
}
|
|
10111
|
+
} catch (err) {
|
|
10112
|
+
const log = {
|
|
10113
|
+
id: snapshot.executionId,
|
|
10114
|
+
taskId: snapshot.taskId,
|
|
10115
|
+
taskName: snapshot.name,
|
|
10116
|
+
startedAt,
|
|
10117
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10118
|
+
status: ac.signal.aborted ? "cancelled" : "failure",
|
|
10119
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10120
|
+
};
|
|
10121
|
+
if (ac.signal.aborted) {
|
|
10122
|
+
this.listener?.onCancelled({
|
|
10123
|
+
executionId: snapshot.executionId,
|
|
10124
|
+
log
|
|
10125
|
+
});
|
|
10126
|
+
} else {
|
|
10127
|
+
this.listener?.onFailed({
|
|
10128
|
+
executionId: snapshot.executionId,
|
|
10129
|
+
status: "failure",
|
|
10130
|
+
reason: "error",
|
|
10131
|
+
log
|
|
9973
10132
|
});
|
|
9974
|
-
return;
|
|
9975
10133
|
}
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
10134
|
+
} finally {
|
|
10135
|
+
this.running.delete(snapshot.executionId);
|
|
10136
|
+
const taskExecIds = this.taskExecutions.get(snapshot.taskId);
|
|
10137
|
+
if (taskExecIds) {
|
|
10138
|
+
taskExecIds.delete(snapshot.executionId);
|
|
10139
|
+
if (taskExecIds.size === 0) {
|
|
10140
|
+
this.taskExecutions.delete(snapshot.taskId);
|
|
10141
|
+
}
|
|
10142
|
+
}
|
|
9982
10143
|
}
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
10144
|
+
}
|
|
10145
|
+
cancel(executionId) {
|
|
10146
|
+
const exec = this.running.get(executionId);
|
|
10147
|
+
if (!exec) return false;
|
|
10148
|
+
exec.cancel();
|
|
10149
|
+
return true;
|
|
10150
|
+
}
|
|
10151
|
+
listRunning() {
|
|
10152
|
+
return Array.from(this.running.values()).map((e) => ({
|
|
10153
|
+
executionId: e.executionId,
|
|
10154
|
+
taskId: e.taskId,
|
|
10155
|
+
startedAt: e.startedAt
|
|
10156
|
+
}));
|
|
10157
|
+
}
|
|
10158
|
+
dispose() {
|
|
10159
|
+
for (const exec of this.running.values()) {
|
|
10160
|
+
exec.cancel();
|
|
9988
10161
|
}
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10162
|
+
this.running.clear();
|
|
10163
|
+
this.taskExecutions.clear();
|
|
10164
|
+
}
|
|
10165
|
+
};
|
|
10166
|
+
|
|
10167
|
+
// src/bridge/TaskBridgeHandler.ts
|
|
10168
|
+
var TaskBridgeHandler = class {
|
|
10169
|
+
constructor(logger, emitEvent) {
|
|
10170
|
+
this.logger = logger;
|
|
10171
|
+
this.emitEvent = emitEvent;
|
|
10172
|
+
this.engine = new TaskExecutionEngine(
|
|
10173
|
+
(taskType) => getExecutor(taskType)
|
|
10174
|
+
);
|
|
10175
|
+
this.engine.setListener({
|
|
10176
|
+
onStarted: (params) => this.emitEvent("task.started", params),
|
|
10177
|
+
onOutput: (params) => this.emitEvent("task.output", params),
|
|
10178
|
+
onCompleted: (params) => this.emitEvent("task.completed", params),
|
|
10179
|
+
onFailed: (params) => this.emitEvent("task.failed", params),
|
|
10180
|
+
onCancelled: (params) => this.emitEvent("task.cancelled", params)
|
|
10006
10181
|
});
|
|
10007
10182
|
}
|
|
10008
|
-
async
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
"OpenCode local server is not running."
|
|
10013
|
-
);
|
|
10014
|
-
}
|
|
10015
|
-
const url = new URL(`http://${this.options.host}:${this.port}${path3}`);
|
|
10016
|
-
for (const [key, value] of Object.entries(options2.query ?? {})) {
|
|
10017
|
-
if (value) {
|
|
10018
|
-
url.searchParams.set(key, value);
|
|
10019
|
-
}
|
|
10020
|
-
}
|
|
10021
|
-
const response = await fetch(url, {
|
|
10022
|
-
method: options2.method,
|
|
10023
|
-
headers: {
|
|
10024
|
-
"Content-Type": "application/json"
|
|
10025
|
-
},
|
|
10026
|
-
body: options2.body ? JSON.stringify(options2.body) : void 0,
|
|
10027
|
-
signal: options2.signal
|
|
10183
|
+
async execute(snapshot) {
|
|
10184
|
+
let earlyError;
|
|
10185
|
+
const executePromise = this.engine.execute(snapshot).catch((err) => {
|
|
10186
|
+
earlyError = err;
|
|
10028
10187
|
});
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
`OpenCode request failed: ${response.status} ${response.statusText}`
|
|
10033
|
-
);
|
|
10188
|
+
await Promise.resolve();
|
|
10189
|
+
if (earlyError) {
|
|
10190
|
+
throw earlyError;
|
|
10034
10191
|
}
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
}
|
|
10038
|
-
return
|
|
10192
|
+
executePromise.then(void 0, (err) => {
|
|
10193
|
+
this.logger.error("Task execution background error", err);
|
|
10194
|
+
});
|
|
10195
|
+
return { executionId: snapshot.executionId, accepted: true };
|
|
10039
10196
|
}
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
};
|
|
10197
|
+
cancel(params) {
|
|
10198
|
+
const cancelled = this.engine.cancel(params.executionId);
|
|
10199
|
+
return { executionId: params.executionId, cancelled };
|
|
10044
10200
|
}
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
`${this.options.sessionPath}/${sessionId}/message`,
|
|
10048
|
-
{
|
|
10049
|
-
method: "GET",
|
|
10050
|
-
query: {
|
|
10051
|
-
...this.createQuery(workspaceRoot),
|
|
10052
|
-
limit: "20"
|
|
10053
|
-
},
|
|
10054
|
-
signal
|
|
10055
|
-
}
|
|
10056
|
-
);
|
|
10057
|
-
const latestAssistant = [...messages].reverse().find((message) => message.info?.role === "assistant");
|
|
10058
|
-
if (!latestAssistant?.parts) {
|
|
10059
|
-
return void 0;
|
|
10060
|
-
}
|
|
10061
|
-
return latestAssistant.parts.filter(
|
|
10062
|
-
(part) => part.type === "text" && typeof part.text === "string"
|
|
10063
|
-
).map((part) => part.text).join("\n\n");
|
|
10201
|
+
listRunning() {
|
|
10202
|
+
return { executions: this.engine.listRunning() };
|
|
10064
10203
|
}
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
case "running":
|
|
10068
|
-
case "active":
|
|
10069
|
-
return "running";
|
|
10070
|
-
case "needs_input":
|
|
10071
|
-
return "needs-input";
|
|
10072
|
-
case "completed":
|
|
10073
|
-
return "completed";
|
|
10074
|
-
case "failed":
|
|
10075
|
-
return "failed";
|
|
10076
|
-
case "aborted":
|
|
10077
|
-
return "aborted";
|
|
10078
|
-
default:
|
|
10079
|
-
return "idle";
|
|
10080
|
-
}
|
|
10204
|
+
dispose() {
|
|
10205
|
+
this.engine.dispose();
|
|
10081
10206
|
}
|
|
10082
10207
|
};
|
|
10083
10208
|
|
|
10084
|
-
// ../../packages/serviceme-core/src/utils/fileUtils.ts
|
|
10085
|
-
__toESM(require_yauzl());
|
|
10086
|
-
|
|
10087
|
-
// src/version.ts
|
|
10088
|
-
var SERVICEME_CLI_VERSION = "0.0.5";
|
|
10089
|
-
|
|
10090
|
-
// src/bridge/ndjson.ts
|
|
10091
|
-
function writeBridgeMessage(message) {
|
|
10092
|
-
process.stdout.write(`${JSON.stringify(message)}
|
|
10093
|
-
`);
|
|
10094
|
-
}
|
|
10095
|
-
|
|
10096
10209
|
// src/bridge/BridgeServer.ts
|
|
10097
10210
|
var CAPABILITIES = {
|
|
10098
10211
|
bridge: true,
|
|
10099
|
-
opencode: 1,
|
|
10100
10212
|
json: 1,
|
|
10101
|
-
env: 1
|
|
10213
|
+
env: 1,
|
|
10214
|
+
tasks: 1
|
|
10102
10215
|
};
|
|
10103
10216
|
var BridgeServer = class {
|
|
10104
|
-
constructor(logger
|
|
10217
|
+
constructor(logger) {
|
|
10105
10218
|
this.logger = logger;
|
|
10106
10219
|
this.handshakeCompleted = false;
|
|
10107
|
-
this.
|
|
10108
|
-
|
|
10220
|
+
this.taskHandler = new TaskBridgeHandler(logger, (event, params) => {
|
|
10221
|
+
this.writeEvent(event, params);
|
|
10222
|
+
});
|
|
10109
10223
|
}
|
|
10110
10224
|
async run() {
|
|
10111
10225
|
const reader = readline__namespace.createInterface({
|
|
@@ -10117,7 +10231,7 @@ var BridgeServer = class {
|
|
|
10117
10231
|
void this.handleLine(line);
|
|
10118
10232
|
});
|
|
10119
10233
|
reader.on("close", () => {
|
|
10120
|
-
|
|
10234
|
+
resolve();
|
|
10121
10235
|
});
|
|
10122
10236
|
});
|
|
10123
10237
|
}
|
|
@@ -10173,111 +10287,42 @@ var BridgeServer = class {
|
|
|
10173
10287
|
}
|
|
10174
10288
|
case "system.shutdown": {
|
|
10175
10289
|
const shutdownRequest = request;
|
|
10290
|
+
this.taskHandler.dispose();
|
|
10176
10291
|
this.writeSuccess(shutdownRequest.id, {
|
|
10177
10292
|
shuttingDown: true
|
|
10178
10293
|
});
|
|
10179
10294
|
queueMicrotask(() => {
|
|
10180
|
-
|
|
10181
|
-
process.exit(0);
|
|
10182
|
-
});
|
|
10295
|
+
process.exit(0);
|
|
10183
10296
|
});
|
|
10184
10297
|
return;
|
|
10185
10298
|
}
|
|
10186
|
-
case "
|
|
10187
|
-
const
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
case "opencode.session.create": {
|
|
10201
|
-
const createRequest = request;
|
|
10202
|
-
const result = await this.openCodeManager.createSession({
|
|
10203
|
-
workspaceRoot: createRequest.params.workspaceRoot,
|
|
10204
|
-
startupTimeoutMs: createRequest.params.runtime?.startupTimeoutMs
|
|
10205
|
-
});
|
|
10206
|
-
this.writeSuccess(createRequest.id, result);
|
|
10207
|
-
return;
|
|
10208
|
-
}
|
|
10209
|
-
case "opencode.session.prompt": {
|
|
10210
|
-
const promptRequest = request;
|
|
10211
|
-
this.writeSuccess(promptRequest.id, {
|
|
10212
|
-
accepted: true,
|
|
10213
|
-
sessionId: promptRequest.params.sessionId,
|
|
10214
|
-
promptId: promptRequest.params.promptId
|
|
10215
|
-
});
|
|
10216
|
-
void this.openCodeManager.prompt({
|
|
10217
|
-
sessionId: promptRequest.params.sessionId,
|
|
10218
|
-
prompt: promptRequest.params.prompt,
|
|
10219
|
-
workspaceRoot: promptRequest.params.workspaceRoot,
|
|
10220
|
-
onEvent: (payload) => {
|
|
10221
|
-
this.writeEvent("opencode.session.event", {
|
|
10222
|
-
sessionId: promptRequest.params.sessionId,
|
|
10223
|
-
promptId: promptRequest.params.promptId,
|
|
10224
|
-
payload
|
|
10225
|
-
});
|
|
10299
|
+
case "task.execute": {
|
|
10300
|
+
const execRequest = request;
|
|
10301
|
+
try {
|
|
10302
|
+
const result = await this.taskHandler.execute(execRequest.params);
|
|
10303
|
+
this.writeSuccess(request.id, result);
|
|
10304
|
+
} catch (err) {
|
|
10305
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10306
|
+
if (message.includes("TASK_ALREADY_RUNNING")) {
|
|
10307
|
+
this.writeError(
|
|
10308
|
+
request.id,
|
|
10309
|
+
createServicemeError("task_already_running", message)
|
|
10310
|
+
);
|
|
10311
|
+
} else {
|
|
10312
|
+
this.writeError(request.id, err);
|
|
10226
10313
|
}
|
|
10227
|
-
}
|
|
10228
|
-
const normalized = normalizeServicemeError(error);
|
|
10229
|
-
this.writeEvent("opencode.session.event", {
|
|
10230
|
-
sessionId: promptRequest.params.sessionId,
|
|
10231
|
-
promptId: promptRequest.params.promptId,
|
|
10232
|
-
payload: {
|
|
10233
|
-
type: "error",
|
|
10234
|
-
text: normalized.message,
|
|
10235
|
-
metadata: {
|
|
10236
|
-
code: normalized.code,
|
|
10237
|
-
retryable: normalized.retryable
|
|
10238
|
-
}
|
|
10239
|
-
}
|
|
10240
|
-
});
|
|
10241
|
-
});
|
|
10314
|
+
}
|
|
10242
10315
|
return;
|
|
10243
10316
|
}
|
|
10244
|
-
case "
|
|
10245
|
-
const
|
|
10246
|
-
const
|
|
10247
|
-
|
|
10248
|
-
statusRequest.params.workspaceRoot
|
|
10249
|
-
);
|
|
10250
|
-
this.writeSuccess(statusRequest.id, {
|
|
10251
|
-
sessionId: statusRequest.params.sessionId,
|
|
10252
|
-
status
|
|
10253
|
-
});
|
|
10317
|
+
case "task.cancel": {
|
|
10318
|
+
const cancelRequest = request;
|
|
10319
|
+
const result = this.taskHandler.cancel(cancelRequest.params);
|
|
10320
|
+
this.writeSuccess(request.id, result);
|
|
10254
10321
|
return;
|
|
10255
10322
|
}
|
|
10256
|
-
case "
|
|
10257
|
-
const
|
|
10258
|
-
|
|
10259
|
-
abortRequest.params.sessionId,
|
|
10260
|
-
abortRequest.params.workspaceRoot
|
|
10261
|
-
);
|
|
10262
|
-
this.writeSuccess(abortRequest.id, {
|
|
10263
|
-
aborted: true,
|
|
10264
|
-
sessionId: abortRequest.params.sessionId
|
|
10265
|
-
});
|
|
10266
|
-
this.writeEvent("opencode.session.event", {
|
|
10267
|
-
sessionId: abortRequest.params.sessionId,
|
|
10268
|
-
payload: {
|
|
10269
|
-
type: "status",
|
|
10270
|
-
status: "aborted"
|
|
10271
|
-
}
|
|
10272
|
-
});
|
|
10273
|
-
return;
|
|
10274
|
-
}
|
|
10275
|
-
case "opencode.session.dispose": {
|
|
10276
|
-
const disposeRequest = request;
|
|
10277
|
-
this.writeSuccess(disposeRequest.id, {
|
|
10278
|
-
disposed: true,
|
|
10279
|
-
sessionId: disposeRequest.params.sessionId
|
|
10280
|
-
});
|
|
10323
|
+
case "task.list-running": {
|
|
10324
|
+
const result = this.taskHandler.listRunning();
|
|
10325
|
+
this.writeSuccess(request.id, result);
|
|
10281
10326
|
return;
|
|
10282
10327
|
}
|
|
10283
10328
|
default:
|
|
@@ -10319,13 +10364,6 @@ var BridgeServer = class {
|
|
|
10319
10364
|
params
|
|
10320
10365
|
});
|
|
10321
10366
|
}
|
|
10322
|
-
async dispose() {
|
|
10323
|
-
if (this.shuttingDown) {
|
|
10324
|
-
return;
|
|
10325
|
-
}
|
|
10326
|
-
this.shuttingDown = true;
|
|
10327
|
-
await this.openCodeManager.dispose();
|
|
10328
|
-
}
|
|
10329
10367
|
};
|
|
10330
10368
|
|
|
10331
10369
|
exports.BridgeServer = BridgeServer;
|