@docyrus/docyrus 0.0.62 → 0.0.64

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/server-loader.js CHANGED
@@ -2218,8 +2218,8 @@ var require_lib = __commonJS({
2218
2218
  }
2219
2219
  };
2220
2220
  var walk2 = (opts, callback) => {
2221
- const p = new Promise((resolve4, reject) => {
2222
- new Walker(opts).on("done", resolve4).on("error", reject).start();
2221
+ const p = new Promise((resolve5, reject) => {
2222
+ new Walker(opts).on("done", resolve5).on("error", reject).start();
2223
2223
  });
2224
2224
  return callback ? p.then((res) => callback(null, res), callback) : p;
2225
2225
  };
@@ -5393,10 +5393,10 @@ function resolveAll(constructs2, events, context) {
5393
5393
  const called = [];
5394
5394
  let index2 = -1;
5395
5395
  while (++index2 < constructs2.length) {
5396
- const resolve4 = constructs2[index2].resolveAll;
5397
- if (resolve4 && !called.includes(resolve4)) {
5398
- events = resolve4(events, context);
5399
- called.push(resolve4);
5396
+ const resolve5 = constructs2[index2].resolveAll;
5397
+ if (resolve5 && !called.includes(resolve5)) {
5398
+ events = resolve5(events, context);
5399
+ called.push(resolve5);
5400
5400
  }
5401
5401
  }
5402
5402
  return events;
@@ -10705,9 +10705,9 @@ var init_lib9 = __esm({
10705
10705
  * @returns {undefined}
10706
10706
  * Nothing.
10707
10707
  */
10708
- set dirname(dirname11) {
10708
+ set dirname(dirname12) {
10709
10709
  assertPath(this.basename, "dirname");
10710
- this.path = import_node_path6.default.join(dirname11 || "", this.basename);
10710
+ this.path = import_node_path6.default.join(dirname12 || "", this.basename);
10711
10711
  }
10712
10712
  /**
10713
10713
  * Get the extname (including dot) (example: `'.js'`).
@@ -11354,7 +11354,7 @@ var init_lib10 = __esm({
11354
11354
  assertParser("process", this.parser || this.Parser);
11355
11355
  assertCompiler("process", this.compiler || this.Compiler);
11356
11356
  return done ? executor(void 0, done) : new Promise(executor);
11357
- function executor(resolve4, reject) {
11357
+ function executor(resolve5, reject) {
11358
11358
  const realFile = vfile(file2);
11359
11359
  const parseTree = (
11360
11360
  /** @type {HeadTree extends undefined ? Node : HeadTree} */
@@ -11385,8 +11385,8 @@ var init_lib10 = __esm({
11385
11385
  function realDone(error48, file3) {
11386
11386
  if (error48 || !file3) {
11387
11387
  reject(error48);
11388
- } else if (resolve4) {
11389
- resolve4(file3);
11388
+ } else if (resolve5) {
11389
+ resolve5(file3);
11390
11390
  } else {
11391
11391
  ok(done, "`done` is defined if `resolve` is not");
11392
11392
  done(void 0, file3);
@@ -11488,7 +11488,7 @@ var init_lib10 = __esm({
11488
11488
  file2 = void 0;
11489
11489
  }
11490
11490
  return done ? executor(void 0, done) : new Promise(executor);
11491
- function executor(resolve4, reject) {
11491
+ function executor(resolve5, reject) {
11492
11492
  ok(
11493
11493
  typeof file2 !== "function",
11494
11494
  "`file` can\u2019t be a `done` anymore, we checked"
@@ -11502,8 +11502,8 @@ var init_lib10 = __esm({
11502
11502
  );
11503
11503
  if (error48) {
11504
11504
  reject(error48);
11505
- } else if (resolve4) {
11506
- resolve4(resultingTree);
11505
+ } else if (resolve5) {
11506
+ resolve5(resultingTree);
11507
11507
  } else {
11508
11508
  ok(done, "`done` is defined if `resolve` is not");
11509
11509
  done(void 0, resultingTree, file3);
@@ -12590,8 +12590,8 @@ var require_dist = __commonJS({
12590
12590
  const header = report.header;
12591
12591
  return typeof header === "object" && !!header && "glibcVersionRuntime" in header;
12592
12592
  }
12593
- function load(dirname11) {
12594
- const m = path6.join(dirname11, "index.node");
12593
+ function load(dirname12) {
12594
+ const m = path6.join(dirname12, "index.node");
12595
12595
  return fs7.existsSync(m) ? require(m) : null;
12596
12596
  }
12597
12597
  exports2.load = load;
@@ -12621,12 +12621,12 @@ var require_filesystem = __commonJS({
12621
12621
  var fs7 = require("fs");
12622
12622
  var LDD_PATH = "/usr/bin/ldd";
12623
12623
  var readFileSync5 = (path6) => fs7.readFileSync(path6, "utf-8");
12624
- var readFile15 = (path6) => new Promise((resolve4, reject) => {
12624
+ var readFile15 = (path6) => new Promise((resolve5, reject) => {
12625
12625
  fs7.readFile(path6, "utf-8", (err2, data) => {
12626
12626
  if (err2) {
12627
12627
  reject(err2);
12628
12628
  } else {
12629
- resolve4(data);
12629
+ resolve5(data);
12630
12630
  }
12631
12631
  });
12632
12632
  });
@@ -12651,10 +12651,10 @@ var require_detect_libc = __commonJS({
12651
12651
  var commandOut = "";
12652
12652
  var safeCommand = () => {
12653
12653
  if (!commandOut) {
12654
- return new Promise((resolve4) => {
12654
+ return new Promise((resolve5) => {
12655
12655
  childProcess.exec(command, (err2, out2) => {
12656
12656
  commandOut = err2 ? " " : out2;
12657
- resolve4(commandOut);
12657
+ resolve5(commandOut);
12658
12658
  });
12659
12659
  });
12660
12660
  }
@@ -16571,7 +16571,7 @@ var require_websocket = __commonJS({
16571
16571
  var http = require("http");
16572
16572
  var net = require("net");
16573
16573
  var tls = require("tls");
16574
- var { randomBytes, createHash: createHash5 } = require("crypto");
16574
+ var { randomBytes, createHash: createHash6 } = require("crypto");
16575
16575
  var { Duplex, Readable: Readable2 } = require("stream");
16576
16576
  var { URL: URL2 } = require("url");
16577
16577
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -17231,7 +17231,7 @@ var require_websocket = __commonJS({
17231
17231
  abortHandshake(websocket, socket, "Invalid Upgrade header");
17232
17232
  return;
17233
17233
  }
17234
- const digest = createHash5("sha1").update(key + GUID).digest("base64");
17234
+ const digest = createHash6("sha1").update(key + GUID).digest("base64");
17235
17235
  if (res.headers["sec-websocket-accept"] !== digest) {
17236
17236
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
17237
17237
  return;
@@ -17598,7 +17598,7 @@ var require_websocket_server = __commonJS({
17598
17598
  var EventEmitter = require("events");
17599
17599
  var http = require("http");
17600
17600
  var { Duplex } = require("stream");
17601
- var { createHash: createHash5 } = require("crypto");
17601
+ var { createHash: createHash6 } = require("crypto");
17602
17602
  var extension3 = require_extension();
17603
17603
  var PerMessageDeflate2 = require_permessage_deflate();
17604
17604
  var subprotocol2 = require_subprotocol();
@@ -17899,7 +17899,7 @@ var require_websocket_server = __commonJS({
17899
17899
  );
17900
17900
  }
17901
17901
  if (this._state > RUNNING) return abortHandshake(socket, 503);
17902
- const digest = createHash5("sha1").update(key + GUID).digest("base64");
17902
+ const digest = createHash6("sha1").update(key + GUID).digest("base64");
17903
17903
  const headers = [
17904
17904
  "HTTP/1.1 101 Switching Protocols",
17905
17905
  "Upgrade: websocket",
@@ -18467,7 +18467,7 @@ var init_dist = __esm({
18467
18467
  });
18468
18468
  if (!chunk) {
18469
18469
  if (i2 === 1) {
18470
- await new Promise((resolve4) => setTimeout(resolve4));
18470
+ await new Promise((resolve5) => setTimeout(resolve5));
18471
18471
  maxReadCount = 3;
18472
18472
  continue;
18473
18473
  }
@@ -18617,7 +18617,7 @@ var init_dist = __esm({
18617
18617
  });
18618
18618
 
18619
18619
  // src/server/server-loader.ts
18620
- var import_node_fs13 = require("node:fs");
18620
+ var import_node_fs14 = require("node:fs");
18621
18621
  var import_node_url3 = require("node:url");
18622
18622
  var import_node_path23 = require("node:path");
18623
18623
  var import_picocolors2 = __toESM(require_picocolors());
@@ -18743,7 +18743,7 @@ function resolvePackagedExtensionPaths(resourceRoot, runtime = "terminal") {
18743
18743
 
18744
18744
  // src/server/agentServer.ts
18745
18745
  var import_node_child_process3 = require("node:child_process");
18746
- var import_node_crypto6 = require("node:crypto");
18746
+ var import_node_crypto7 = require("node:crypto");
18747
18747
  var import_promises18 = require("node:fs/promises");
18748
18748
  var import_node_path22 = require("node:path");
18749
18749
 
@@ -32612,7 +32612,8 @@ var EnvironmentConfigStateSchema = external_exports.object({
32612
32612
  version: external_exports.literal(1),
32613
32613
  activeEnvironmentId: external_exports.string().min(1),
32614
32614
  environments: external_exports.array(EnvironmentConfigItemSchema).min(1),
32615
- defaultClientId: external_exports.string().min(1).optional()
32615
+ defaultClientId: external_exports.string().min(1).optional(),
32616
+ activeSoulId: external_exports.string().min(1).optional()
32616
32617
  });
32617
32618
  var LegacyAuthSessionSchema = external_exports.object({
32618
32619
  apiBaseUrl: external_exports.string().min(1),
@@ -32982,6 +32983,76 @@ var AuthStore = class {
32982
32983
  // src/services/environmentConfig.ts
32983
32984
  var import_promises3 = require("node:fs/promises");
32984
32985
  var import_node_path5 = require("node:path");
32986
+
32987
+ // resources/pi-agent/souls/catalog.ts
32988
+ var DEFAULT_SOUL_ID = "default";
32989
+ var SOULS = [
32990
+ {
32991
+ id: "default",
32992
+ name: "Default",
32993
+ description: "Standard Docyrus operator voice. No style overlay.",
32994
+ promptFile: null
32995
+ },
32996
+ {
32997
+ id: "caveman",
32998
+ name: "Caveman",
32999
+ description: "Short grunts. Agent talk simple. Fire good.",
33000
+ promptFile: "caveman.md"
33001
+ },
33002
+ {
33003
+ id: "boomer-parent",
33004
+ name: "Boomer Parent",
33005
+ description: "Overly proud dad-jokes energy with 'back in my day' asides.",
33006
+ promptFile: "boomer-parent.md"
33007
+ },
33008
+ {
33009
+ id: "master-yoda",
33010
+ name: "Master Yoda",
33011
+ description: "Inverted syntax the agent speaks. Mystical brevity, hmm.",
33012
+ promptFile: "master-yoda.md"
33013
+ },
33014
+ {
33015
+ id: "bro",
33016
+ name: "Bro",
33017
+ description: "Pure gym-bro hype energy. 'Lit, fam, let's go.'",
33018
+ promptFile: "bro.md"
33019
+ },
33020
+ {
33021
+ id: "nonsense-engineer",
33022
+ name: "Nonsense Engineer",
33023
+ description: "Maximum jargon, minimum meaning. 'Leveraging synergistic abstractions.'",
33024
+ promptFile: "nonsense-engineer.md"
33025
+ },
33026
+ {
33027
+ id: "shakespeare",
33028
+ name: "Shakespeare",
33029
+ description: "Thee/thou, dramatic flair, iambic vibes.",
33030
+ promptFile: "shakespeare.md"
33031
+ },
33032
+ {
33033
+ id: "noir-detective",
33034
+ name: "Noir Detective",
33035
+ description: "Hard-boiled 1940s narrator. Every bug is a dame.",
33036
+ promptFile: "noir-detective.md"
33037
+ },
33038
+ {
33039
+ id: "pirate",
33040
+ name: "Pirate",
33041
+ description: "Aye matey, every task a seafaring metaphor, arrr.",
33042
+ promptFile: "pirate.md"
33043
+ },
33044
+ {
33045
+ id: "linkedin-influencer",
33046
+ name: "LinkedIn Influencer",
33047
+ description: "Every reply is a mini-post. Single-sentence paragraphs. Humble-brags. 'Agree? \u{1F447}'",
33048
+ promptFile: "linkedin-influencer.md"
33049
+ }
33050
+ ];
33051
+ function findSoul(id) {
33052
+ return SOULS.find((soul) => soul.id === id);
33053
+ }
33054
+
33055
+ // src/services/environmentConfig.ts
32985
33056
  var ENVIRONMENT_ID_ALIASES = {
32986
33057
  "local-development": "dev",
32987
33058
  prod: "live"
@@ -33005,9 +33076,20 @@ function createDefaultState2() {
33005
33076
  name: environment.name,
33006
33077
  apiBaseUrl: environment.apiBaseUrl
33007
33078
  })),
33008
- defaultClientId: void 0
33079
+ defaultClientId: void 0,
33080
+ activeSoulId: DEFAULT_SOUL_ID
33009
33081
  };
33010
33082
  }
33083
+ function normalizeSoulId(value2) {
33084
+ if (!value2) {
33085
+ return DEFAULT_SOUL_ID;
33086
+ }
33087
+ const trimmed = value2.trim();
33088
+ if (!trimmed) {
33089
+ return DEFAULT_SOUL_ID;
33090
+ }
33091
+ return findSoul(trimmed) ? trimmed : DEFAULT_SOUL_ID;
33092
+ }
33011
33093
  function normalizeState3(state) {
33012
33094
  const defaultsById = new Map(
33013
33095
  DEFAULT_ENVIRONMENTS.map((environment) => [
@@ -33058,7 +33140,8 @@ function normalizeState3(state) {
33058
33140
  version: 1,
33059
33141
  activeEnvironmentId: hasActiveEnvironment ? canonicalActiveEnvironmentId : fallbackActiveEnvironment,
33060
33142
  environments,
33061
- defaultClientId: state.defaultClientId?.trim() || void 0
33143
+ defaultClientId: state.defaultClientId?.trim() || void 0,
33144
+ activeSoulId: normalizeSoulId(state.activeSoulId)
33062
33145
  };
33063
33146
  }
33064
33147
  var EnvironmentConfigService = class {
@@ -33163,6 +33246,25 @@ var EnvironmentConfigService = class {
33163
33246
  defaultClientId: trimmedClientId
33164
33247
  });
33165
33248
  }
33249
+ async getActiveSoulId() {
33250
+ const state = await this.#readStateWithDefaults();
33251
+ return normalizeSoulId(state.activeSoulId);
33252
+ }
33253
+ async setActiveSoulId(soulId) {
33254
+ const trimmed = soulId.trim();
33255
+ if (!trimmed) {
33256
+ throw new UserInputError("Soul ID cannot be empty.");
33257
+ }
33258
+ if (!findSoul(trimmed)) {
33259
+ throw new UserInputError(`Unknown soul '${soulId}'. Use /souls inside the pi agent or GET /api/souls to view options.`);
33260
+ }
33261
+ const state = await this.#readStateWithDefaults();
33262
+ await this.writeState({
33263
+ ...state,
33264
+ activeSoulId: trimmed
33265
+ });
33266
+ return trimmed;
33267
+ }
33166
33268
  async #readStateWithDefaults() {
33167
33269
  const state = await this.readState();
33168
33270
  if (state.environments.length > 0) {
@@ -33177,6 +33279,14 @@ var EnvironmentConfigService = class {
33177
33279
  }
33178
33280
  };
33179
33281
 
33282
+ // src/services/soulRegistry.ts
33283
+ function listSouls(activeSoulId) {
33284
+ return SOULS.map((soul) => ({
33285
+ ...soul,
33286
+ isActive: soul.id === activeSoulId
33287
+ }));
33288
+ }
33289
+
33180
33290
  // ../../node_modules/.pnpm/hono@4.12.14/node_modules/hono/dist/compose.js
33181
33291
  var compose = (middleware, onError, onNotFound) => {
33182
33292
  return (context, next) => {
@@ -35695,23 +35805,23 @@ var EXCLUDE_DIRS = ["docyrus/knowledge", ".claude"];
35695
35805
  var EXCLUDE_GLOBS = ["*.md"];
35696
35806
  var DOCYRUS_REF_RE = /(?:\/\/|#)\s*@docyrus:\s*\[\[([^\]]+)\]\]/gu;
35697
35807
  function tryExec(command, args2, cwd) {
35698
- return new Promise((resolve4) => {
35808
+ return new Promise((resolve5) => {
35699
35809
  (0, import_node_child_process.execFile)(command, args2, { cwd, maxBuffer: 50 * 1024 * 1024 }, (error48, stdout) => {
35700
35810
  if (error48) {
35701
35811
  const exitCode = error48.code;
35702
35812
  const status = error48.status;
35703
35813
  if (exitCode === "ENOENT") {
35704
- resolve4(null);
35814
+ resolve5(null);
35705
35815
  return;
35706
35816
  }
35707
35817
  if (status === 1 && stdout === "") {
35708
- resolve4("");
35818
+ resolve5("");
35709
35819
  return;
35710
35820
  }
35711
- resolve4(null);
35821
+ resolve5(null);
35712
35822
  return;
35713
35823
  }
35714
- resolve4(stdout);
35824
+ resolve5(stdout);
35715
35825
  });
35716
35826
  });
35717
35827
  }
@@ -37917,8 +38027,8 @@ var Module2 = (() => {
37917
38027
  var moduleRtn;
37918
38028
  var Module = moduleArg;
37919
38029
  var readyPromiseResolve, readyPromiseReject;
37920
- var readyPromise = new Promise((resolve4, reject) => {
37921
- readyPromiseResolve = resolve4;
38030
+ var readyPromise = new Promise((resolve5, reject) => {
38031
+ readyPromiseResolve = resolve5;
37922
38032
  readyPromiseReject = reject;
37923
38033
  });
37924
38034
  var ENVIRONMENT_IS_WEB = typeof window == "object";
@@ -38001,13 +38111,13 @@ var Module2 = (() => {
38001
38111
  }
38002
38112
  readAsync = /* @__PURE__ */ __name(async (url2) => {
38003
38113
  if (isFileURI(url2)) {
38004
- return new Promise((resolve4, reject) => {
38114
+ return new Promise((resolve5, reject) => {
38005
38115
  var xhr = new XMLHttpRequest();
38006
38116
  xhr.open("GET", url2, true);
38007
38117
  xhr.responseType = "arraybuffer";
38008
38118
  xhr.onload = () => {
38009
38119
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
38010
- resolve4(xhr.response);
38120
+ resolve5(xhr.response);
38011
38121
  return;
38012
38122
  }
38013
38123
  reject(xhr.status);
@@ -38231,10 +38341,10 @@ var Module2 = (() => {
38231
38341
  __name(receiveInstantiationResult, "receiveInstantiationResult");
38232
38342
  var info2 = getWasmImports();
38233
38343
  if (Module["instantiateWasm"]) {
38234
- return new Promise((resolve4, reject) => {
38344
+ return new Promise((resolve5, reject) => {
38235
38345
  Module["instantiateWasm"](info2, (mod, inst) => {
38236
38346
  receiveInstance(mod, inst);
38237
- resolve4(mod.exports);
38347
+ resolve5(mod.exports);
38238
38348
  });
38239
38349
  });
38240
38350
  }
@@ -43379,8 +43489,8 @@ var OAuthFlowManager = class {
43379
43489
  if (state.currentStep) {
43380
43490
  return Promise.resolve(state.currentStep);
43381
43491
  }
43382
- return new Promise((resolve4) => {
43383
- state.waitingForStep = { resolve: resolve4 };
43492
+ return new Promise((resolve5) => {
43493
+ state.waitingForStep = { resolve: resolve5 };
43384
43494
  });
43385
43495
  }
43386
43496
  async start(params) {
@@ -43401,8 +43511,8 @@ var OAuthFlowManager = class {
43401
43511
  onPrompt: async (prompt) => {
43402
43512
  const step = buildPromptStep({ state, type: "prompt", prompt });
43403
43513
  this.emitStep(state, step);
43404
- return await new Promise((resolve4) => {
43405
- state.pendingInput = { type: "prompt", resolve: resolve4 };
43514
+ return await new Promise((resolve5) => {
43515
+ state.pendingInput = { type: "prompt", resolve: resolve5 };
43406
43516
  });
43407
43517
  },
43408
43518
  onManualCodeInput: async () => {
@@ -43414,8 +43524,8 @@ var OAuthFlowManager = class {
43414
43524
  }
43415
43525
  });
43416
43526
  this.emitStep(state, step);
43417
- return await new Promise((resolve4) => {
43418
- state.pendingInput = { type: "manual-code", resolve: resolve4 };
43527
+ return await new Promise((resolve5) => {
43528
+ state.pendingInput = { type: "manual-code", resolve: resolve5 };
43419
43529
  });
43420
43530
  },
43421
43531
  onProgress: (_message) => {
@@ -43459,6 +43569,7 @@ var OAuthFlowManager = class {
43459
43569
 
43460
43570
  // src/server/mcpConfigService.ts
43461
43571
  var import_node_crypto5 = require("node:crypto");
43572
+ var import_node_fs12 = require("node:fs");
43462
43573
  var import_promises16 = require("node:fs/promises");
43463
43574
  var import_node_os2 = require("node:os");
43464
43575
  var import_node_path20 = require("node:path");
@@ -43784,6 +43895,95 @@ async function removeMcpServer(agentDir, name2) {
43784
43895
  raw2[key] = servers;
43785
43896
  await writeUserConfig(agentDir, raw2);
43786
43897
  }
43898
+ var SUPPORTED_IMPORT_KINDS = [
43899
+ "cursor",
43900
+ "claude-code",
43901
+ "claude-desktop",
43902
+ "codex",
43903
+ "windsurf",
43904
+ "vscode"
43905
+ ];
43906
+ function validateMcpSettings(value2) {
43907
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) {
43908
+ return { ok: false, error: "settings must be an object" };
43909
+ }
43910
+ const obj = value2;
43911
+ const settings = {};
43912
+ if (obj.toolPrefix !== void 0) {
43913
+ if (!["server", "none", "short"].includes(obj.toolPrefix)) {
43914
+ return { ok: false, error: "settings.toolPrefix must be 'server', 'none', or 'short'" };
43915
+ }
43916
+ settings.toolPrefix = obj.toolPrefix;
43917
+ }
43918
+ if (obj.idleTimeout !== void 0) {
43919
+ if (typeof obj.idleTimeout !== "number" || !Number.isFinite(obj.idleTimeout) || obj.idleTimeout < 0) {
43920
+ return { ok: false, error: "settings.idleTimeout must be a non-negative number (minutes)" };
43921
+ }
43922
+ settings.idleTimeout = obj.idleTimeout;
43923
+ }
43924
+ if (obj.directTools !== void 0) {
43925
+ if (typeof obj.directTools !== "boolean") {
43926
+ return { ok: false, error: "settings.directTools must be a boolean" };
43927
+ }
43928
+ settings.directTools = obj.directTools;
43929
+ }
43930
+ return { ok: true, value: settings };
43931
+ }
43932
+ function validateImportKinds(value2) {
43933
+ if (!Array.isArray(value2)) {
43934
+ return { ok: false, error: "imports must be an array of import kinds" };
43935
+ }
43936
+ const kinds = [];
43937
+ for (const item of value2) {
43938
+ if (typeof item !== "string" || !SUPPORTED_IMPORT_KINDS.includes(item)) {
43939
+ return {
43940
+ ok: false,
43941
+ error: `Unsupported import kind: ${String(item)}. Allowed: ${SUPPORTED_IMPORT_KINDS.join(", ")}`
43942
+ };
43943
+ }
43944
+ if (!kinds.includes(item)) {
43945
+ kinds.push(item);
43946
+ }
43947
+ }
43948
+ return { ok: true, value: kinds };
43949
+ }
43950
+ function getImportPath(kind, cwd) {
43951
+ const raw2 = IMPORT_PATHS[kind];
43952
+ return raw2.startsWith(".") ? (0, import_node_path20.resolve)(cwd, raw2) : raw2;
43953
+ }
43954
+ async function updateMcpSettings(agentDir, patch) {
43955
+ const { raw: raw2 } = await readUserConfig(agentDir);
43956
+ const current = raw2.settings && typeof raw2.settings === "object" && !Array.isArray(raw2.settings) ? raw2.settings : {};
43957
+ const merged = { ...current, ...patch };
43958
+ raw2.settings = merged;
43959
+ await writeUserConfig(agentDir, raw2);
43960
+ return merged;
43961
+ }
43962
+ async function updateMcpImports(agentDir, imports) {
43963
+ const { raw: raw2 } = await readUserConfig(agentDir);
43964
+ raw2.imports = imports;
43965
+ await writeUserConfig(agentDir, raw2);
43966
+ return imports;
43967
+ }
43968
+ async function describeImports(agentDir, cwd) {
43969
+ const { config: config2 } = await readUserConfig(agentDir);
43970
+ const enabled = config2.imports ?? [];
43971
+ const available = await Promise.all(
43972
+ SUPPORTED_IMPORT_KINDS.map(async (kind) => {
43973
+ const path6 = getImportPath(kind, cwd);
43974
+ const imported = (0, import_node_fs12.existsSync)(path6) ? await readJsonFile2(path6) : null;
43975
+ const servers = imported ? extractServers(imported, kind) : {};
43976
+ return {
43977
+ kind,
43978
+ path: path6,
43979
+ exists: imported !== null,
43980
+ enabled: enabled.includes(kind),
43981
+ serverCount: Object.keys(servers).length
43982
+ };
43983
+ })
43984
+ );
43985
+ return { enabled, available };
43986
+ }
43787
43987
  function getServerPrefix(serverName, mode) {
43788
43988
  if (mode === "none") {
43789
43989
  return "";
@@ -43803,8 +44003,18 @@ function formatToolName(toolName, serverName, prefix) {
43803
44003
  }
43804
44004
 
43805
44005
  // src/server/skillsService.ts
44006
+ var import_node_crypto6 = require("node:crypto");
43806
44007
  var import_promises17 = require("node:fs/promises");
43807
44008
  var import_node_path21 = require("node:path");
44009
+ var SKILL_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
44010
+ var SkillError = class extends Error {
44011
+ code;
44012
+ constructor(code, message) {
44013
+ super(message);
44014
+ this.name = "SkillError";
44015
+ this.code = code;
44016
+ }
44017
+ };
43808
44018
  function parseFrontmatter2(content3) {
43809
44019
  const frontmatter2 = {};
43810
44020
  if (!content3.startsWith("---")) {
@@ -43910,6 +44120,229 @@ async function getSkillDetail(agentDir, skillName) {
43910
44120
  }
43911
44121
  return null;
43912
44122
  }
44123
+ function getSkillsLockPath(agentDir) {
44124
+ return (0, import_node_path21.join)(agentDir, "skills-lock.json");
44125
+ }
44126
+ function getSkillsDir(agentDir) {
44127
+ return (0, import_node_path21.join)(agentDir, "skills");
44128
+ }
44129
+ function createEmptyLockFile() {
44130
+ return { version: 1, skills: {} };
44131
+ }
44132
+ async function readSkillsLockFile(agentDir) {
44133
+ let content3;
44134
+ try {
44135
+ content3 = await (0, import_promises17.readFile)(getSkillsLockPath(agentDir), "utf-8");
44136
+ } catch (error48) {
44137
+ if (isErrnoException(error48) && error48.code === "ENOENT") {
44138
+ return createEmptyLockFile();
44139
+ }
44140
+ throw error48;
44141
+ }
44142
+ let parsed;
44143
+ try {
44144
+ parsed = JSON.parse(content3);
44145
+ } catch {
44146
+ return createEmptyLockFile();
44147
+ }
44148
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
44149
+ return createEmptyLockFile();
44150
+ }
44151
+ const obj = parsed;
44152
+ const skills = obj.skills;
44153
+ if (!skills || typeof skills !== "object" || Array.isArray(skills)) {
44154
+ return createEmptyLockFile();
44155
+ }
44156
+ return {
44157
+ version: 1,
44158
+ skills
44159
+ };
44160
+ }
44161
+ function isErrnoException(error48) {
44162
+ return error48 instanceof Error && "code" in error48;
44163
+ }
44164
+ async function writeSkillsLockFile(agentDir, lock) {
44165
+ const lockPath = getSkillsLockPath(agentDir);
44166
+ await (0, import_promises17.mkdir)((0, import_node_path21.dirname)(lockPath), { recursive: true });
44167
+ const tmpPath = `${lockPath}.${process.pid}.tmp`;
44168
+ await (0, import_promises17.writeFile)(tmpPath, JSON.stringify(lock, null, 2) + "\n", "utf-8");
44169
+ await (0, import_promises17.rename)(tmpPath, lockPath);
44170
+ }
44171
+ function validateSkillName(name2) {
44172
+ if (!name2) {
44173
+ return { ok: false, error: "Skill name is required" };
44174
+ }
44175
+ if (!SKILL_NAME_REGEX.test(name2)) {
44176
+ return {
44177
+ ok: false,
44178
+ error: "Skill name must start with alphanumeric and contain only alphanumeric, hyphens, and underscores"
44179
+ };
44180
+ }
44181
+ return { ok: true };
44182
+ }
44183
+ function validateInstallRequest(request) {
44184
+ if (!request.source || typeof request.source !== "string") {
44185
+ return { ok: false, code: "INVALID_REQUEST", error: "Missing required field: source" };
44186
+ }
44187
+ if (!request.sourceType || !["local"].includes(request.sourceType)) {
44188
+ return { ok: false, code: "INVALID_REQUEST", error: "sourceType must be 'local'" };
44189
+ }
44190
+ if (request.name !== void 0) {
44191
+ const nameCheck = validateSkillName(request.name);
44192
+ if (!nameCheck.ok) {
44193
+ return { ok: false, code: "INVALID_NAME", error: nameCheck.error };
44194
+ }
44195
+ }
44196
+ return { ok: true };
44197
+ }
44198
+ async function collectFiles2(dir, baseDir) {
44199
+ const files = [];
44200
+ const entries = await (0, import_promises17.readdir)(dir, { withFileTypes: true });
44201
+ for (const entry of entries) {
44202
+ const full = (0, import_node_path21.join)(dir, entry.name);
44203
+ if (entry.isDirectory()) {
44204
+ files.push(...await collectFiles2(full, baseDir));
44205
+ } else if (entry.isFile()) {
44206
+ files.push(full);
44207
+ }
44208
+ }
44209
+ return files;
44210
+ }
44211
+ async function computeSkillHash(skillDir) {
44212
+ const files = (await collectFiles2(skillDir, skillDir)).sort();
44213
+ const hash2 = (0, import_node_crypto6.createHash)("sha256");
44214
+ for (const file2 of files) {
44215
+ const rel = file2.slice(skillDir.length + 1).replace(/\\/g, "/");
44216
+ const content3 = await (0, import_promises17.readFile)(file2);
44217
+ hash2.update(rel);
44218
+ hash2.update("\0");
44219
+ hash2.update(content3);
44220
+ hash2.update("\0");
44221
+ }
44222
+ return hash2.digest("hex");
44223
+ }
44224
+ async function readSkillFrontmatterName(skillDir) {
44225
+ try {
44226
+ const content3 = await (0, import_promises17.readFile)((0, import_node_path21.join)(skillDir, "SKILL.md"), "utf-8");
44227
+ const parsed = parseFrontmatter2(content3);
44228
+ return parsed.frontmatter.name || null;
44229
+ } catch {
44230
+ return null;
44231
+ }
44232
+ }
44233
+ async function verifySkillDirectory(skillDir) {
44234
+ const skillMd = (0, import_node_path21.join)(skillDir, "SKILL.md");
44235
+ try {
44236
+ const s = await (0, import_promises17.stat)(skillMd);
44237
+ if (!s.isFile()) {
44238
+ throw new SkillError("INVALID_SOURCE", `SKILL.md at ${skillMd} is not a regular file`);
44239
+ }
44240
+ } catch (error48) {
44241
+ if (error48 instanceof SkillError) {
44242
+ throw error48;
44243
+ }
44244
+ const message = error48 instanceof Error ? error48.message : String(error48);
44245
+ throw new SkillError("INVALID_SOURCE", `Invalid skill directory: ${message}`);
44246
+ }
44247
+ }
44248
+ async function copyLocalSkill(params) {
44249
+ const absoluteSource = (0, import_node_path21.isAbsolute)(params.source) ? params.source : (0, import_node_path21.resolve)(params.cwd, params.source);
44250
+ const s = await (0, import_promises17.stat)(absoluteSource).catch(() => null);
44251
+ if (!s || !s.isDirectory()) {
44252
+ throw new SkillError("INVALID_SOURCE", `Local skill source must be an existing directory: ${absoluteSource}`);
44253
+ }
44254
+ await verifySkillDirectory(absoluteSource);
44255
+ await (0, import_promises17.rm)(params.destination, { recursive: true, force: true });
44256
+ await (0, import_promises17.cp)(absoluteSource, params.destination, { recursive: true });
44257
+ }
44258
+ function deriveSkillName(request) {
44259
+ if (request.name) {
44260
+ return request.name;
44261
+ }
44262
+ const normalized = request.source.replace(/[\\/]+$/, "");
44263
+ const base = normalized.split(/[\\/]/).filter(Boolean).pop() ?? "";
44264
+ return base;
44265
+ }
44266
+ async function installSkill(params) {
44267
+ const validation = validateInstallRequest(params.request);
44268
+ if (!validation.ok) {
44269
+ throw new SkillError(validation.code, validation.error);
44270
+ }
44271
+ const skillName = params.request.name ?? deriveSkillName(params.request);
44272
+ if (!params.request.name) {
44273
+ const nameCheck = validateSkillName(skillName);
44274
+ if (!nameCheck.ok) {
44275
+ throw new SkillError(
44276
+ "INVALID_NAME",
44277
+ `Unable to derive a valid skill name from "${params.request.source}". Provide an explicit "name".`
44278
+ );
44279
+ }
44280
+ }
44281
+ const skillsDir = getSkillsDir(params.agentDir);
44282
+ const destination = (0, import_node_path21.join)(skillsDir, skillName);
44283
+ const existed = await dirExists(destination);
44284
+ if (existed && !params.request.overwrite) {
44285
+ throw new SkillError(
44286
+ "ALREADY_EXISTS",
44287
+ `Skill "${skillName}" already exists. Set overwrite=true to replace it.`
44288
+ );
44289
+ }
44290
+ await (0, import_promises17.mkdir)(skillsDir, { recursive: true });
44291
+ switch (params.request.sourceType) {
44292
+ case "local":
44293
+ await copyLocalSkill({
44294
+ source: params.request.source,
44295
+ cwd: params.cwd,
44296
+ destination
44297
+ });
44298
+ break;
44299
+ }
44300
+ const fmName = await readSkillFrontmatterName(destination);
44301
+ const computedHash = await computeSkillHash(destination);
44302
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
44303
+ const lock = await readSkillsLockFile(params.agentDir);
44304
+ lock.skills[skillName] = {
44305
+ source: params.request.source,
44306
+ sourceType: params.request.sourceType,
44307
+ computedHash,
44308
+ installedAt
44309
+ };
44310
+ await writeSkillsLockFile(params.agentDir, lock);
44311
+ return {
44312
+ name: fmName ?? skillName,
44313
+ directory: skillName,
44314
+ source: params.request.source,
44315
+ sourceType: params.request.sourceType,
44316
+ computedHash,
44317
+ installedAt,
44318
+ overwritten: existed
44319
+ };
44320
+ }
44321
+ async function uninstallSkill(params) {
44322
+ const nameCheck = validateSkillName(params.name);
44323
+ if (!nameCheck.ok) {
44324
+ throw new SkillError("INVALID_NAME", nameCheck.error);
44325
+ }
44326
+ const skillsDir = getSkillsDir(params.agentDir);
44327
+ let targetDir = params.name;
44328
+ const existed = await dirExists((0, import_node_path21.join)(skillsDir, targetDir));
44329
+ if (!existed) {
44330
+ const detail = await getSkillDetail(params.agentDir, params.name);
44331
+ if (!detail) {
44332
+ throw new SkillError("NOT_FOUND", `Skill "${params.name}" not found`);
44333
+ }
44334
+ targetDir = detail.skill.directory;
44335
+ }
44336
+ const destination = (0, import_node_path21.join)(skillsDir, targetDir);
44337
+ const lock = await readSkillsLockFile(params.agentDir);
44338
+ const lockKey = lock.skills[targetDir] ? targetDir : lock.skills[params.name] ? params.name : null;
44339
+ await (0, import_promises17.rm)(destination, { recursive: true, force: true });
44340
+ if (lockKey) {
44341
+ delete lock.skills[lockKey];
44342
+ await writeSkillsLockFile(params.agentDir, lock);
44343
+ }
44344
+ return { name: params.name, directory: targetDir, removed: true };
44345
+ }
43913
44346
 
43914
44347
  // src/server/toolsService.ts
43915
44348
  var BUILT_IN_TOOLS = {
@@ -44180,7 +44613,7 @@ var BROWSER_TOOL_SCHEMAS = [
44180
44613
  ];
44181
44614
 
44182
44615
  // src/server/sessionConfig.ts
44183
- var import_node_fs12 = require("node:fs");
44616
+ var import_node_fs13 = require("node:fs");
44184
44617
  var path5 = __toESM(require("node:path"));
44185
44618
  var DEFAULT_CONFIG = { autoCommit: false };
44186
44619
  function configPath(agentDir, sessionId) {
@@ -44188,7 +44621,7 @@ function configPath(agentDir, sessionId) {
44188
44621
  }
44189
44622
  async function readSessionConfig(agentDir, sessionId) {
44190
44623
  try {
44191
- const raw2 = await import_node_fs12.promises.readFile(configPath(agentDir, sessionId), "utf-8");
44624
+ const raw2 = await import_node_fs13.promises.readFile(configPath(agentDir, sessionId), "utf-8");
44192
44625
  return { ...DEFAULT_CONFIG, ...JSON.parse(raw2) };
44193
44626
  } catch {
44194
44627
  return { ...DEFAULT_CONFIG };
@@ -44196,14 +44629,28 @@ async function readSessionConfig(agentDir, sessionId) {
44196
44629
  }
44197
44630
  async function writeSessionConfig(agentDir, sessionId, patch) {
44198
44631
  const dir = path5.join(agentDir, "session-config");
44199
- await import_node_fs12.promises.mkdir(dir, { recursive: true });
44632
+ await import_node_fs13.promises.mkdir(dir, { recursive: true });
44200
44633
  const current = await readSessionConfig(agentDir, sessionId);
44201
44634
  const next = { ...current, ...patch };
44202
- await import_node_fs12.promises.writeFile(configPath(agentDir, sessionId), JSON.stringify(next, null, 2), "utf-8");
44635
+ await import_node_fs13.promises.writeFile(configPath(agentDir, sessionId), JSON.stringify(next, null, 2), "utf-8");
44203
44636
  return next;
44204
44637
  }
44205
44638
 
44206
44639
  // src/server/agentServer.ts
44640
+ var SKILL_ERROR_STATUS = {
44641
+ INVALID_REQUEST: 400,
44642
+ INVALID_NAME: 400,
44643
+ INVALID_SOURCE: 404,
44644
+ ALREADY_EXISTS: 409,
44645
+ NOT_FOUND: 404
44646
+ };
44647
+ function skillErrorResponse(c, error48) {
44648
+ if (error48 instanceof SkillError) {
44649
+ return c.json({ error: error48.message, code: error48.code }, SKILL_ERROR_STATUS[error48.code]);
44650
+ }
44651
+ const message = error48 instanceof Error ? error48.message : String(error48);
44652
+ return c.json({ error: message }, 500);
44653
+ }
44207
44654
  function generateMessageId() {
44208
44655
  return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
44209
44656
  }
@@ -44271,7 +44718,7 @@ async function waitForIdle(session, timeoutMs = 3e4) {
44271
44718
  if (!session.isStreaming) {
44272
44719
  return;
44273
44720
  }
44274
- return new Promise((resolve4, reject) => {
44721
+ return new Promise((resolve5, reject) => {
44275
44722
  const timer = setTimeout(() => {
44276
44723
  unsubscribe();
44277
44724
  reject(new Error("Timed out waiting for session to become idle"));
@@ -44280,13 +44727,13 @@ async function waitForIdle(session, timeoutMs = 3e4) {
44280
44727
  if (event.type === "agent_end") {
44281
44728
  clearTimeout(timer);
44282
44729
  unsubscribe();
44283
- resolve4();
44730
+ resolve5();
44284
44731
  }
44285
44732
  });
44286
44733
  if (!session.isStreaming) {
44287
44734
  clearTimeout(timer);
44288
44735
  unsubscribe();
44289
- resolve4();
44736
+ resolve5();
44290
44737
  }
44291
44738
  });
44292
44739
  }
@@ -44829,10 +45276,28 @@ async function createAgentServer(params) {
44829
45276
  const encoder = new TextEncoder();
44830
45277
  const stream = new ReadableStream({
44831
45278
  start(controller) {
45279
+ let closed = false;
44832
45280
  function writeChunk(chunk) {
44833
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
45281
+ if (closed) {
45282
+ return;
45283
+ }
45284
+ try {
45285
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
44834
45286
 
44835
45287
  `));
45288
+ } catch {
45289
+ closed = true;
45290
+ }
45291
+ }
45292
+ function closeController() {
45293
+ if (closed) {
45294
+ return;
45295
+ }
45296
+ closed = true;
45297
+ try {
45298
+ controller.close();
45299
+ } catch {
45300
+ }
44836
45301
  }
44837
45302
  writeChunk({ type: "start" });
44838
45303
  writeChunk({ type: "start-step" });
@@ -44856,14 +45321,14 @@ async function createAgentServer(params) {
44856
45321
  extensionUICleanup?.();
44857
45322
  writeChunk({ type: "finish-step" });
44858
45323
  writeChunk({ type: "finish" });
44859
- controller.close();
45324
+ closeController();
44860
45325
  },
44861
45326
  onError: (errorText) => {
44862
45327
  extensionUICleanup?.();
44863
45328
  writeChunk({ type: "error", errorText });
44864
45329
  writeChunk({ type: "finish-step" });
44865
45330
  writeChunk({ type: "finish" });
44866
- controller.close();
45331
+ closeController();
44867
45332
  }
44868
45333
  });
44869
45334
  const unsubscribe = activeSession.subscribe((event) => {
@@ -44875,17 +45340,19 @@ async function createAgentServer(params) {
44875
45340
  activeSession.prompt(promptText).then(() => {
44876
45341
  if (!activeSession.isStreaming) {
44877
45342
  unsubscribe();
45343
+ extensionUICleanup?.();
44878
45344
  writeChunk({ type: "finish-step" });
44879
45345
  writeChunk({ type: "finish" });
44880
- controller.close();
45346
+ closeController();
44881
45347
  }
44882
45348
  }).catch((error48) => {
44883
45349
  const errorMessage = error48 instanceof Error ? error48.message : String(error48);
45350
+ extensionUICleanup?.();
44884
45351
  writeChunk({ type: "error", errorText: errorMessage });
44885
45352
  writeChunk({ type: "finish-step" });
44886
45353
  writeChunk({ type: "finish" });
44887
45354
  unsubscribe();
44888
- controller.close();
45355
+ closeController();
44889
45356
  });
44890
45357
  }
44891
45358
  });
@@ -45050,6 +45517,41 @@ async function createAgentServer(params) {
45050
45517
  commands: normalizeSlashCommands(activeSession.listCommands())
45051
45518
  });
45052
45519
  });
45520
+ app.get("/api/souls", async (c) => {
45521
+ const configFilePath = (0, import_node_path22.join)(settingsRootPath, "config.json");
45522
+ const envConfigService = new EnvironmentConfigService(configFilePath);
45523
+ const activeSoulId = await envConfigService.getActiveSoulId();
45524
+ const runtimeSoulId = process.env.DOCYRUS_PI_SOUL_ID?.trim() || DEFAULT_SOUL_ID;
45525
+ return c.json({
45526
+ souls: listSouls(activeSoulId),
45527
+ activeSoulId,
45528
+ runtimeSoulId,
45529
+ restartRequired: activeSoulId !== runtimeSoulId
45530
+ });
45531
+ });
45532
+ app.post("/api/souls/select", async (c) => {
45533
+ const body2 = await c.req.json().catch(() => ({}));
45534
+ const requested = typeof body2.soulId === "string" ? body2.soulId.trim() : "";
45535
+ if (!requested) {
45536
+ return c.json({ error: "Field 'soulId' is required." }, 400);
45537
+ }
45538
+ if (!SOULS.some((soul) => soul.id === requested)) {
45539
+ return c.json({
45540
+ error: `Unknown soul '${requested}'.`,
45541
+ availableSoulIds: SOULS.map((soul) => soul.id)
45542
+ }, 400);
45543
+ }
45544
+ const configFilePath = (0, import_node_path22.join)(settingsRootPath, "config.json");
45545
+ const envConfigService = new EnvironmentConfigService(configFilePath);
45546
+ const persistedSoulId = await envConfigService.setActiveSoulId(requested);
45547
+ const runtimeSoulId = process.env.DOCYRUS_PI_SOUL_ID?.trim() || DEFAULT_SOUL_ID;
45548
+ return c.json({
45549
+ soulId: persistedSoulId,
45550
+ runtimeSoulId,
45551
+ restartRequired: persistedSoulId !== runtimeSoulId,
45552
+ message: persistedSoulId === runtimeSoulId ? "Soul persisted. Already running \u2014 no restart needed." : "Soul persisted. Restart the agent (or open a new session) for the new soul to take effect."
45553
+ });
45554
+ });
45053
45555
  app.get("/api/knowledge/sections", async (c) => {
45054
45556
  try {
45055
45557
  const query = c.req.query("query")?.trim();
@@ -46400,6 +46902,69 @@ async function createAgentServer(params) {
46400
46902
  return c.json({ error: message }, status);
46401
46903
  }
46402
46904
  });
46905
+ app.get("/api/mcp/settings", async (c) => {
46906
+ try {
46907
+ const config2 = await loadMcpServerConfig(context.agentDir, context.cwd);
46908
+ return c.json({
46909
+ settings: {
46910
+ toolPrefix: config2.settings?.toolPrefix ?? "server",
46911
+ idleTimeout: config2.settings?.idleTimeout ?? 10,
46912
+ directTools: config2.settings?.directTools ?? false
46913
+ },
46914
+ supportedToolPrefixes: ["server", "none", "short"]
46915
+ });
46916
+ } catch (error48) {
46917
+ const message = error48 instanceof Error ? error48.message : String(error48);
46918
+ return c.json({ error: message }, 500);
46919
+ }
46920
+ });
46921
+ app.put("/api/mcp/settings", async (c) => {
46922
+ try {
46923
+ const body2 = await c.req.json();
46924
+ const result = validateMcpSettings(body2.settings);
46925
+ if (!result.ok) {
46926
+ return c.json({ error: result.error }, 400);
46927
+ }
46928
+ const merged = await updateMcpSettings(context.agentDir, result.value);
46929
+ return c.json({
46930
+ settings: {
46931
+ toolPrefix: merged.toolPrefix ?? "server",
46932
+ idleTimeout: merged.idleTimeout ?? 10,
46933
+ directTools: merged.directTools ?? false
46934
+ }
46935
+ });
46936
+ } catch (error48) {
46937
+ const message = error48 instanceof Error ? error48.message : String(error48);
46938
+ return c.json({ error: message }, 500);
46939
+ }
46940
+ });
46941
+ app.get("/api/mcp/imports", async (c) => {
46942
+ try {
46943
+ const summary = await describeImports(context.agentDir, context.cwd);
46944
+ return c.json({
46945
+ enabled: summary.enabled,
46946
+ available: summary.available,
46947
+ supported: SUPPORTED_IMPORT_KINDS
46948
+ });
46949
+ } catch (error48) {
46950
+ const message = error48 instanceof Error ? error48.message : String(error48);
46951
+ return c.json({ error: message }, 500);
46952
+ }
46953
+ });
46954
+ app.put("/api/mcp/imports", async (c) => {
46955
+ try {
46956
+ const body2 = await c.req.json();
46957
+ const result = validateImportKinds(body2.imports);
46958
+ if (!result.ok) {
46959
+ return c.json({ error: result.error }, 400);
46960
+ }
46961
+ const imports = await updateMcpImports(context.agentDir, result.value);
46962
+ return c.json({ imports });
46963
+ } catch (error48) {
46964
+ const message = error48 instanceof Error ? error48.message : String(error48);
46965
+ return c.json({ error: message }, 500);
46966
+ }
46967
+ });
46403
46968
  app.get("/api/skills", async (c) => {
46404
46969
  try {
46405
46970
  const skills = (await listSkills(context.agentDir)).filter((skill) => skill.name !== "docyrus-chrome-devtools-cli");
@@ -46425,6 +46990,56 @@ async function createAgentServer(params) {
46425
46990
  return c.json({ error: message }, 500);
46426
46991
  }
46427
46992
  });
46993
+ app.get("/api/skills/lock/entries", async (c) => {
46994
+ try {
46995
+ const lock = await readSkillsLockFile(context.agentDir);
46996
+ return c.json({
46997
+ version: lock.version,
46998
+ skills: lock.skills,
46999
+ supportedSourceTypes: ["local"]
47000
+ });
47001
+ } catch (error48) {
47002
+ const message = error48 instanceof Error ? error48.message : String(error48);
47003
+ return c.json({ error: message }, 500);
47004
+ }
47005
+ });
47006
+ app.post("/api/skills", async (c) => {
47007
+ try {
47008
+ const body2 = await c.req.json();
47009
+ if (!body2.source || typeof body2.source !== "string") {
47010
+ return c.json({ error: "Missing required field: source" }, 400);
47011
+ }
47012
+ if (!body2.sourceType || typeof body2.sourceType !== "string") {
47013
+ return c.json({ error: "Missing required field: sourceType" }, 400);
47014
+ }
47015
+ const request = {
47016
+ source: body2.source,
47017
+ sourceType: body2.sourceType,
47018
+ name: body2.name,
47019
+ overwrite: body2.overwrite === true
47020
+ };
47021
+ const result = await installSkill({
47022
+ agentDir: context.agentDir,
47023
+ cwd: context.cwd,
47024
+ request
47025
+ });
47026
+ return c.json({ ok: true, skill: result }, result.overwritten ? 200 : 201);
47027
+ } catch (error48) {
47028
+ return skillErrorResponse(c, error48);
47029
+ }
47030
+ });
47031
+ app.delete("/api/skills/:skillName", async (c) => {
47032
+ const skillName = c.req.param("skillName");
47033
+ try {
47034
+ const result = await uninstallSkill({
47035
+ agentDir: context.agentDir,
47036
+ name: skillName
47037
+ });
47038
+ return c.json({ ok: true, ...result });
47039
+ } catch (error48) {
47040
+ return skillErrorResponse(c, error48);
47041
+ }
47042
+ });
46428
47043
  app.post("/api/extension-ui-response", async (c) => {
46429
47044
  if (!extensionUIBridge) {
46430
47045
  return c.json({ error: "Extension UI bridge not available" }, 404);
@@ -46672,10 +47287,24 @@ async function createAgentServer(params) {
46672
47287
  process.stderr.write(` PUT /api/mcp/servers/:name \u2014 update MCP server
46673
47288
  `);
46674
47289
  process.stderr.write(` DEL /api/mcp/servers/:name \u2014 remove MCP server
47290
+ `);
47291
+ process.stderr.write(` GET /api/mcp/settings \u2014 MCP global settings
47292
+ `);
47293
+ process.stderr.write(` PUT /api/mcp/settings \u2014 update MCP global settings
47294
+ `);
47295
+ process.stderr.write(` GET /api/mcp/imports \u2014 available and enabled MCP imports
47296
+ `);
47297
+ process.stderr.write(` PUT /api/mcp/imports \u2014 update enabled MCP imports
46675
47298
  `);
46676
47299
  process.stderr.write(` GET /api/skills \u2014 list skills
46677
47300
  `);
46678
47301
  process.stderr.write(` GET /api/skills/:name \u2014 skill details
47302
+ `);
47303
+ process.stderr.write(` POST /api/skills \u2014 install a skill from local source
47304
+ `);
47305
+ process.stderr.write(` DEL /api/skills/:name \u2014 uninstall a skill
47306
+ `);
47307
+ process.stderr.write(` GET /api/skills/lock/entries \u2014 skills-lock.json entries
46679
47308
  `);
46680
47309
  process.stderr.write(` GET /api/tools \u2014 list all tools
46681
47310
  `);
@@ -46736,7 +47365,7 @@ function extractBearerToken(authorizationHeader) {
46736
47365
  return token;
46737
47366
  }
46738
47367
  function hashToken(token) {
46739
- return (0, import_node_crypto6.createHash)("sha256").update(token).digest();
47368
+ return (0, import_node_crypto7.createHash)("sha256").update(token).digest();
46740
47369
  }
46741
47370
  function isBearerTokenAuthorized(actualToken, expectedToken) {
46742
47371
  if (!expectedToken) {
@@ -46747,7 +47376,7 @@ function isBearerTokenAuthorized(actualToken, expectedToken) {
46747
47376
  }
46748
47377
  const actualHash = hashToken(actualToken);
46749
47378
  const expectedHash = hashToken(expectedToken);
46750
- return (0, import_node_crypto6.timingSafeEqual)(actualHash, expectedHash);
47379
+ return (0, import_node_crypto7.timingSafeEqual)(actualHash, expectedHash);
46751
47380
  }
46752
47381
  function createUnauthorizedResponse() {
46753
47382
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
@@ -46881,7 +47510,7 @@ function createServerSessionAdapter(params) {
46881
47510
  }
46882
47511
 
46883
47512
  // src/server/extensionUIBridge.ts
46884
- var import_node_crypto7 = require("node:crypto");
47513
+ var import_node_crypto8 = require("node:crypto");
46885
47514
  var ExtensionUIBridge = class {
46886
47515
  pending = /* @__PURE__ */ new Map();
46887
47516
  requestHandler;
@@ -46925,9 +47554,9 @@ var ExtensionUIBridge = class {
46925
47554
  if (opts?.signal?.aborted) {
46926
47555
  return Promise.resolve(defaultValue);
46927
47556
  }
46928
- const id = (0, import_node_crypto7.randomUUID)();
47557
+ const id = (0, import_node_crypto8.randomUUID)();
46929
47558
  const timeout = opts?.timeout ?? this.defaultTimeout;
46930
- return new Promise((resolve4, reject) => {
47559
+ return new Promise((resolve5, reject) => {
46931
47560
  const cleanup = () => {
46932
47561
  if (timer) {
46933
47562
  clearTimeout(timer);
@@ -46937,17 +47566,17 @@ var ExtensionUIBridge = class {
46937
47566
  };
46938
47567
  const onAbort = () => {
46939
47568
  cleanup();
46940
- resolve4(defaultValue);
47569
+ resolve5(defaultValue);
46941
47570
  };
46942
47571
  opts?.signal?.addEventListener("abort", onAbort, { once: true });
46943
47572
  const timer = timeout > 0 ? setTimeout(() => {
46944
47573
  cleanup();
46945
- resolve4(defaultValue);
47574
+ resolve5(defaultValue);
46946
47575
  }, timeout) : void 0;
46947
47576
  this.pending.set(id, {
46948
47577
  resolve: (response) => {
46949
47578
  cleanup();
46950
- resolve4(parseResponse(response));
47579
+ resolve5(parseResponse(response));
46951
47580
  },
46952
47581
  reject: (error48) => {
46953
47582
  cleanup();
@@ -46988,23 +47617,23 @@ var ExtensionUIBridge = class {
46988
47617
  opts
46989
47618
  ),
46990
47619
  notify: (message, notifyType) => {
46991
- const id = (0, import_node_crypto7.randomUUID)();
47620
+ const id = (0, import_node_crypto8.randomUUID)();
46992
47621
  this.requestHandler({ type: "extension_ui_request", id, method: "notify", message, placeholder: notifyType });
46993
47622
  },
46994
47623
  setStatus: (statusKey, statusText) => {
46995
- const id = (0, import_node_crypto7.randomUUID)();
47624
+ const id = (0, import_node_crypto8.randomUUID)();
46996
47625
  this.requestHandler({ type: "extension_ui_request", id, method: "setStatus", title: statusKey, message: statusText });
46997
47626
  },
46998
47627
  setWidget: (widgetKey, widgetLines) => {
46999
- const id = (0, import_node_crypto7.randomUUID)();
47628
+ const id = (0, import_node_crypto8.randomUUID)();
47000
47629
  this.requestHandler({ type: "extension_ui_request", id, method: "setWidget", title: widgetKey, options: widgetLines });
47001
47630
  },
47002
47631
  setTitle: (title) => {
47003
- const id = (0, import_node_crypto7.randomUUID)();
47632
+ const id = (0, import_node_crypto8.randomUUID)();
47004
47633
  this.requestHandler({ type: "extension_ui_request", id, method: "setTitle", title });
47005
47634
  },
47006
47635
  setEditorText: (text3) => {
47007
- const id = (0, import_node_crypto7.randomUUID)();
47636
+ const id = (0, import_node_crypto8.randomUUID)();
47008
47637
  this.requestHandler({ type: "extension_ui_request", id, method: "set_editor_text", message: text3 });
47009
47638
  },
47010
47639
  // No-op methods for server mode (not applicable without TUI)
@@ -47085,7 +47714,7 @@ function resolvePackagedPiResourceRoot() {
47085
47714
  (0, import_node_path23.resolve)(__dirname, "resources/pi-agent"),
47086
47715
  (0, import_node_path23.resolve)(process.cwd(), "dist/apps/api-cli/resources/pi-agent")
47087
47716
  ];
47088
- const resolved = candidates.find((candidate) => (0, import_node_fs13.existsSync)(candidate));
47717
+ const resolved = candidates.find((candidate) => (0, import_node_fs14.existsSync)(candidate));
47089
47718
  if (!resolved) {
47090
47719
  throw new Error(`Unable to locate pi agent resources. Checked: ${candidates.join(", ")}`);
47091
47720
  }
@@ -47217,7 +47846,7 @@ async function main() {
47217
47846
  skills: skills.filter((skill) => skill.name !== "docyrus-chrome-devtools-cli"),
47218
47847
  diagnostics
47219
47848
  }),
47220
- systemPrompt: (0, import_node_path23.join)(
47849
+ systemPrompt: process.env.DOCYRUS_PI_SYSTEM_PROMPT_PATH ?? (0, import_node_path23.join)(
47221
47850
  resourceRoot,
47222
47851
  "prompts",
47223
47852
  request.profile === "agent" ? "agent-system.md" : "coder-system.md"