@serviceme/devtools-core 0.1.6 → 0.1.8
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/index.d.mts +127 -75
- package/dist/index.d.ts +127 -75
- package/dist/index.js +214 -137
- package/dist/index.mjs +209 -139
- package/package.json +4 -2
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
// src/agents/AgentCatalogClient.ts
|
|
9
|
+
import { createServicemeError } from "@serviceme/devtools-protocol";
|
|
9
10
|
var AgentCatalogClient = class {
|
|
10
11
|
constructor(options = {}) {
|
|
11
12
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -13,10 +14,10 @@ var AgentCatalogClient = class {
|
|
|
13
14
|
}
|
|
14
15
|
async getCatalog() {
|
|
15
16
|
if (!this.baseUrl) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
throw createServicemeError(
|
|
18
|
+
"workspace_not_found",
|
|
19
|
+
"Agent catalog baseUrl is not configured."
|
|
20
|
+
);
|
|
20
21
|
}
|
|
21
22
|
const response = await this.fetchImpl(
|
|
22
23
|
`${this.baseUrl}/api/v1/marketplace/agents`
|
|
@@ -30,6 +31,30 @@ var AgentCatalogClient = class {
|
|
|
30
31
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
31
32
|
};
|
|
32
33
|
}
|
|
34
|
+
async downloadAgent(remoteId) {
|
|
35
|
+
if (!this.baseUrl) {
|
|
36
|
+
throw createServicemeError(
|
|
37
|
+
"workspace_not_found",
|
|
38
|
+
"Agent catalog baseUrl is not configured."
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const response = await this.fetchImpl(
|
|
42
|
+
`${this.baseUrl}/api/v1/marketplace/agents/download/${remoteId}`
|
|
43
|
+
);
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
if (response.status === 404) {
|
|
46
|
+
throw createServicemeError(
|
|
47
|
+
"not_found",
|
|
48
|
+
`Agent '${remoteId}' not found`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Failed to download agent ${remoteId}: ${response.status}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const payload = await response.json();
|
|
56
|
+
return payload.data?.files ?? [];
|
|
57
|
+
}
|
|
33
58
|
};
|
|
34
59
|
|
|
35
60
|
// src/permissions/agent-permissions.ts
|
|
@@ -289,7 +314,7 @@ var AgentStore = class {
|
|
|
289
314
|
// src/copilot/doctor.ts
|
|
290
315
|
import {
|
|
291
316
|
COPILOT_ERROR_CODES,
|
|
292
|
-
createServicemeError
|
|
317
|
+
createServicemeError as createServicemeError2
|
|
293
318
|
} from "@serviceme/devtools-protocol";
|
|
294
319
|
|
|
295
320
|
// src/process/runCommand.ts
|
|
@@ -434,13 +459,13 @@ async function copilotDoctor() {
|
|
|
434
459
|
return { installed: true, version, authenticated };
|
|
435
460
|
}
|
|
436
461
|
function createCopilotNotInstalledError() {
|
|
437
|
-
return
|
|
462
|
+
return createServicemeError2(
|
|
438
463
|
COPILOT_ERROR_CODES.NOT_INSTALLED,
|
|
439
464
|
"GitHub Copilot CLI is not installed. Visit https://docs.github.com/en/copilot/using-github-copilot/using-github-copilot-in-the-command-line to install."
|
|
440
465
|
);
|
|
441
466
|
}
|
|
442
467
|
function createCopilotAuthRequiredError() {
|
|
443
|
-
return
|
|
468
|
+
return createServicemeError2(
|
|
444
469
|
COPILOT_ERROR_CODES.AUTH_REQUIRED,
|
|
445
470
|
"GitHub Copilot CLI requires authentication. Run `copilot auth login` to sign in."
|
|
446
471
|
);
|
|
@@ -449,7 +474,8 @@ function createCopilotAuthRequiredError() {
|
|
|
449
474
|
// src/copilot/prompt.ts
|
|
450
475
|
import {
|
|
451
476
|
COPILOT_ERROR_CODES as COPILOT_ERROR_CODES2,
|
|
452
|
-
createServicemeError as
|
|
477
|
+
createServicemeError as createServicemeError3,
|
|
478
|
+
ServicemeProtocolError
|
|
453
479
|
} from "@serviceme/devtools-protocol";
|
|
454
480
|
var COPILOT_COMMAND2 = "copilot";
|
|
455
481
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -484,7 +510,7 @@ async function copilotPrompt(options) {
|
|
|
484
510
|
});
|
|
485
511
|
const output = stripAnsi(result.stdout);
|
|
486
512
|
if (output.includes("I'm sorry, but I cannot assist") && result.stderr?.includes("Stream completed without a response")) {
|
|
487
|
-
throw
|
|
513
|
+
throw createServicemeError3(
|
|
488
514
|
COPILOT_ERROR_CODES2.AUTH_REQUIRED,
|
|
489
515
|
"Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.",
|
|
490
516
|
{ exitCode: 0, output, stderr: result.stderr }
|
|
@@ -492,9 +518,12 @@ async function copilotPrompt(options) {
|
|
|
492
518
|
}
|
|
493
519
|
return { output, exitCode: 0 };
|
|
494
520
|
} catch (error) {
|
|
521
|
+
if (error instanceof ServicemeProtocolError) {
|
|
522
|
+
throw error;
|
|
523
|
+
}
|
|
495
524
|
const err = error;
|
|
496
525
|
if (err.code === "ETIMEDOUT") {
|
|
497
|
-
throw
|
|
526
|
+
throw createServicemeError3(
|
|
498
527
|
COPILOT_ERROR_CODES2.TIMEOUT,
|
|
499
528
|
`Copilot prompt timed out after ${String(timeout)}ms.`
|
|
500
529
|
);
|
|
@@ -503,7 +532,7 @@ async function copilotPrompt(options) {
|
|
|
503
532
|
const output = stripAnsi(err.stdout ?? "");
|
|
504
533
|
const stderr = err.stderr ?? err.message ?? "";
|
|
505
534
|
if (output.includes("I'm sorry, but I cannot assist") || stderr.includes("Stream completed without a response")) {
|
|
506
|
-
throw
|
|
535
|
+
throw createServicemeError3(
|
|
507
536
|
COPILOT_ERROR_CODES2.AUTH_REQUIRED,
|
|
508
537
|
"Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.",
|
|
509
538
|
{ exitCode, output, stderr }
|
|
@@ -512,7 +541,7 @@ async function copilotPrompt(options) {
|
|
|
512
541
|
if (exitCode !== 0 && output) {
|
|
513
542
|
return { output, exitCode };
|
|
514
543
|
}
|
|
515
|
-
throw
|
|
544
|
+
throw createServicemeError3(
|
|
516
545
|
COPILOT_ERROR_CODES2.EXECUTION_FAILED,
|
|
517
546
|
stderr || `Copilot exited with code ${String(exitCode)}.`,
|
|
518
547
|
{ exitCode, stderr }
|
|
@@ -522,7 +551,7 @@ async function copilotPrompt(options) {
|
|
|
522
551
|
|
|
523
552
|
// src/env/environmentInspector.ts
|
|
524
553
|
import {
|
|
525
|
-
createServicemeError as
|
|
554
|
+
createServicemeError as createServicemeError4,
|
|
526
555
|
KNOWN_ENVIRONMENT_TOOLS
|
|
527
556
|
} from "@serviceme/devtools-protocol";
|
|
528
557
|
var DEFAULT_TOOL_CHECK_TIMEOUT_MS = 5e3;
|
|
@@ -540,17 +569,19 @@ var TOOL_CHECK_TIMEOUT_MS = {
|
|
|
540
569
|
var ERROR_CODE_NOT_FOUND = 127;
|
|
541
570
|
var ERROR_CODE_TIMEOUT = "ETIMEDOUT";
|
|
542
571
|
var EnvironmentInspector = class {
|
|
572
|
+
constructor(options = {}) {
|
|
573
|
+
this.runCommandFn = options.runCommand ?? runCommand;
|
|
574
|
+
this.platform = options.platform ?? process.platform;
|
|
575
|
+
}
|
|
543
576
|
async checkEnvironment() {
|
|
544
577
|
const results = await Promise.all(
|
|
545
|
-
KNOWN_ENVIRONMENT_TOOLS.map(
|
|
546
|
-
async (tool) => [tool, await this.checkTool(tool)]
|
|
547
|
-
)
|
|
578
|
+
KNOWN_ENVIRONMENT_TOOLS.map(async (tool) => [tool, await this.checkTool(tool)])
|
|
548
579
|
);
|
|
549
580
|
return Object.fromEntries(results);
|
|
550
581
|
}
|
|
551
582
|
async checkTool(toolName) {
|
|
552
583
|
if (!/^[a-zA-Z0-9-]+$/.test(toolName)) {
|
|
553
|
-
throw
|
|
584
|
+
throw createServicemeError4("invalid_params", "Invalid tool name.");
|
|
554
585
|
}
|
|
555
586
|
try {
|
|
556
587
|
if (toolName === "nvm") {
|
|
@@ -572,8 +603,8 @@ var EnvironmentInspector = class {
|
|
|
572
603
|
}
|
|
573
604
|
async getToolPath(toolName) {
|
|
574
605
|
try {
|
|
575
|
-
const isWindows =
|
|
576
|
-
const result = await
|
|
606
|
+
const isWindows = this.platform === "win32";
|
|
607
|
+
const result = await this.runCommandFn(isWindows ? "where" : "which", {
|
|
577
608
|
args: [toolName],
|
|
578
609
|
timeoutMs: this.getToolTimeout(toolName)
|
|
579
610
|
});
|
|
@@ -590,14 +621,12 @@ var EnvironmentInspector = class {
|
|
|
590
621
|
*/
|
|
591
622
|
async getToolShimPath(toolName) {
|
|
592
623
|
try {
|
|
593
|
-
const result = await
|
|
624
|
+
const result = await this.runCommandFn("where", {
|
|
594
625
|
args: [toolName],
|
|
595
626
|
timeoutMs: this.getToolTimeout(toolName)
|
|
596
627
|
});
|
|
597
628
|
const candidates = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
598
|
-
const cmdShim = candidates.find(
|
|
599
|
-
(line) => line.toLowerCase().endsWith(".cmd")
|
|
600
|
-
);
|
|
629
|
+
const cmdShim = candidates.find((line) => line.toLowerCase().endsWith(".cmd"));
|
|
601
630
|
return cmdShim ?? candidates[0];
|
|
602
631
|
} catch {
|
|
603
632
|
return void 0;
|
|
@@ -605,16 +634,16 @@ var EnvironmentInspector = class {
|
|
|
605
634
|
}
|
|
606
635
|
async getToolVersion(toolName) {
|
|
607
636
|
const invocation = await this.getVersionInvocation(toolName);
|
|
608
|
-
const result = await
|
|
637
|
+
const result = await this.runCommandFn(invocation.command, {
|
|
609
638
|
args: invocation.args,
|
|
610
639
|
timeoutMs: this.getToolTimeout(toolName)
|
|
611
640
|
});
|
|
612
641
|
return this.parseVersion(toolName, result.stdout || result.stderr);
|
|
613
642
|
}
|
|
614
643
|
async checkNvm() {
|
|
615
|
-
if (
|
|
644
|
+
if (this.platform === "win32") {
|
|
616
645
|
try {
|
|
617
|
-
const result = await
|
|
646
|
+
const result = await this.runCommandFn("cmd.exe", {
|
|
618
647
|
args: ["/c", "nvm version"],
|
|
619
648
|
timeoutMs: this.getToolTimeout("nvm")
|
|
620
649
|
});
|
|
@@ -631,7 +660,7 @@ var EnvironmentInspector = class {
|
|
|
631
660
|
}
|
|
632
661
|
}
|
|
633
662
|
try {
|
|
634
|
-
const result = await
|
|
663
|
+
const result = await this.runCommandFn("/bin/bash", {
|
|
635
664
|
args: [
|
|
636
665
|
"-lc",
|
|
637
666
|
'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; nvm --version'
|
|
@@ -659,7 +688,7 @@ var EnvironmentInspector = class {
|
|
|
659
688
|
};
|
|
660
689
|
}
|
|
661
690
|
try {
|
|
662
|
-
const result = await
|
|
691
|
+
const result = await this.runCommandFn("dotnet", {
|
|
663
692
|
args: ["nuget", "list", "source"],
|
|
664
693
|
timeoutMs: this.getToolTimeout("nuget")
|
|
665
694
|
});
|
|
@@ -680,7 +709,7 @@ var EnvironmentInspector = class {
|
|
|
680
709
|
}
|
|
681
710
|
}
|
|
682
711
|
async getVersionInvocation(toolName) {
|
|
683
|
-
const isWindows =
|
|
712
|
+
const isWindows = this.platform === "win32";
|
|
684
713
|
if (isWindows && ["npm", "pnpm", "nrm"].includes(toolName)) {
|
|
685
714
|
const shim = await this.getToolShimPath(toolName);
|
|
686
715
|
if (shim) {
|
|
@@ -734,7 +763,7 @@ var EnvironmentInspector = class {
|
|
|
734
763
|
const execError = error;
|
|
735
764
|
const message = execError.message || String(error);
|
|
736
765
|
const code = execError.code;
|
|
737
|
-
const isNotFound = message.includes("command not found") || message.includes("not recognized") || message.includes("ENOENT") || code === "ENOENT" || code === ERROR_CODE_NOT_FOUND;
|
|
766
|
+
const isNotFound = message.includes("command not found") || message.includes("not recognized") || message.includes("ENOENT") || message.includes("EACCES") || code === "ENOENT" || code === "EACCES" || code === ERROR_CODE_NOT_FOUND;
|
|
738
767
|
const isTimeout = code === ERROR_CODE_TIMEOUT || message.toLowerCase().includes("timed out");
|
|
739
768
|
return {
|
|
740
769
|
installed: false,
|
|
@@ -747,7 +776,7 @@ var EnvironmentInspector = class {
|
|
|
747
776
|
import * as fs2 from "fs/promises";
|
|
748
777
|
import * as path2 from "path";
|
|
749
778
|
import {
|
|
750
|
-
createServicemeError as
|
|
779
|
+
createServicemeError as createServicemeError5
|
|
751
780
|
} from "@serviceme/devtools-protocol";
|
|
752
781
|
var SUPPORTED_FORMATS = [
|
|
753
782
|
".jpg",
|
|
@@ -792,7 +821,7 @@ var ImageTools = class {
|
|
|
792
821
|
async compress(imagePath, options) {
|
|
793
822
|
const validation = await this.validate(imagePath, options.sharpModulePath);
|
|
794
823
|
if (!validation.valid) {
|
|
795
|
-
throw
|
|
824
|
+
throw createServicemeError5(
|
|
796
825
|
"invalid_params",
|
|
797
826
|
`Invalid image file: ${imagePath}`
|
|
798
827
|
);
|
|
@@ -866,7 +895,7 @@ var ImageTools = class {
|
|
|
866
895
|
try {
|
|
867
896
|
return sharpModulePath ? __require(sharpModulePath) : __require("sharp");
|
|
868
897
|
} catch (error) {
|
|
869
|
-
throw
|
|
898
|
+
throw createServicemeError5(
|
|
870
899
|
"internal_error",
|
|
871
900
|
`sharp is required for image operations: ${error instanceof Error ? error.message : String(error)}`
|
|
872
901
|
);
|
|
@@ -879,7 +908,7 @@ function createImageTools() {
|
|
|
879
908
|
|
|
880
909
|
// src/json/jsonTools.ts
|
|
881
910
|
import {
|
|
882
|
-
createServicemeError as
|
|
911
|
+
createServicemeError as createServicemeError6,
|
|
883
912
|
DEFAULT_JSON_SORT_OPTIONS
|
|
884
913
|
} from "@serviceme/devtools-protocol";
|
|
885
914
|
import { parse as parseCommentJson } from "comment-json";
|
|
@@ -923,7 +952,7 @@ function parseJson(text) {
|
|
|
923
952
|
} catch {
|
|
924
953
|
}
|
|
925
954
|
}
|
|
926
|
-
throw
|
|
955
|
+
throw createServicemeError6("json_invalid_input", "Invalid JSON format.");
|
|
927
956
|
}
|
|
928
957
|
function sortObject(obj, options) {
|
|
929
958
|
if (Array.isArray(obj)) {
|
|
@@ -1018,67 +1047,52 @@ function createConsoleLogger(prefix = "serviceme") {
|
|
|
1018
1047
|
import * as fs3 from "fs/promises";
|
|
1019
1048
|
import * as path3 from "path";
|
|
1020
1049
|
import {
|
|
1021
|
-
createServicemeError as
|
|
1050
|
+
createServicemeError as createServicemeError7
|
|
1022
1051
|
} from "@serviceme/devtools-protocol";
|
|
1023
1052
|
|
|
1024
1053
|
// src/utils/fileUtils.ts
|
|
1025
1054
|
import { constants, createWriteStream } from "fs";
|
|
1026
|
-
import {
|
|
1027
|
-
access as access2,
|
|
1028
|
-
copyFile,
|
|
1029
|
-
lstat,
|
|
1030
|
-
mkdir,
|
|
1031
|
-
readdir,
|
|
1032
|
-
rename,
|
|
1033
|
-
rm
|
|
1034
|
-
} from "fs/promises";
|
|
1055
|
+
import { access as access2, copyFile, lstat, mkdir, readdir, rename, rm } from "fs/promises";
|
|
1035
1056
|
import { dirname as dirname3, join as join3 } from "path";
|
|
1036
|
-
import
|
|
1057
|
+
import yauzl from "yauzl";
|
|
1037
1058
|
var unzipFile = (zipPath, dest) => {
|
|
1038
1059
|
return new Promise((resolve, reject) => {
|
|
1039
|
-
open(
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
(
|
|
1043
|
-
|
|
1044
|
-
if (
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
entry
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
resolve();
|
|
1076
|
-
});
|
|
1077
|
-
zipfile.on("error", (zipError) => {
|
|
1078
|
-
reject(zipError);
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
);
|
|
1060
|
+
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
1061
|
+
if (err) return reject(err);
|
|
1062
|
+
if (!zipfile) return reject(new Error("Failed to open zip file."));
|
|
1063
|
+
zipfile.readEntry();
|
|
1064
|
+
zipfile.on("entry", (entry) => {
|
|
1065
|
+
if (/\/$/.test(entry.fileName)) {
|
|
1066
|
+
void mkdir(join3(dest, entry.fileName), { recursive: true }).then(() => {
|
|
1067
|
+
zipfile.readEntry();
|
|
1068
|
+
}).catch(reject);
|
|
1069
|
+
} else {
|
|
1070
|
+
const outputPath = join3(dest, entry.fileName);
|
|
1071
|
+
void mkdir(dirname3(outputPath), { recursive: true }).then(() => {
|
|
1072
|
+
zipfile.openReadStream(
|
|
1073
|
+
entry,
|
|
1074
|
+
(streamError, readStream) => {
|
|
1075
|
+
if (streamError) return reject(streamError);
|
|
1076
|
+
if (!readStream) return reject(new Error("Failed to open zip entry stream."));
|
|
1077
|
+
const writeStream = createWriteStream(outputPath);
|
|
1078
|
+
readStream.on("error", reject);
|
|
1079
|
+
writeStream.on("error", reject);
|
|
1080
|
+
writeStream.on("close", () => {
|
|
1081
|
+
zipfile.readEntry();
|
|
1082
|
+
});
|
|
1083
|
+
readStream.pipe(writeStream);
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
}).catch(reject);
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
zipfile.on("end", () => {
|
|
1090
|
+
resolve();
|
|
1091
|
+
});
|
|
1092
|
+
zipfile.on("error", (zipError) => {
|
|
1093
|
+
reject(zipError);
|
|
1094
|
+
});
|
|
1095
|
+
});
|
|
1082
1096
|
});
|
|
1083
1097
|
};
|
|
1084
1098
|
var tryLstat = async (targetPath) => {
|
|
@@ -1102,11 +1116,7 @@ var mergeEntry = async (sourcePath, destPath, overwrite) => {
|
|
|
1102
1116
|
await mkdir(destPath, { recursive: true });
|
|
1103
1117
|
const children = await readdir(sourcePath);
|
|
1104
1118
|
for (const child of children) {
|
|
1105
|
-
await mergeEntry(
|
|
1106
|
-
join3(sourcePath, child),
|
|
1107
|
-
join3(destPath, child),
|
|
1108
|
-
overwrite
|
|
1109
|
-
);
|
|
1119
|
+
await mergeEntry(join3(sourcePath, child), join3(destPath, child), overwrite);
|
|
1110
1120
|
}
|
|
1111
1121
|
await rm(sourcePath, { recursive: true, force: true });
|
|
1112
1122
|
return;
|
|
@@ -1183,7 +1193,7 @@ var ProjectTools = class {
|
|
|
1183
1193
|
}
|
|
1184
1194
|
async installDependencies(workspacePath, command) {
|
|
1185
1195
|
if (!command) {
|
|
1186
|
-
throw
|
|
1196
|
+
throw createServicemeError7("invalid_params", "Expected install command.");
|
|
1187
1197
|
}
|
|
1188
1198
|
await runCommand(command, {
|
|
1189
1199
|
cwd: workspacePath,
|
|
@@ -1196,23 +1206,15 @@ var ProjectTools = class {
|
|
|
1196
1206
|
}
|
|
1197
1207
|
async makeScriptsExecutable(workspacePath) {
|
|
1198
1208
|
const isWindows = process.platform === "win32";
|
|
1199
|
-
const scripts = await this.findScripts(
|
|
1200
|
-
workspacePath,
|
|
1201
|
-
isWindows ? [".ps1", ".bat"] : [".sh"]
|
|
1202
|
-
);
|
|
1209
|
+
const scripts = await this.findScripts(workspacePath, isWindows ? [".ps1", ".bat"] : [".sh"]);
|
|
1203
1210
|
let updatedCount = 0;
|
|
1204
1211
|
if (isWindows) {
|
|
1205
|
-
for (const scriptPath of scripts.filter(
|
|
1206
|
-
(script) => script.endsWith(".ps1")
|
|
1207
|
-
)) {
|
|
1212
|
+
for (const scriptPath of scripts.filter((script) => script.endsWith(".ps1"))) {
|
|
1208
1213
|
try {
|
|
1209
|
-
await runCommand(
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
shell: true
|
|
1214
|
-
}
|
|
1215
|
-
);
|
|
1214
|
+
await runCommand(`powershell -Command "Unblock-File -Path '${scriptPath}'"`, {
|
|
1215
|
+
cwd: workspacePath,
|
|
1216
|
+
shell: true
|
|
1217
|
+
});
|
|
1216
1218
|
updatedCount += 1;
|
|
1217
1219
|
} catch {
|
|
1218
1220
|
}
|
|
@@ -1246,13 +1248,16 @@ var ProjectTools = class {
|
|
|
1246
1248
|
}
|
|
1247
1249
|
async runScaffoldPrune(workspacePath, preset) {
|
|
1248
1250
|
if (!preset) {
|
|
1249
|
-
throw
|
|
1251
|
+
throw createServicemeError7("invalid_params", "Expected scaffold preset.");
|
|
1250
1252
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
];
|
|
1253
|
+
await this.ensurePresetManifest(workspacePath, preset);
|
|
1254
|
+
const pruneCommand = `pnpm run scaffold:prune -- --preset ${preset} --project-root .`;
|
|
1255
|
+
const initMetadataCommand = `pnpm run scaffold:init-metadata -- --preset ${preset} --project-root . --force`;
|
|
1256
|
+
const commands = [pruneCommand, initMetadataCommand];
|
|
1255
1257
|
for (const command of commands) {
|
|
1258
|
+
if (command === initMetadataCommand) {
|
|
1259
|
+
await this.ensurePresetManifest(workspacePath, preset);
|
|
1260
|
+
}
|
|
1256
1261
|
await runCommand(command, {
|
|
1257
1262
|
cwd: workspacePath,
|
|
1258
1263
|
shell: true
|
|
@@ -1264,6 +1269,40 @@ var ProjectTools = class {
|
|
|
1264
1269
|
commands
|
|
1265
1270
|
};
|
|
1266
1271
|
}
|
|
1272
|
+
async ensurePresetManifest(workspacePath, preset) {
|
|
1273
|
+
const presetManifestPath = path3.join(
|
|
1274
|
+
workspacePath,
|
|
1275
|
+
".ms-scaffold",
|
|
1276
|
+
"presets",
|
|
1277
|
+
`${preset}.json`
|
|
1278
|
+
);
|
|
1279
|
+
if (await this.pathExists(presetManifestPath)) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
const projectModePath = path3.join(workspacePath, ".ms-scaffold", "project-mode.json");
|
|
1283
|
+
if (!await this.pathExists(projectModePath)) {
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
const projectModeRaw = await fs3.readFile(projectModePath, "utf8");
|
|
1287
|
+
const projectMode = JSON.parse(projectModeRaw);
|
|
1288
|
+
const synthesizedPreset = {
|
|
1289
|
+
preset,
|
|
1290
|
+
selectedModules: Array.isArray(projectMode.selectedModules) ? projectMode.selectedModules : [],
|
|
1291
|
+
prunedModules: Array.isArray(projectMode.prunedModules) ? projectMode.prunedModules : [],
|
|
1292
|
+
exclude: [],
|
|
1293
|
+
recommendedFollowUps: [],
|
|
1294
|
+
managedFiles: [],
|
|
1295
|
+
mergeManagedFiles: [],
|
|
1296
|
+
userOwnedPaths: []
|
|
1297
|
+
};
|
|
1298
|
+
await fs3.mkdir(path3.dirname(presetManifestPath), { recursive: true });
|
|
1299
|
+
await fs3.writeFile(
|
|
1300
|
+
presetManifestPath,
|
|
1301
|
+
`${JSON.stringify(synthesizedPreset, null, 2)}
|
|
1302
|
+
`,
|
|
1303
|
+
"utf8"
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1267
1306
|
async findScripts(dir, extensions) {
|
|
1268
1307
|
const results = [];
|
|
1269
1308
|
let entries;
|
|
@@ -1294,9 +1333,7 @@ var ProjectTools = class {
|
|
|
1294
1333
|
if (directoryNames.length === 1) {
|
|
1295
1334
|
return directoryNames[0] ?? null;
|
|
1296
1335
|
}
|
|
1297
|
-
const exactMatch = directoryNames.find(
|
|
1298
|
-
(directoryName) => directoryName === expectedDirName
|
|
1299
|
-
);
|
|
1336
|
+
const exactMatch = directoryNames.find((directoryName) => directoryName === expectedDirName);
|
|
1300
1337
|
if (exactMatch) {
|
|
1301
1338
|
return exactMatch;
|
|
1302
1339
|
}
|
|
@@ -1537,10 +1574,8 @@ var GithubCopilotCliExecutor = class {
|
|
|
1537
1574
|
windowsHide: true
|
|
1538
1575
|
});
|
|
1539
1576
|
if (child.pid) {
|
|
1540
|
-
writeDiagnostic(
|
|
1541
|
-
|
|
1542
|
-
`
|
|
1543
|
-
);
|
|
1577
|
+
writeDiagnostic(`[GithubCopilotCliExecutor] spawned child PID=${child.pid}
|
|
1578
|
+
`);
|
|
1544
1579
|
}
|
|
1545
1580
|
const timeoutMs = execution.timeoutMs;
|
|
1546
1581
|
const timer = timeoutMs != null ? setTimeout(() => {
|
|
@@ -1558,15 +1593,13 @@ var GithubCopilotCliExecutor = class {
|
|
|
1558
1593
|
child.stdout.on("data", (chunk) => {
|
|
1559
1594
|
const data = chunk.toString();
|
|
1560
1595
|
stdoutBuf += data;
|
|
1561
|
-
if (stdoutBuf.length > MAX_OUTPUT_BYTES)
|
|
1562
|
-
stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES);
|
|
1596
|
+
if (stdoutBuf.length > MAX_OUTPUT_BYTES) stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES);
|
|
1563
1597
|
onOutput("stdout", data);
|
|
1564
1598
|
});
|
|
1565
1599
|
child.stderr.on("data", (chunk) => {
|
|
1566
1600
|
const data = chunk.toString();
|
|
1567
1601
|
stderrBuf += data;
|
|
1568
|
-
if (stderrBuf.length > MAX_OUTPUT_BYTES)
|
|
1569
|
-
stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES);
|
|
1602
|
+
if (stderrBuf.length > MAX_OUTPUT_BYTES) stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES);
|
|
1570
1603
|
onOutput("stderr", data);
|
|
1571
1604
|
});
|
|
1572
1605
|
child.on("close", (code) => {
|
|
@@ -1884,7 +1917,7 @@ function getExecutor(taskType) {
|
|
|
1884
1917
|
import { randomUUID } from "crypto";
|
|
1885
1918
|
import * as fs8 from "fs";
|
|
1886
1919
|
import * as path7 from "path";
|
|
1887
|
-
import { createServicemeError as
|
|
1920
|
+
import { createServicemeError as createServicemeError8 } from "@serviceme/devtools-protocol";
|
|
1888
1921
|
var CONFIG_DIR3 = ".serviceme";
|
|
1889
1922
|
var CONFIG_FILE = "scheduled-tasks.json";
|
|
1890
1923
|
function emptyConfig() {
|
|
@@ -1895,10 +1928,7 @@ function isRecord(value) {
|
|
|
1895
1928
|
}
|
|
1896
1929
|
function requireNonEmptyString(payload, field, taskType) {
|
|
1897
1930
|
if (!isRecord(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
|
|
1898
|
-
throw
|
|
1899
|
-
"invalid_payload",
|
|
1900
|
-
`${taskType} payload ${field} is required`
|
|
1901
|
-
);
|
|
1931
|
+
throw createServicemeError8("invalid_payload", `${taskType} payload ${field} is required`);
|
|
1902
1932
|
}
|
|
1903
1933
|
}
|
|
1904
1934
|
function validateTaskPayload(taskType, payload) {
|
|
@@ -2061,9 +2091,7 @@ var TaskExecutionEngine = class {
|
|
|
2061
2091
|
if (policy === "reject") {
|
|
2062
2092
|
const existingIds = this.taskExecutions.get(snapshot.taskId);
|
|
2063
2093
|
if (existingIds && existingIds.size > 0) {
|
|
2064
|
-
throw new Error(
|
|
2065
|
-
`TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`
|
|
2066
|
-
);
|
|
2094
|
+
throw new Error(`TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`);
|
|
2067
2095
|
}
|
|
2068
2096
|
}
|
|
2069
2097
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2328,7 +2356,7 @@ var TaskLogManager = class {
|
|
|
2328
2356
|
var TICK_INTERVAL = 1e3;
|
|
2329
2357
|
var MIN_SCHEDULE_INTERVAL = 1e3;
|
|
2330
2358
|
var SchedulerDaemon = class {
|
|
2331
|
-
constructor(workspacePath) {
|
|
2359
|
+
constructor(workspacePath, options = {}) {
|
|
2332
2360
|
this.tickTimer = null;
|
|
2333
2361
|
this.watcher = null;
|
|
2334
2362
|
this.running = false;
|
|
@@ -2341,6 +2369,7 @@ var SchedulerDaemon = class {
|
|
|
2341
2369
|
this.logManager = new TaskLogManager(workspacePath);
|
|
2342
2370
|
this.pidManager = new PidManager(workspacePath);
|
|
2343
2371
|
this.logger = new DaemonLogger(workspacePath);
|
|
2372
|
+
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
2344
2373
|
}
|
|
2345
2374
|
start() {
|
|
2346
2375
|
if (this.running) return;
|
|
@@ -2446,7 +2475,7 @@ var SchedulerDaemon = class {
|
|
|
2446
2475
|
this.workspacePath
|
|
2447
2476
|
);
|
|
2448
2477
|
validateTaskPayload(task.taskType, executionPayload);
|
|
2449
|
-
const executor = getExecutor(task.taskType);
|
|
2478
|
+
const executor = this.getExecutor(task.taskType);
|
|
2450
2479
|
const result = await executor.execute(executionPayload);
|
|
2451
2480
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2452
2481
|
const durationMs = Date.now() - startMs;
|
|
@@ -2543,6 +2572,7 @@ function matchCronField(field, value) {
|
|
|
2543
2572
|
}
|
|
2544
2573
|
|
|
2545
2574
|
// src/skills/SkillCatalogClient.ts
|
|
2575
|
+
import { createServicemeError as createServicemeError9 } from "@serviceme/devtools-protocol";
|
|
2546
2576
|
var SkillCatalogClient = class {
|
|
2547
2577
|
constructor(options = {}) {
|
|
2548
2578
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -2550,10 +2580,10 @@ var SkillCatalogClient = class {
|
|
|
2550
2580
|
}
|
|
2551
2581
|
async getCatalog() {
|
|
2552
2582
|
if (!this.baseUrl) {
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2583
|
+
throw createServicemeError9(
|
|
2584
|
+
"workspace_not_found",
|
|
2585
|
+
"Skill catalog baseUrl is not configured."
|
|
2586
|
+
);
|
|
2557
2587
|
}
|
|
2558
2588
|
const response = await this.fetchImpl(
|
|
2559
2589
|
`${this.baseUrl}/api/v1/marketplace/skills`
|
|
@@ -2567,6 +2597,30 @@ var SkillCatalogClient = class {
|
|
|
2567
2597
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2568
2598
|
};
|
|
2569
2599
|
}
|
|
2600
|
+
async downloadSkill(remoteId) {
|
|
2601
|
+
if (!this.baseUrl) {
|
|
2602
|
+
throw createServicemeError9(
|
|
2603
|
+
"workspace_not_found",
|
|
2604
|
+
"Skill catalog baseUrl is not configured."
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2607
|
+
const response = await this.fetchImpl(
|
|
2608
|
+
`${this.baseUrl}/api/v1/marketplace/skills/download/${remoteId}`
|
|
2609
|
+
);
|
|
2610
|
+
if (!response.ok) {
|
|
2611
|
+
if (response.status === 404) {
|
|
2612
|
+
throw createServicemeError9(
|
|
2613
|
+
"not_found",
|
|
2614
|
+
`Skill '${remoteId}' not found`
|
|
2615
|
+
);
|
|
2616
|
+
}
|
|
2617
|
+
throw new Error(
|
|
2618
|
+
`Failed to download skill ${remoteId}: ${response.status}`
|
|
2619
|
+
);
|
|
2620
|
+
}
|
|
2621
|
+
const payload = await response.json();
|
|
2622
|
+
return payload.data?.files ?? [];
|
|
2623
|
+
}
|
|
2570
2624
|
};
|
|
2571
2625
|
|
|
2572
2626
|
// src/skills/SkillReconciler.ts
|
|
@@ -2703,6 +2757,22 @@ var SkillStore = class {
|
|
|
2703
2757
|
return false;
|
|
2704
2758
|
}
|
|
2705
2759
|
}
|
|
2760
|
+
async writeSkillFiles(skillId, scope, files) {
|
|
2761
|
+
const root = scope === "workspace" ? path9.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
|
|
2762
|
+
const targetDir = path9.join(root, skillId);
|
|
2763
|
+
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
2764
|
+
for (const file of files) {
|
|
2765
|
+
const filePath = path9.join(targetDir, file.path);
|
|
2766
|
+
await this.fileSystem.mkdir(path9.dirname(filePath), { recursive: true });
|
|
2767
|
+
await this.fileSystem.writeFile(filePath, file.content, "utf-8");
|
|
2768
|
+
if (file.executable) {
|
|
2769
|
+
try {
|
|
2770
|
+
await this.fileSystem.chmod(filePath, 493);
|
|
2771
|
+
} catch {
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2706
2776
|
};
|
|
2707
2777
|
export {
|
|
2708
2778
|
AgentCatalogClient,
|