@openacp/cli 2026.402.5 → 2026.403.1

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/cli.js CHANGED
@@ -1634,21 +1634,23 @@ var init_security_guard = __esm({
1634
1634
  "src/plugins/security/security-guard.ts"() {
1635
1635
  "use strict";
1636
1636
  SecurityGuard = class {
1637
- constructor(configManager, sessionManager) {
1638
- this.configManager = configManager;
1637
+ constructor(getSecurityConfig, sessionManager) {
1638
+ this.getSecurityConfig = getSecurityConfig;
1639
1639
  this.sessionManager = sessionManager;
1640
1640
  }
1641
- checkAccess(message) {
1642
- const config = this.configManager.get();
1643
- if (config.security.allowedUserIds.length > 0) {
1641
+ async checkAccess(message) {
1642
+ const config = await this.getSecurityConfig();
1643
+ const allowedIds = config.allowedUserIds ?? [];
1644
+ const maxSessions = config.maxConcurrentSessions ?? 20;
1645
+ if (allowedIds.length > 0) {
1644
1646
  const userId = String(message.userId);
1645
- if (!config.security.allowedUserIds.includes(userId)) {
1647
+ if (!allowedIds.includes(userId)) {
1646
1648
  return { allowed: false, reason: "Unauthorized user" };
1647
1649
  }
1648
1650
  }
1649
1651
  const active = this.sessionManager.listSessions().filter((s) => s.status === "active" || s.status === "initializing");
1650
- if (active.length >= config.security.maxConcurrentSessions) {
1651
- return { allowed: false, reason: `Session limit reached (${config.security.maxConcurrentSessions})` };
1652
+ if (active.length >= maxSessions) {
1653
+ return { allowed: false, reason: `Session limit reached (${maxSessions})` };
1652
1654
  }
1653
1655
  return { allowed: true };
1654
1656
  }
@@ -1668,7 +1670,7 @@ function createSecurityPlugin() {
1668
1670
  description: "User access control and session limits",
1669
1671
  essential: false,
1670
1672
  permissions: ["services:register", "middleware:register", "kernel:access", "commands:register"],
1671
- inheritableKeys: ["allowedUsers", "maxSessionsPerUser", "rateLimits"],
1673
+ inheritableKeys: ["allowedUserIds", "maxConcurrentSessions", "sessionTimeoutMinutes"],
1672
1674
  async install(ctx) {
1673
1675
  const { settings, legacyConfig, terminal } = ctx;
1674
1676
  if (legacyConfig) {
@@ -1745,10 +1747,28 @@ function createSecurityPlugin() {
1745
1747
  },
1746
1748
  async setup(ctx) {
1747
1749
  const core = ctx.core;
1748
- const guard = new SecurityGuard(core.configManager, core.sessionManager);
1750
+ const settingsManager = core.lifecycleManager?.settingsManager;
1751
+ const pluginName = ctx.pluginName;
1752
+ const getSecurityConfig = async () => {
1753
+ if (settingsManager) {
1754
+ const settings = await settingsManager.loadSettings(pluginName);
1755
+ if (Object.keys(settings).length > 0) {
1756
+ return {
1757
+ allowedUserIds: settings.allowedUserIds ?? [],
1758
+ maxConcurrentSessions: settings.maxConcurrentSessions ?? 20
1759
+ };
1760
+ }
1761
+ }
1762
+ const cfg = ctx.pluginConfig;
1763
+ return {
1764
+ allowedUserIds: cfg.allowedUserIds ?? [],
1765
+ maxConcurrentSessions: cfg.maxConcurrentSessions ?? 20
1766
+ };
1767
+ };
1768
+ const guard = new SecurityGuard(getSecurityConfig, core.sessionManager);
1749
1769
  ctx.registerMiddleware("message:incoming", {
1750
1770
  handler: async (payload, next) => {
1751
- const access2 = guard.checkAccess(payload);
1771
+ const access2 = await guard.checkAccess(payload);
1752
1772
  if (!access2.allowed) {
1753
1773
  ctx.log.info(`Access denied: ${access2.reason}`);
1754
1774
  return null;
@@ -3869,7 +3889,7 @@ var init_speech = __esm({
3869
3889
  essential: false,
3870
3890
  optionalPluginDependencies: { "@openacp/file-service": "^1.0.0" },
3871
3891
  permissions: ["services:register", "commands:register", "kernel:access"],
3872
- inheritableKeys: ["tts"],
3892
+ inheritableKeys: ["ttsProvider", "ttsVoice"],
3873
3893
  async install(ctx) {
3874
3894
  const { terminal, settings, legacyConfig } = ctx;
3875
3895
  const pluginsDir = ctx.instanceRoot ? path13.join(ctx.instanceRoot, "plugins") : void 0;
@@ -6826,6 +6846,289 @@ var init_token_store = __esm({
6826
6846
  }
6827
6847
  });
6828
6848
 
6849
+ // src/core/instance/instance-context.ts
6850
+ var instance_context_exports = {};
6851
+ __export(instance_context_exports, {
6852
+ createInstanceContext: () => createInstanceContext,
6853
+ generateSlug: () => generateSlug,
6854
+ getGlobalRoot: () => getGlobalRoot,
6855
+ resolveInstanceRoot: () => resolveInstanceRoot
6856
+ });
6857
+ import path21 from "path";
6858
+ import fs17 from "fs";
6859
+ import os10 from "os";
6860
+ function createInstanceContext(opts) {
6861
+ const { id, root, isGlobal } = opts;
6862
+ return {
6863
+ id,
6864
+ root,
6865
+ isGlobal,
6866
+ paths: {
6867
+ config: path21.join(root, "config.json"),
6868
+ sessions: path21.join(root, "sessions.json"),
6869
+ agents: path21.join(root, "agents.json"),
6870
+ registryCache: path21.join(root, "registry-cache.json"),
6871
+ plugins: path21.join(root, "plugins"),
6872
+ pluginsData: path21.join(root, "plugins", "data"),
6873
+ pluginRegistry: path21.join(root, "plugins.json"),
6874
+ logs: path21.join(root, "logs"),
6875
+ pid: path21.join(root, "openacp.pid"),
6876
+ running: path21.join(root, "running"),
6877
+ apiPort: path21.join(root, "api.port"),
6878
+ apiSecret: path21.join(root, "api-secret"),
6879
+ bin: path21.join(root, "bin"),
6880
+ cache: path21.join(root, "cache"),
6881
+ tunnels: path21.join(root, "tunnels.json"),
6882
+ agentsDir: path21.join(root, "agents")
6883
+ }
6884
+ };
6885
+ }
6886
+ function generateSlug(name) {
6887
+ const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
6888
+ return slug || "openacp";
6889
+ }
6890
+ function expandHome2(p2) {
6891
+ if (p2.startsWith("~")) return path21.join(os10.homedir(), p2.slice(1));
6892
+ return p2;
6893
+ }
6894
+ function resolveInstanceRoot(opts) {
6895
+ const cwd = opts.cwd ?? process.cwd();
6896
+ if (opts.dir) return path21.join(expandHome2(opts.dir), ".openacp");
6897
+ if (opts.local) return path21.join(cwd, ".openacp");
6898
+ if (opts.global) return path21.join(os10.homedir(), ".openacp");
6899
+ const localRoot = path21.join(cwd, ".openacp");
6900
+ if (fs17.existsSync(localRoot)) return localRoot;
6901
+ if (process.env.OPENACP_INSTANCE_ROOT) return process.env.OPENACP_INSTANCE_ROOT;
6902
+ return null;
6903
+ }
6904
+ function getGlobalRoot() {
6905
+ return path21.join(os10.homedir(), ".openacp");
6906
+ }
6907
+ var init_instance_context = __esm({
6908
+ "src/core/instance/instance-context.ts"() {
6909
+ "use strict";
6910
+ }
6911
+ });
6912
+
6913
+ // src/core/config/config-registry.ts
6914
+ var config_registry_exports = {};
6915
+ __export(config_registry_exports, {
6916
+ CONFIG_REGISTRY: () => CONFIG_REGISTRY,
6917
+ ConfigValidationError: () => ConfigValidationError,
6918
+ getConfigValue: () => getConfigValue,
6919
+ getFieldDef: () => getFieldDef,
6920
+ getFieldValueAsync: () => getFieldValueAsync,
6921
+ getSafeFields: () => getSafeFields,
6922
+ isHotReloadable: () => isHotReloadable,
6923
+ resolveOptions: () => resolveOptions,
6924
+ setFieldValueAsync: () => setFieldValueAsync
6925
+ });
6926
+ import * as fs18 from "fs";
6927
+ import * as path22 from "path";
6928
+ function getFieldDef(path68) {
6929
+ return CONFIG_REGISTRY.find((f) => f.path === path68);
6930
+ }
6931
+ function getSafeFields() {
6932
+ return CONFIG_REGISTRY.filter((f) => f.scope === "safe");
6933
+ }
6934
+ function isHotReloadable(path68) {
6935
+ const def = getFieldDef(path68);
6936
+ return def?.hotReload ?? false;
6937
+ }
6938
+ function resolveOptions(def, config) {
6939
+ if (!def.options) return void 0;
6940
+ return typeof def.options === "function" ? def.options(config) : def.options;
6941
+ }
6942
+ function getConfigValue(config, path68) {
6943
+ const parts = path68.split(".");
6944
+ let current = config;
6945
+ for (const part of parts) {
6946
+ if (current && typeof current === "object" && part in current) {
6947
+ current = current[part];
6948
+ } else {
6949
+ return void 0;
6950
+ }
6951
+ }
6952
+ return current;
6953
+ }
6954
+ async function getFieldValueAsync(field, configManager, settingsManager) {
6955
+ if (field.plugin && settingsManager) {
6956
+ const settings = await settingsManager.loadSettings(field.plugin.name);
6957
+ return settings[field.plugin.key];
6958
+ }
6959
+ return getConfigValue(configManager.get(), field.path);
6960
+ }
6961
+ function validateFieldValue(field, value) {
6962
+ switch (field.type) {
6963
+ case "number":
6964
+ if (typeof value !== "number" || Number.isNaN(value)) {
6965
+ throw new ConfigValidationError(`"${field.path}" expects a number, got ${typeof value}`);
6966
+ }
6967
+ break;
6968
+ case "toggle":
6969
+ if (typeof value !== "boolean") {
6970
+ throw new ConfigValidationError(`"${field.path}" expects a boolean, got ${typeof value}`);
6971
+ }
6972
+ break;
6973
+ case "string":
6974
+ if (typeof value !== "string") {
6975
+ throw new ConfigValidationError(`"${field.path}" expects a string, got ${typeof value}`);
6976
+ }
6977
+ break;
6978
+ case "select": {
6979
+ if (typeof value !== "string") {
6980
+ throw new ConfigValidationError(`"${field.path}" expects a string, got ${typeof value}`);
6981
+ }
6982
+ break;
6983
+ }
6984
+ }
6985
+ }
6986
+ async function setFieldValueAsync(field, value, configManager, settingsManager) {
6987
+ validateFieldValue(field, value);
6988
+ if (field.plugin && settingsManager) {
6989
+ await settingsManager.updatePluginSettings(field.plugin.name, {
6990
+ [field.plugin.key]: value
6991
+ });
6992
+ if (configManager.emit) {
6993
+ configManager.emit("config:changed", { path: field.path, value, oldValue: void 0 });
6994
+ }
6995
+ return { needsRestart: !field.hotReload };
6996
+ }
6997
+ await configManager.setPath(field.path, value);
6998
+ return { needsRestart: !field.hotReload };
6999
+ }
7000
+ var CONFIG_REGISTRY, ConfigValidationError;
7001
+ var init_config_registry = __esm({
7002
+ "src/core/config/config-registry.ts"() {
7003
+ "use strict";
7004
+ init_instance_context();
7005
+ CONFIG_REGISTRY = [
7006
+ {
7007
+ path: "defaultAgent",
7008
+ displayName: "Default Agent",
7009
+ group: "agent",
7010
+ type: "select",
7011
+ options: (config) => {
7012
+ try {
7013
+ const agentsPath = path22.join(getGlobalRoot(), "agents.json");
7014
+ if (fs18.existsSync(agentsPath)) {
7015
+ const data = JSON.parse(fs18.readFileSync(agentsPath, "utf-8"));
7016
+ return Object.keys(data.installed ?? {});
7017
+ }
7018
+ } catch {
7019
+ }
7020
+ return Object.keys(config.agents ?? {});
7021
+ },
7022
+ scope: "safe",
7023
+ hotReload: true
7024
+ },
7025
+ {
7026
+ path: "channels.telegram.outputMode",
7027
+ displayName: "Telegram Output Mode",
7028
+ group: "display",
7029
+ type: "select",
7030
+ options: ["low", "medium", "high"],
7031
+ scope: "safe",
7032
+ hotReload: true
7033
+ },
7034
+ {
7035
+ path: "channels.discord.outputMode",
7036
+ displayName: "Discord Output Mode",
7037
+ group: "display",
7038
+ type: "select",
7039
+ options: ["low", "medium", "high"],
7040
+ scope: "safe",
7041
+ hotReload: true
7042
+ },
7043
+ {
7044
+ path: "logging.level",
7045
+ displayName: "Log Level",
7046
+ group: "logging",
7047
+ type: "select",
7048
+ options: ["silent", "debug", "info", "warn", "error", "fatal"],
7049
+ scope: "safe",
7050
+ hotReload: true
7051
+ },
7052
+ {
7053
+ path: "tunnel.enabled",
7054
+ displayName: "Tunnel",
7055
+ group: "tunnel",
7056
+ type: "toggle",
7057
+ scope: "safe",
7058
+ hotReload: false,
7059
+ plugin: { name: "@openacp/tunnel", key: "enabled" }
7060
+ },
7061
+ {
7062
+ path: "security.maxConcurrentSessions",
7063
+ displayName: "Max Concurrent Sessions",
7064
+ group: "security",
7065
+ type: "number",
7066
+ scope: "safe",
7067
+ hotReload: true,
7068
+ plugin: { name: "@openacp/security", key: "maxConcurrentSessions" }
7069
+ },
7070
+ {
7071
+ path: "security.sessionTimeoutMinutes",
7072
+ displayName: "Session Timeout (min)",
7073
+ group: "security",
7074
+ type: "number",
7075
+ scope: "safe",
7076
+ hotReload: true,
7077
+ plugin: { name: "@openacp/security", key: "sessionTimeoutMinutes" }
7078
+ },
7079
+ {
7080
+ path: "workspace.baseDir",
7081
+ displayName: "Workspace Directory",
7082
+ group: "workspace",
7083
+ type: "string",
7084
+ scope: "safe",
7085
+ hotReload: true
7086
+ },
7087
+ {
7088
+ path: "sessionStore.ttlDays",
7089
+ displayName: "Session Store TTL (days)",
7090
+ group: "storage",
7091
+ type: "number",
7092
+ scope: "safe",
7093
+ hotReload: true
7094
+ },
7095
+ {
7096
+ path: "speech.stt.provider",
7097
+ displayName: "Speech to Text",
7098
+ group: "speech",
7099
+ type: "select",
7100
+ options: ["groq"],
7101
+ scope: "safe",
7102
+ hotReload: true,
7103
+ plugin: { name: "@openacp/speech", key: "sttProvider" }
7104
+ },
7105
+ {
7106
+ path: "speech.stt.apiKey",
7107
+ displayName: "STT API Key",
7108
+ group: "speech",
7109
+ type: "string",
7110
+ scope: "sensitive",
7111
+ hotReload: true,
7112
+ plugin: { name: "@openacp/speech", key: "groqApiKey" }
7113
+ },
7114
+ {
7115
+ path: "agentSwitch.labelHistory",
7116
+ displayName: "Label Agent in History",
7117
+ group: "agent",
7118
+ type: "toggle",
7119
+ scope: "safe",
7120
+ hotReload: true
7121
+ }
7122
+ ];
7123
+ ConfigValidationError = class extends Error {
7124
+ constructor(message) {
7125
+ super(message);
7126
+ this.name = "ConfigValidationError";
7127
+ }
7128
+ };
7129
+ }
7130
+ });
7131
+
6829
7132
  // src/plugins/api-server/middleware/error-handler.ts
6830
7133
  var error_handler_exports = {};
6831
7134
  __export(error_handler_exports, {
@@ -6848,6 +7151,16 @@ function globalErrorHandler(error, _request, reply) {
6848
7151
  });
6849
7152
  return;
6850
7153
  }
7154
+ if (error instanceof ConfigValidationError) {
7155
+ reply.status(400).send({
7156
+ error: {
7157
+ code: "VALIDATION_ERROR",
7158
+ message: error.message,
7159
+ statusCode: 400
7160
+ }
7161
+ });
7162
+ return;
7163
+ }
6851
7164
  if (error instanceof BadRequestError) {
6852
7165
  reply.status(400).send({
6853
7166
  error: {
@@ -6900,6 +7213,7 @@ var NotFoundError, BadRequestError, ServiceUnavailableError, AuthError;
6900
7213
  var init_error_handler = __esm({
6901
7214
  "src/plugins/api-server/middleware/error-handler.ts"() {
6902
7215
  "use strict";
7216
+ init_config_registry();
6903
7217
  NotFoundError = class extends Error {
6904
7218
  constructor(code, message) {
6905
7219
  super(message);
@@ -7204,7 +7518,7 @@ var init_sse_manager = __esm({
7204
7518
  handleRequest(req, res) {
7205
7519
  const parsedUrl = new URL(req.url || "", "http://localhost");
7206
7520
  const sessionFilter = parsedUrl.searchParams.get("sessionId");
7207
- console.log(`[sse] new connection from origin=${req.headers.origin ?? "none"} filter=${sessionFilter ?? "none"} total=${this.sseConnections.size + 1}`);
7521
+ console.log(`[sse] +connection total=${this.sseConnections.size + 1}`);
7208
7522
  const origin = req.headers.origin;
7209
7523
  const corsHeaders = {
7210
7524
  "Content-Type": "text/event-stream",
@@ -7226,7 +7540,7 @@ var init_sse_manager = __esm({
7226
7540
  const cleanup = () => {
7227
7541
  this.sseConnections.delete(res);
7228
7542
  this.sseCleanupHandlers.delete(res);
7229
- console.log(`[sse] connection closed, remaining=${this.sseConnections.size}`);
7543
+ console.log(`[sse] -connection remaining=${this.sseConnections.size}`);
7230
7544
  };
7231
7545
  this.sseCleanupHandlers.set(res, cleanup);
7232
7546
  req.on("close", cleanup);
@@ -7286,8 +7600,8 @@ var static_server_exports = {};
7286
7600
  __export(static_server_exports, {
7287
7601
  StaticServer: () => StaticServer
7288
7602
  });
7289
- import * as fs17 from "fs";
7290
- import * as path21 from "path";
7603
+ import * as fs19 from "fs";
7604
+ import * as path23 from "path";
7291
7605
  import { fileURLToPath as fileURLToPath2 } from "url";
7292
7606
  var MIME_TYPES, StaticServer;
7293
7607
  var init_static_server = __esm({
@@ -7311,16 +7625,16 @@ var init_static_server = __esm({
7311
7625
  this.uiDir = uiDir;
7312
7626
  if (!this.uiDir) {
7313
7627
  const __filename = fileURLToPath2(import.meta.url);
7314
- const candidate = path21.resolve(path21.dirname(__filename), "../../ui/dist");
7315
- if (fs17.existsSync(path21.join(candidate, "index.html"))) {
7628
+ const candidate = path23.resolve(path23.dirname(__filename), "../../ui/dist");
7629
+ if (fs19.existsSync(path23.join(candidate, "index.html"))) {
7316
7630
  this.uiDir = candidate;
7317
7631
  }
7318
7632
  if (!this.uiDir) {
7319
- const publishCandidate = path21.resolve(
7320
- path21.dirname(__filename),
7633
+ const publishCandidate = path23.resolve(
7634
+ path23.dirname(__filename),
7321
7635
  "../ui"
7322
7636
  );
7323
- if (fs17.existsSync(path21.join(publishCandidate, "index.html"))) {
7637
+ if (fs19.existsSync(path23.join(publishCandidate, "index.html"))) {
7324
7638
  this.uiDir = publishCandidate;
7325
7639
  }
7326
7640
  }
@@ -7332,12 +7646,12 @@ var init_static_server = __esm({
7332
7646
  serve(req, res) {
7333
7647
  if (!this.uiDir) return false;
7334
7648
  const urlPath = (req.url || "/").split("?")[0];
7335
- const safePath = path21.normalize(urlPath);
7336
- const filePath = path21.join(this.uiDir, safePath);
7337
- if (!filePath.startsWith(this.uiDir + path21.sep) && filePath !== this.uiDir)
7649
+ const safePath = path23.normalize(urlPath);
7650
+ const filePath = path23.join(this.uiDir, safePath);
7651
+ if (!filePath.startsWith(this.uiDir + path23.sep) && filePath !== this.uiDir)
7338
7652
  return false;
7339
- if (fs17.existsSync(filePath) && fs17.statSync(filePath).isFile()) {
7340
- const ext = path21.extname(filePath);
7653
+ if (fs19.existsSync(filePath) && fs19.statSync(filePath).isFile()) {
7654
+ const ext = path23.extname(filePath);
7341
7655
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
7342
7656
  const isHashed = /\.[a-zA-Z0-9]{8,}\.(js|css)$/.test(filePath);
7343
7657
  const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
@@ -7345,16 +7659,16 @@ var init_static_server = __esm({
7345
7659
  "Content-Type": contentType,
7346
7660
  "Cache-Control": cacheControl
7347
7661
  });
7348
- fs17.createReadStream(filePath).pipe(res);
7662
+ fs19.createReadStream(filePath).pipe(res);
7349
7663
  return true;
7350
7664
  }
7351
- const indexPath = path21.join(this.uiDir, "index.html");
7352
- if (fs17.existsSync(indexPath)) {
7665
+ const indexPath = path23.join(this.uiDir, "index.html");
7666
+ if (fs19.existsSync(indexPath)) {
7353
7667
  res.writeHead(200, {
7354
7668
  "Content-Type": "text/html; charset=utf-8",
7355
7669
  "Cache-Control": "no-cache"
7356
7670
  });
7357
- fs17.createReadStream(indexPath).pipe(res);
7671
+ fs19.createReadStream(indexPath).pipe(res);
7358
7672
  return true;
7359
7673
  }
7360
7674
  return false;
@@ -7841,291 +8155,68 @@ var init_common = __esm({
7841
8155
  })
7842
8156
  });
7843
8157
  }
7844
- });
7845
-
7846
- // src/core/agents/agent-registry.ts
7847
- var agent_registry_exports = {};
7848
- __export(agent_registry_exports, {
7849
- getAgentCapabilities: () => getAgentCapabilities
7850
- });
7851
- var init_agent_registry = __esm({
7852
- "src/core/agents/agent-registry.ts"() {
7853
- "use strict";
7854
- init_agent_dependencies();
7855
- }
7856
- });
7857
-
7858
- // src/plugins/api-server/routes/agents.ts
7859
- var agents_exports = {};
7860
- __export(agents_exports, {
7861
- agentRoutes: () => agentRoutes
7862
- });
7863
- async function agentRoutes(app, deps) {
7864
- app.get("/", { preHandler: requireScopes("agents:read") }, async () => {
7865
- const agents = deps.core.agentManager.getAvailableAgents();
7866
- const defaultAgent = deps.core.configManager.get().defaultAgent;
7867
- const agentsWithCaps = agents.map((a) => ({
7868
- ...a,
7869
- capabilities: getAgentCapabilities(a.name)
7870
- }));
7871
- return { agents: agentsWithCaps, default: defaultAgent };
7872
- });
7873
- app.get("/:name", { preHandler: requireScopes("agents:read") }, async (request) => {
7874
- const { name } = NameParamSchema.parse(request.params);
7875
- const agent = deps.core.agentCatalog.getInstalledAgent(name);
7876
- if (!agent) {
7877
- throw new NotFoundError("AGENT_NOT_FOUND", `Agent "${name}" not found`);
7878
- }
7879
- return {
7880
- ...agent,
7881
- key: name,
7882
- capabilities: getAgentCapabilities(name)
7883
- };
7884
- });
7885
- }
7886
- var init_agents = __esm({
7887
- "src/plugins/api-server/routes/agents.ts"() {
7888
- "use strict";
7889
- init_common();
7890
- init_error_handler();
7891
- init_auth();
7892
- init_agent_registry();
7893
- }
7894
- });
7895
-
7896
- // src/plugins/api-server/schemas/config.ts
7897
- import { z as z3 } from "zod";
7898
- var UpdateConfigBodySchema;
7899
- var init_config = __esm({
7900
- "src/plugins/api-server/schemas/config.ts"() {
7901
- "use strict";
7902
- UpdateConfigBodySchema = z3.object({
7903
- path: z3.string().min(1),
7904
- value: z3.unknown()
7905
- });
7906
- }
7907
- });
7908
-
7909
- // src/core/instance/instance-context.ts
7910
- var instance_context_exports = {};
7911
- __export(instance_context_exports, {
7912
- createInstanceContext: () => createInstanceContext,
7913
- generateSlug: () => generateSlug,
7914
- getGlobalRoot: () => getGlobalRoot,
7915
- resolveInstanceRoot: () => resolveInstanceRoot
7916
- });
7917
- import path22 from "path";
7918
- import fs18 from "fs";
7919
- import os10 from "os";
7920
- function createInstanceContext(opts) {
7921
- const { id, root, isGlobal } = opts;
7922
- return {
7923
- id,
7924
- root,
7925
- isGlobal,
7926
- paths: {
7927
- config: path22.join(root, "config.json"),
7928
- sessions: path22.join(root, "sessions.json"),
7929
- agents: path22.join(root, "agents.json"),
7930
- registryCache: path22.join(root, "registry-cache.json"),
7931
- plugins: path22.join(root, "plugins"),
7932
- pluginsData: path22.join(root, "plugins", "data"),
7933
- pluginRegistry: path22.join(root, "plugins.json"),
7934
- logs: path22.join(root, "logs"),
7935
- pid: path22.join(root, "openacp.pid"),
7936
- running: path22.join(root, "running"),
7937
- apiPort: path22.join(root, "api.port"),
7938
- apiSecret: path22.join(root, "api-secret"),
7939
- bin: path22.join(root, "bin"),
7940
- cache: path22.join(root, "cache"),
7941
- tunnels: path22.join(root, "tunnels.json"),
7942
- agentsDir: path22.join(root, "agents")
7943
- }
7944
- };
7945
- }
7946
- function generateSlug(name) {
7947
- const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
7948
- return slug || "openacp";
7949
- }
7950
- function expandHome2(p2) {
7951
- if (p2.startsWith("~")) return path22.join(os10.homedir(), p2.slice(1));
7952
- return p2;
7953
- }
7954
- function resolveInstanceRoot(opts) {
7955
- const cwd = opts.cwd ?? process.cwd();
7956
- if (opts.dir) return path22.join(expandHome2(opts.dir), ".openacp");
7957
- if (opts.local) return path22.join(cwd, ".openacp");
7958
- if (opts.global) return path22.join(os10.homedir(), ".openacp");
7959
- const localRoot = path22.join(cwd, ".openacp");
7960
- if (fs18.existsSync(localRoot)) return localRoot;
7961
- if (process.env.OPENACP_INSTANCE_ROOT) return process.env.OPENACP_INSTANCE_ROOT;
7962
- return null;
7963
- }
7964
- function getGlobalRoot() {
7965
- return path22.join(os10.homedir(), ".openacp");
7966
- }
7967
- var init_instance_context = __esm({
7968
- "src/core/instance/instance-context.ts"() {
7969
- "use strict";
7970
- }
7971
- });
7972
-
7973
- // src/core/config/config-registry.ts
7974
- var config_registry_exports = {};
7975
- __export(config_registry_exports, {
7976
- CONFIG_REGISTRY: () => CONFIG_REGISTRY,
7977
- getConfigValue: () => getConfigValue,
7978
- getFieldDef: () => getFieldDef,
7979
- getSafeFields: () => getSafeFields,
7980
- isHotReloadable: () => isHotReloadable,
7981
- resolveOptions: () => resolveOptions
7982
- });
7983
- import * as fs19 from "fs";
7984
- import * as path23 from "path";
7985
- function getFieldDef(path67) {
7986
- return CONFIG_REGISTRY.find((f) => f.path === path67);
7987
- }
7988
- function getSafeFields() {
7989
- return CONFIG_REGISTRY.filter((f) => f.scope === "safe");
7990
- }
7991
- function isHotReloadable(path67) {
7992
- const def = getFieldDef(path67);
7993
- return def?.hotReload ?? false;
7994
- }
7995
- function resolveOptions(def, config) {
7996
- if (!def.options) return void 0;
7997
- return typeof def.options === "function" ? def.options(config) : def.options;
7998
- }
7999
- function getConfigValue(config, path67) {
8000
- const parts = path67.split(".");
8001
- let current = config;
8002
- for (const part of parts) {
8003
- if (current && typeof current === "object" && part in current) {
8004
- current = current[part];
8005
- } else {
8006
- return void 0;
8007
- }
8008
- }
8009
- return current;
8010
- }
8011
- var CONFIG_REGISTRY;
8012
- var init_config_registry = __esm({
8013
- "src/core/config/config-registry.ts"() {
8014
- "use strict";
8015
- init_instance_context();
8016
- CONFIG_REGISTRY = [
8017
- {
8018
- path: "defaultAgent",
8019
- displayName: "Default Agent",
8020
- group: "agent",
8021
- type: "select",
8022
- options: (config) => {
8023
- try {
8024
- const agentsPath = path23.join(getGlobalRoot(), "agents.json");
8025
- if (fs19.existsSync(agentsPath)) {
8026
- const data = JSON.parse(fs19.readFileSync(agentsPath, "utf-8"));
8027
- return Object.keys(data.installed ?? {});
8028
- }
8029
- } catch {
8030
- }
8031
- return Object.keys(config.agents ?? {});
8032
- },
8033
- scope: "safe",
8034
- hotReload: true
8035
- },
8036
- {
8037
- path: "channels.telegram.outputMode",
8038
- displayName: "Telegram Output Mode",
8039
- group: "display",
8040
- type: "select",
8041
- options: ["low", "medium", "high"],
8042
- scope: "safe",
8043
- hotReload: true
8044
- },
8045
- {
8046
- path: "channels.discord.outputMode",
8047
- displayName: "Discord Output Mode",
8048
- group: "display",
8049
- type: "select",
8050
- options: ["low", "medium", "high"],
8051
- scope: "safe",
8052
- hotReload: true
8053
- },
8054
- {
8055
- path: "logging.level",
8056
- displayName: "Log Level",
8057
- group: "logging",
8058
- type: "select",
8059
- options: ["silent", "debug", "info", "warn", "error", "fatal"],
8060
- scope: "safe",
8061
- hotReload: true
8062
- },
8063
- {
8064
- path: "tunnel.enabled",
8065
- displayName: "Tunnel",
8066
- group: "tunnel",
8067
- type: "toggle",
8068
- scope: "safe",
8069
- hotReload: false
8070
- },
8071
- {
8072
- path: "security.maxConcurrentSessions",
8073
- displayName: "Max Concurrent Sessions",
8074
- group: "security",
8075
- type: "number",
8076
- scope: "safe",
8077
- hotReload: true
8078
- },
8079
- {
8080
- path: "security.sessionTimeoutMinutes",
8081
- displayName: "Session Timeout (min)",
8082
- group: "security",
8083
- type: "number",
8084
- scope: "safe",
8085
- hotReload: true
8086
- },
8087
- {
8088
- path: "workspace.baseDir",
8089
- displayName: "Workspace Directory",
8090
- group: "workspace",
8091
- type: "string",
8092
- scope: "safe",
8093
- hotReload: true
8094
- },
8095
- {
8096
- path: "sessionStore.ttlDays",
8097
- displayName: "Session Store TTL (days)",
8098
- group: "storage",
8099
- type: "number",
8100
- scope: "safe",
8101
- hotReload: true
8102
- },
8103
- {
8104
- path: "speech.stt.provider",
8105
- displayName: "Speech to Text",
8106
- group: "speech",
8107
- type: "select",
8108
- options: ["groq"],
8109
- scope: "safe",
8110
- hotReload: true
8111
- },
8112
- {
8113
- path: "speech.stt.apiKey",
8114
- displayName: "STT API Key",
8115
- group: "speech",
8116
- type: "string",
8117
- scope: "sensitive",
8118
- hotReload: true
8119
- },
8120
- {
8121
- path: "agentSwitch.labelHistory",
8122
- displayName: "Label Agent in History",
8123
- group: "agent",
8124
- type: "toggle",
8125
- scope: "safe",
8126
- hotReload: true
8127
- }
8128
- ];
8158
+ });
8159
+
8160
+ // src/core/agents/agent-registry.ts
8161
+ var agent_registry_exports = {};
8162
+ __export(agent_registry_exports, {
8163
+ getAgentCapabilities: () => getAgentCapabilities
8164
+ });
8165
+ var init_agent_registry = __esm({
8166
+ "src/core/agents/agent-registry.ts"() {
8167
+ "use strict";
8168
+ init_agent_dependencies();
8169
+ }
8170
+ });
8171
+
8172
+ // src/plugins/api-server/routes/agents.ts
8173
+ var agents_exports = {};
8174
+ __export(agents_exports, {
8175
+ agentRoutes: () => agentRoutes
8176
+ });
8177
+ async function agentRoutes(app, deps) {
8178
+ app.get("/", { preHandler: requireScopes("agents:read") }, async () => {
8179
+ const agents = deps.core.agentManager.getAvailableAgents();
8180
+ const defaultAgent = deps.core.configManager.get().defaultAgent;
8181
+ const agentsWithCaps = agents.map((a) => ({
8182
+ ...a,
8183
+ capabilities: getAgentCapabilities(a.name)
8184
+ }));
8185
+ return { agents: agentsWithCaps, default: defaultAgent };
8186
+ });
8187
+ app.get("/:name", { preHandler: requireScopes("agents:read") }, async (request) => {
8188
+ const { name } = NameParamSchema.parse(request.params);
8189
+ const agent = deps.core.agentCatalog.getInstalledAgent(name);
8190
+ if (!agent) {
8191
+ throw new NotFoundError("AGENT_NOT_FOUND", `Agent "${name}" not found`);
8192
+ }
8193
+ return {
8194
+ ...agent,
8195
+ key: name,
8196
+ capabilities: getAgentCapabilities(name)
8197
+ };
8198
+ });
8199
+ }
8200
+ var init_agents = __esm({
8201
+ "src/plugins/api-server/routes/agents.ts"() {
8202
+ "use strict";
8203
+ init_common();
8204
+ init_error_handler();
8205
+ init_auth();
8206
+ init_agent_registry();
8207
+ }
8208
+ });
8209
+
8210
+ // src/plugins/api-server/schemas/config.ts
8211
+ import { z as z3 } from "zod";
8212
+ var UpdateConfigBodySchema;
8213
+ var init_config = __esm({
8214
+ "src/plugins/api-server/schemas/config.ts"() {
8215
+ "use strict";
8216
+ UpdateConfigBodySchema = z3.object({
8217
+ path: z3.string().min(1),
8218
+ value: z3.unknown()
8219
+ });
8129
8220
  }
8130
8221
  });
8131
8222
 
@@ -8533,6 +8624,26 @@ var init_config2 = __esm({
8533
8624
  fs21.mkdirSync(dir, { recursive: true });
8534
8625
  fs21.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
8535
8626
  }
8627
+ async applyEnvToPluginSettings(settingsManager) {
8628
+ const pluginOverrides = [
8629
+ { envVar: "OPENACP_TUNNEL_ENABLED", pluginName: "@openacp/tunnel", key: "enabled", transform: (v) => v === "true" },
8630
+ { envVar: "OPENACP_TUNNEL_PORT", pluginName: "@openacp/tunnel", key: "port", transform: (v) => Number(v) },
8631
+ { envVar: "OPENACP_TUNNEL_PROVIDER", pluginName: "@openacp/tunnel", key: "provider" },
8632
+ { envVar: "OPENACP_API_PORT", pluginName: "@openacp/api-server", key: "port", transform: (v) => Number(v) },
8633
+ { envVar: "OPENACP_SPEECH_STT_PROVIDER", pluginName: "@openacp/speech", key: "sttProvider" },
8634
+ { envVar: "OPENACP_SPEECH_GROQ_API_KEY", pluginName: "@openacp/speech", key: "groqApiKey" },
8635
+ { envVar: "OPENACP_TELEGRAM_BOT_TOKEN", pluginName: "@openacp/telegram", key: "botToken" },
8636
+ { envVar: "OPENACP_TELEGRAM_CHAT_ID", pluginName: "@openacp/telegram", key: "chatId", transform: (v) => Number(v) }
8637
+ ];
8638
+ for (const { envVar, pluginName, key, transform } of pluginOverrides) {
8639
+ const value = process.env[envVar];
8640
+ if (value !== void 0) {
8641
+ const resolved = transform ? transform(value) : value;
8642
+ await settingsManager.updatePluginSettings(pluginName, { [key]: resolved });
8643
+ log12.debug({ envVar, pluginName, key }, "Env var override applied to plugin settings");
8644
+ }
8645
+ }
8646
+ }
8536
8647
  applyEnvOverrides(raw) {
8537
8648
  const overrides = [
8538
8649
  ["OPENACP_TELEGRAM_BOT_TOKEN", ["channels", "telegram", "botToken"]],
@@ -8645,18 +8756,19 @@ function redactDeep(obj) {
8645
8756
  }
8646
8757
  async function configRoutes(app, deps) {
8647
8758
  app.get("/editable", { preHandler: requireScopes("config:read") }, async () => {
8648
- const { getSafeFields: getSafeFields2, resolveOptions: resolveOptions2, getConfigValue: getConfigValue2 } = await Promise.resolve().then(() => (init_config_registry(), config_registry_exports));
8759
+ const { getSafeFields: getSafeFields2, resolveOptions: resolveOptions2, getFieldValueAsync: getFieldValueAsync2 } = await Promise.resolve().then(() => (init_config_registry(), config_registry_exports));
8649
8760
  const config = deps.core.configManager.get();
8761
+ const settingsManager = deps.core.settingsManager;
8650
8762
  const safeFields = getSafeFields2();
8651
- const fields = safeFields.map((def) => ({
8763
+ const fields = await Promise.all(safeFields.map(async (def) => ({
8652
8764
  path: def.path,
8653
8765
  displayName: def.displayName,
8654
8766
  group: def.group,
8655
8767
  type: def.type,
8656
8768
  options: resolveOptions2(def, config),
8657
- value: getConfigValue2(config, def.path),
8769
+ value: await getFieldValueAsync2(def, deps.core.configManager, settingsManager),
8658
8770
  hotReload: def.hotReload
8659
- }));
8771
+ })));
8660
8772
  return { fields };
8661
8773
  });
8662
8774
  app.get("/schema", { preHandler: requireScopes("config:read") }, async () => {
@@ -8676,16 +8788,20 @@ async function configRoutes(app, deps) {
8676
8788
  if (configPath.split(".").some((p2) => BLOCKED_KEYS.has(p2))) {
8677
8789
  return reply.status(400).send({ error: "Invalid config path" });
8678
8790
  }
8679
- const { getFieldDef: getFieldDef2 } = await Promise.resolve().then(() => (init_config_registry(), config_registry_exports));
8791
+ const { getFieldDef: getFieldDef2, setFieldValueAsync: setFieldValueAsync2 } = await Promise.resolve().then(() => (init_config_registry(), config_registry_exports));
8680
8792
  const fieldDef = getFieldDef2(configPath);
8681
8793
  if (!fieldDef || fieldDef.scope !== "safe") {
8682
8794
  return reply.status(403).send({
8683
8795
  error: "This config field cannot be modified via the API"
8684
8796
  });
8685
8797
  }
8686
- await deps.core.configManager.setPath(configPath, value);
8687
- const { isHotReloadable: isHotReloadable2 } = await Promise.resolve().then(() => (init_config_registry(), config_registry_exports));
8688
- const needsRestart = !isHotReloadable2(configPath);
8798
+ const settingsManager = deps.core.settingsManager;
8799
+ const { needsRestart } = await setFieldValueAsync2(
8800
+ fieldDef,
8801
+ value,
8802
+ deps.core.configManager,
8803
+ settingsManager
8804
+ );
8689
8805
  return {
8690
8806
  ok: true,
8691
8807
  needsRestart,
@@ -9196,13 +9312,152 @@ var init_workspace = __esm({
9196
9312
  }
9197
9313
  });
9198
9314
 
9315
+ // src/plugins/api-server/routes/plugins.ts
9316
+ var plugins_exports = {};
9317
+ __export(plugins_exports, {
9318
+ pluginRoutes: () => pluginRoutes
9319
+ });
9320
+ import path26 from "path";
9321
+ import os12 from "os";
9322
+ async function pluginRoutes(app, deps) {
9323
+ const { lifecycleManager } = deps;
9324
+ const admin = [requireScopes("system:admin")];
9325
+ app.get("/", { preHandler: admin }, async () => {
9326
+ if (!lifecycleManager?.registry) return { plugins: [] };
9327
+ const registry = lifecycleManager.registry;
9328
+ const loadedSet = new Set(lifecycleManager.loadedPlugins);
9329
+ const failedSet = new Set(lifecycleManager.failedPlugins);
9330
+ const loadOrderMap = new Map(lifecycleManager.plugins.map((p2) => [p2.name, p2]));
9331
+ const coreMap = new Map(corePlugins.map((p2) => [p2.name, p2]));
9332
+ const plugins = Array.from(registry.list().entries()).map(([name, entry]) => {
9333
+ const def = loadOrderMap.get(name) ?? coreMap.get(name);
9334
+ return {
9335
+ name,
9336
+ version: entry.version,
9337
+ description: entry.description,
9338
+ source: entry.source,
9339
+ enabled: entry.enabled,
9340
+ loaded: loadedSet.has(name),
9341
+ failed: failedSet.has(name),
9342
+ essential: def?.essential ?? false,
9343
+ hasConfigure: typeof def?.configure === "function"
9344
+ };
9345
+ });
9346
+ return { plugins };
9347
+ });
9348
+ app.get("/marketplace", { preHandler: admin }, async (_req, reply) => {
9349
+ try {
9350
+ const data = await registryClient.getRegistry();
9351
+ const installedNames = new Set(
9352
+ lifecycleManager?.registry ? Array.from(lifecycleManager.registry.list().keys()) : []
9353
+ );
9354
+ const plugins = data.plugins.map((p2) => ({
9355
+ ...p2,
9356
+ installed: installedNames.has(p2.name) || installedNames.has(p2.npm)
9357
+ }));
9358
+ return { plugins, categories: data.categories };
9359
+ } catch {
9360
+ return reply.status(503).send({ error: "Marketplace unavailable" });
9361
+ }
9362
+ });
9363
+ app.post("/:name/enable", { preHandler: admin }, async (req, reply) => {
9364
+ if (!lifecycleManager?.registry) {
9365
+ return reply.status(503).send({ error: "Plugin manager unavailable" });
9366
+ }
9367
+ const name = req.params.name;
9368
+ const registry = lifecycleManager.registry;
9369
+ const entry = registry.get(name);
9370
+ if (!entry) {
9371
+ return reply.status(404).send({ error: `Plugin "${name}" not found` });
9372
+ }
9373
+ if (lifecycleManager.loadedPlugins.includes(name)) {
9374
+ registry.setEnabled(name, true);
9375
+ await registry.save();
9376
+ return { ok: true };
9377
+ }
9378
+ let pluginDef = lifecycleManager.plugins.find((p2) => p2.name === name);
9379
+ if (!pluginDef) {
9380
+ if (entry.source === "builtin") {
9381
+ pluginDef = corePlugins.find((p2) => p2.name === name);
9382
+ } else {
9383
+ const { importFromDir: importFromDir2 } = await Promise.resolve().then(() => (init_plugin_installer(), plugin_installer_exports));
9384
+ const instanceRoot = lifecycleManager.instanceRoot ?? path26.join(os12.homedir(), ".openacp");
9385
+ const pluginsDir = path26.join(instanceRoot, "plugins");
9386
+ try {
9387
+ const mod = await importFromDir2(name, pluginsDir);
9388
+ pluginDef = mod.default ?? mod;
9389
+ } catch {
9390
+ return reply.status(500).send({ error: "Plugin module could not be loaded. Try restarting the server." });
9391
+ }
9392
+ }
9393
+ }
9394
+ if (!pluginDef) {
9395
+ return reply.status(500).send({ error: `Plugin definition not found for "${name}"` });
9396
+ }
9397
+ registry.setEnabled(name, true);
9398
+ await registry.save();
9399
+ await lifecycleManager.boot([pluginDef]);
9400
+ if (lifecycleManager.failedPlugins.includes(name)) {
9401
+ return reply.status(500).send({ error: `Plugin "${name}" failed to start` });
9402
+ }
9403
+ return { ok: true };
9404
+ });
9405
+ app.post("/:name/disable", { preHandler: admin }, async (req, reply) => {
9406
+ if (!lifecycleManager?.registry) {
9407
+ return reply.status(503).send({ error: "Plugin manager unavailable" });
9408
+ }
9409
+ const name = req.params.name;
9410
+ const registry = lifecycleManager.registry;
9411
+ const entry = registry.get(name);
9412
+ if (!entry) {
9413
+ return reply.status(404).send({ error: `Plugin "${name}" not found` });
9414
+ }
9415
+ const def = lifecycleManager.plugins.find((p2) => p2.name === name) ?? corePlugins.find((p2) => p2.name === name);
9416
+ if (def?.essential) {
9417
+ return reply.status(409).send({ error: "Essential plugins cannot be disabled" });
9418
+ }
9419
+ await lifecycleManager.unloadPlugin(name);
9420
+ registry.setEnabled(name, false);
9421
+ await registry.save();
9422
+ return { ok: true };
9423
+ });
9424
+ app.delete("/:name", { preHandler: admin }, async (req, reply) => {
9425
+ if (!lifecycleManager?.registry) {
9426
+ return reply.status(503).send({ error: "Plugin manager unavailable" });
9427
+ }
9428
+ const name = req.params.name;
9429
+ const registry = lifecycleManager.registry;
9430
+ const entry = registry.get(name);
9431
+ if (!entry) {
9432
+ return reply.status(404).send({ error: `Plugin "${name}" not found` });
9433
+ }
9434
+ if (entry.source === "builtin") {
9435
+ return reply.status(400).send({ error: "Builtin plugins cannot be uninstalled. Use disable instead." });
9436
+ }
9437
+ await lifecycleManager.unloadPlugin(name);
9438
+ registry.remove(name);
9439
+ await registry.save();
9440
+ return { ok: true };
9441
+ });
9442
+ }
9443
+ var registryClient;
9444
+ var init_plugins = __esm({
9445
+ "src/plugins/api-server/routes/plugins.ts"() {
9446
+ "use strict";
9447
+ init_auth();
9448
+ init_core_plugins();
9449
+ init_registry_client();
9450
+ registryClient = new RegistryClient();
9451
+ }
9452
+ });
9453
+
9199
9454
  // src/core/instance/instance-registry.ts
9200
9455
  var instance_registry_exports = {};
9201
9456
  __export(instance_registry_exports, {
9202
9457
  InstanceRegistry: () => InstanceRegistry
9203
9458
  });
9204
9459
  import fs22 from "fs";
9205
- import path26 from "path";
9460
+ import path27 from "path";
9206
9461
  var InstanceRegistry;
9207
9462
  var init_instance_registry = __esm({
9208
9463
  "src/core/instance/instance-registry.ts"() {
@@ -9240,7 +9495,7 @@ var init_instance_registry = __esm({
9240
9495
  }
9241
9496
  }
9242
9497
  save() {
9243
- const dir = path26.dirname(this.registryPath);
9498
+ const dir = path27.dirname(this.registryPath);
9244
9499
  fs22.mkdirSync(dir, { recursive: true });
9245
9500
  fs22.writeFileSync(this.registryPath, JSON.stringify(this.data, null, 2));
9246
9501
  }
@@ -9276,13 +9531,13 @@ __export(api_server_exports, {
9276
9531
  });
9277
9532
  import * as fs23 from "fs";
9278
9533
  import * as crypto2 from "crypto";
9279
- import * as path27 from "path";
9534
+ import * as path28 from "path";
9280
9535
  import { fileURLToPath as fileURLToPath3 } from "url";
9281
9536
  function getVersion() {
9282
9537
  if (cachedVersion) return cachedVersion;
9283
9538
  try {
9284
9539
  const __filename = fileURLToPath3(import.meta.url);
9285
- const pkgPath = path27.resolve(path27.dirname(__filename), "../../../package.json");
9540
+ const pkgPath = path28.resolve(path28.dirname(__filename), "../../../package.json");
9286
9541
  const pkg = JSON.parse(fs23.readFileSync(pkgPath, "utf-8"));
9287
9542
  cachedVersion = pkg.version ?? "0.0.0-dev";
9288
9543
  } catch {
@@ -9291,7 +9546,7 @@ function getVersion() {
9291
9546
  return cachedVersion;
9292
9547
  }
9293
9548
  function loadOrCreateSecret(secretFilePath) {
9294
- const dir = path27.dirname(secretFilePath);
9549
+ const dir = path28.dirname(secretFilePath);
9295
9550
  fs23.mkdirSync(dir, { recursive: true });
9296
9551
  try {
9297
9552
  const existing = fs23.readFileSync(secretFilePath, "utf-8").trim();
@@ -9317,7 +9572,7 @@ function loadOrCreateSecret(secretFilePath) {
9317
9572
  return secret;
9318
9573
  }
9319
9574
  function writePortFile(portFilePath, port) {
9320
- const dir = path27.dirname(portFilePath);
9575
+ const dir = path28.dirname(portFilePath);
9321
9576
  fs23.mkdirSync(dir, { recursive: true });
9322
9577
  fs23.writeFileSync(portFilePath, String(port));
9323
9578
  }
@@ -9409,10 +9664,10 @@ function createApiServerPlugin() {
9409
9664
  { port: apiConfig.port, host: apiConfig.host, instanceRoot },
9410
9665
  "API server plugin setup \u2014 config loaded"
9411
9666
  );
9412
- portFilePath = path27.join(instanceRoot, "api.port");
9413
- const secretFilePath = path27.join(instanceRoot, "api-secret");
9414
- const jwtSecretFilePath = path27.join(instanceRoot, "jwt-secret");
9415
- const tokensFilePath = path27.join(instanceRoot, "tokens.json");
9667
+ portFilePath = path28.join(instanceRoot, "api.port");
9668
+ const secretFilePath = path28.join(instanceRoot, "api-secret");
9669
+ const jwtSecretFilePath = path28.join(instanceRoot, "jwt-secret");
9670
+ const tokensFilePath = path28.join(instanceRoot, "tokens.json");
9416
9671
  const startedAt = Date.now();
9417
9672
  const secret = loadOrCreateSecret(secretFilePath);
9418
9673
  const jwtSecret = loadOrCreateSecret(jwtSecretFilePath);
@@ -9435,6 +9690,7 @@ function createApiServerPlugin() {
9435
9690
  const { commandRoutes: commandRoutes2 } = await Promise.resolve().then(() => (init_commands2(), commands_exports));
9436
9691
  const { authRoutes: authRoutes2 } = await Promise.resolve().then(() => (init_auth3(), auth_exports3));
9437
9692
  const { workspaceRoute: workspaceRoute2 } = await Promise.resolve().then(() => (init_workspace(), workspace_exports));
9693
+ const { pluginRoutes: pluginRoutes2 } = await Promise.resolve().then(() => (init_plugins(), plugins_exports));
9438
9694
  server = await createApiServer2({
9439
9695
  port: apiConfig.port,
9440
9696
  host: apiConfig.host,
@@ -9453,7 +9709,8 @@ function createApiServerPlugin() {
9453
9709
  getVersion,
9454
9710
  commandRegistry,
9455
9711
  authPreHandler: routeAuthPreHandler,
9456
- contextManager
9712
+ contextManager,
9713
+ lifecycleManager: core.lifecycleManager
9457
9714
  };
9458
9715
  server.registerPlugin("/api/v1/sessions", async (app) => sessionRoutes2(app, deps));
9459
9716
  server.registerPlugin("/api/v1/agents", async (app) => agentRoutes2(app, deps));
@@ -9464,16 +9721,17 @@ function createApiServerPlugin() {
9464
9721
  server.registerPlugin("/api/v1/notify", async (app) => notifyRoutes2(app, deps));
9465
9722
  server.registerPlugin("/api/v1/commands", async (app) => commandRoutes2(app, deps));
9466
9723
  server.registerPlugin("/api/v1/auth", async (app) => authRoutes2(app, { tokenStore, getJwtSecret: () => jwtSecret }));
9724
+ server.registerPlugin("/api/v1/plugins", async (app) => pluginRoutes2(app, deps));
9467
9725
  const { InstanceRegistry: InstanceRegistry2 } = await Promise.resolve().then(() => (init_instance_registry(), instance_registry_exports));
9468
9726
  const { getGlobalRoot: getGlobalRoot2 } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
9469
9727
  const globalRoot = getGlobalRoot2();
9470
- const instanceReg = new InstanceRegistry2(path27.join(globalRoot, "instances.json"));
9728
+ const instanceReg = new InstanceRegistry2(path28.join(globalRoot, "instances.json"));
9471
9729
  instanceReg.load();
9472
9730
  const instanceEntry = instanceReg.getByRoot(instanceRoot);
9473
9731
  const workspaceId = instanceEntry?.id ?? "main";
9474
9732
  const appConfig = core.configManager.get();
9475
9733
  const workspaceName = appConfig.instanceName ?? "Main";
9476
- const workspaceDir = path27.dirname(instanceRoot);
9734
+ const workspaceDir = path28.dirname(instanceRoot);
9477
9735
  server.registerPlugin("/api/v1", async (app) => {
9478
9736
  await app.register(workspaceRoute2, {
9479
9737
  id: workspaceId,
@@ -10408,8 +10666,8 @@ function formatToolSummary(name, rawInput, displaySummary) {
10408
10666
  }
10409
10667
  if (lowerName === "grep") {
10410
10668
  const pattern = args2.pattern ?? "";
10411
- const path67 = args2.path ?? "";
10412
- return pattern ? `\u{1F50D} Grep "${pattern}"${path67 ? ` in ${path67}` : ""}` : `\u{1F527} ${name}`;
10669
+ const path68 = args2.path ?? "";
10670
+ return pattern ? `\u{1F50D} Grep "${pattern}"${path68 ? ` in ${path68}` : ""}` : `\u{1F527} ${name}`;
10413
10671
  }
10414
10672
  if (lowerName === "glob") {
10415
10673
  const pattern = args2.pattern ?? "";
@@ -10445,8 +10703,8 @@ function formatToolTitle(name, rawInput, displayTitle) {
10445
10703
  }
10446
10704
  if (lowerName === "grep") {
10447
10705
  const pattern = args2.pattern ?? "";
10448
- const path67 = args2.path ?? "";
10449
- return pattern ? `"${pattern}"${path67 ? ` in ${path67}` : ""}` : name;
10706
+ const path68 = args2.path ?? "";
10707
+ return pattern ? `"${pattern}"${path68 ? ` in ${path68}` : ""}` : name;
10450
10708
  }
10451
10709
  if (lowerName === "glob") {
10452
10710
  return String(args2.pattern ?? name);
@@ -12254,12 +12512,11 @@ var init_menu = __esm({
12254
12512
 
12255
12513
  // src/plugins/telegram/commands/settings.ts
12256
12514
  import { InlineKeyboard as InlineKeyboard6 } from "grammy";
12257
- function buildSettingsKeyboard(core) {
12258
- const config = core.configManager.get();
12515
+ async function buildSettingsKeyboard(core) {
12259
12516
  const fields = getSafeFields();
12260
12517
  const kb = new InlineKeyboard6();
12261
12518
  for (const field of fields) {
12262
- const value = getConfigValue(config, field.path);
12519
+ const value = await getFieldValueAsync(field, core.configManager, core.settingsManager);
12263
12520
  const label = formatFieldLabel(field, value);
12264
12521
  if (field.type === "toggle") {
12265
12522
  kb.text(`${label}`, `s:toggle:${field.path}`).row();
@@ -12290,7 +12547,7 @@ function formatFieldLabel(field, value) {
12290
12547
  return `${icon} ${field.displayName}: ${displayValue}`;
12291
12548
  }
12292
12549
  async function handleSettings(ctx, core) {
12293
- const kb = buildSettingsKeyboard(core);
12550
+ const kb = await buildSettingsKeyboard(core);
12294
12551
  await ctx.reply(`<b>\u2699\uFE0F Settings</b>
12295
12552
  Tap to change:`, {
12296
12553
  parse_mode: "HTML",
@@ -12300,19 +12557,20 @@ Tap to change:`, {
12300
12557
  function setupSettingsCallbacks(bot, core, getAssistantSession) {
12301
12558
  bot.callbackQuery(/^s:toggle:/, async (ctx) => {
12302
12559
  const fieldPath = ctx.callbackQuery.data.replace("s:toggle:", "");
12303
- const config = core.configManager.get();
12304
- const currentValue = getConfigValue(config, fieldPath);
12560
+ const fieldDef = getSafeFields().find((f) => f.path === fieldPath);
12561
+ if (!fieldDef) return;
12562
+ const settingsManager = core.settingsManager;
12563
+ const currentValue = await getFieldValueAsync(fieldDef, core.configManager, settingsManager);
12305
12564
  const newValue = !currentValue;
12306
12565
  try {
12307
- const updates = buildNestedUpdate(fieldPath, newValue);
12308
- await core.configManager.save(updates, fieldPath);
12566
+ await setFieldValueAsync(fieldDef, newValue, core.configManager, settingsManager);
12309
12567
  const toast = isHotReloadable(fieldPath) ? `\u2705 ${fieldPath} = ${newValue}` : `\u2705 ${fieldPath} = ${newValue} (restart needed)`;
12310
12568
  try {
12311
12569
  await ctx.answerCallbackQuery({ text: toast });
12312
12570
  } catch {
12313
12571
  }
12314
12572
  try {
12315
- await ctx.editMessageReplyMarkup({ reply_markup: buildSettingsKeyboard(core) });
12573
+ await ctx.editMessageReplyMarkup({ reply_markup: await buildSettingsKeyboard(core) });
12316
12574
  } catch {
12317
12575
  }
12318
12576
  } catch (err) {
@@ -12329,7 +12587,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
12329
12587
  const fieldDef = getSafeFields().find((f) => f.path === fieldPath);
12330
12588
  if (!fieldDef) return;
12331
12589
  const options = resolveOptions(fieldDef, config) ?? [];
12332
- const currentValue = getConfigValue(config, fieldPath);
12590
+ const currentValue = await getFieldValueAsync(fieldDef, core.configManager, core.settingsManager);
12333
12591
  const kb = new InlineKeyboard6();
12334
12592
  for (const opt of options) {
12335
12593
  const marker = opt === String(currentValue) ? " \u2713" : "";
@@ -12353,11 +12611,21 @@ Select a value:`, {
12353
12611
  const parts = ctx.callbackQuery.data.replace("s:pick:", "").split(":");
12354
12612
  const fieldPath = parts.slice(0, -1).join(":");
12355
12613
  const newValue = parts[parts.length - 1];
12614
+ const fieldDef = getSafeFields().find((f) => f.path === fieldPath);
12615
+ if (!fieldDef) return;
12356
12616
  try {
12357
12617
  if (fieldPath === "speech.stt.provider") {
12358
- const config = core.configManager.get();
12359
- const providerConfig = config.speech?.stt?.providers?.[newValue];
12360
- if (!providerConfig?.apiKey) {
12618
+ const sm = core.settingsManager;
12619
+ let hasApiKey = false;
12620
+ if (sm) {
12621
+ const speechSettings = await sm.loadSettings("@openacp/speech");
12622
+ hasApiKey = !!speechSettings.groqApiKey;
12623
+ } else {
12624
+ const config = core.configManager.get();
12625
+ const providerConfig = config.speech?.stt?.providers?.[newValue];
12626
+ hasApiKey = !!providerConfig?.apiKey;
12627
+ }
12628
+ if (!hasApiKey) {
12361
12629
  const assistant = getAssistantSession();
12362
12630
  if (assistant) {
12363
12631
  try {
@@ -12375,8 +12643,7 @@ Select a value:`, {
12375
12643
  return;
12376
12644
  }
12377
12645
  }
12378
- const updates = buildNestedUpdate(fieldPath, newValue);
12379
- await core.configManager.save(updates, fieldPath);
12646
+ await setFieldValueAsync(fieldDef, newValue, core.configManager, core.settingsManager);
12380
12647
  try {
12381
12648
  await ctx.answerCallbackQuery({ text: `\u2705 ${fieldPath} = ${newValue}` });
12382
12649
  } catch {
@@ -12385,7 +12652,7 @@ Select a value:`, {
12385
12652
  await ctx.editMessageText(`<b>\u2699\uFE0F Settings</b>
12386
12653
  Tap to change:`, {
12387
12654
  parse_mode: "HTML",
12388
- reply_markup: buildSettingsKeyboard(core)
12655
+ reply_markup: await buildSettingsKeyboard(core)
12389
12656
  });
12390
12657
  } catch {
12391
12658
  }
@@ -12399,10 +12666,9 @@ Tap to change:`, {
12399
12666
  });
12400
12667
  bot.callbackQuery(/^s:input:/, async (ctx) => {
12401
12668
  const fieldPath = ctx.callbackQuery.data.replace("s:input:", "");
12402
- const config = core.configManager.get();
12403
12669
  const fieldDef = getSafeFields().find((f) => f.path === fieldPath);
12404
12670
  if (!fieldDef) return;
12405
- const currentValue = getConfigValue(config, fieldPath);
12671
+ const currentValue = await getFieldValueAsync(fieldDef, core.configManager, core.settingsManager);
12406
12672
  const assistant = getAssistantSession();
12407
12673
  if (!assistant) {
12408
12674
  try {
@@ -12443,23 +12709,12 @@ Choose an action:`, {
12443
12709
  await ctx.editMessageText(`<b>\u2699\uFE0F Settings</b>
12444
12710
  Tap to change:`, {
12445
12711
  parse_mode: "HTML",
12446
- reply_markup: buildSettingsKeyboard(core)
12712
+ reply_markup: await buildSettingsKeyboard(core)
12447
12713
  });
12448
12714
  } catch {
12449
12715
  }
12450
12716
  });
12451
12717
  }
12452
- function buildNestedUpdate(dotPath, value) {
12453
- const parts = dotPath.split(".");
12454
- const result = {};
12455
- let target = result;
12456
- for (let i = 0; i < parts.length - 1; i++) {
12457
- target[parts[i]] = {};
12458
- target = target[parts[i]];
12459
- }
12460
- target[parts[parts.length - 1]] = value;
12461
- return result;
12462
- }
12463
12718
  var log18;
12464
12719
  var init_settings = __esm({
12465
12720
  "src/plugins/telegram/commands/settings.ts"() {
@@ -12534,7 +12789,7 @@ var init_config4 = __esm({
12534
12789
  // src/core/doctor/checks/agents.ts
12535
12790
  import { execFileSync as execFileSync3 } from "child_process";
12536
12791
  import * as fs25 from "fs";
12537
- import * as path28 from "path";
12792
+ import * as path29 from "path";
12538
12793
  function commandExists2(cmd) {
12539
12794
  try {
12540
12795
  execFileSync3("which", [cmd], { stdio: "pipe" });
@@ -12543,9 +12798,9 @@ function commandExists2(cmd) {
12543
12798
  }
12544
12799
  let dir = process.cwd();
12545
12800
  while (true) {
12546
- const binPath = path28.join(dir, "node_modules", ".bin", cmd);
12801
+ const binPath = path29.join(dir, "node_modules", ".bin", cmd);
12547
12802
  if (fs25.existsSync(binPath)) return true;
12548
- const parent = path28.dirname(dir);
12803
+ const parent = path29.dirname(dir);
12549
12804
  if (parent === dir) break;
12550
12805
  dir = parent;
12551
12806
  }
@@ -12820,9 +13075,9 @@ var init_workspace2 = __esm({
12820
13075
 
12821
13076
  // src/core/doctor/checks/plugins.ts
12822
13077
  import * as fs28 from "fs";
12823
- import * as path29 from "path";
13078
+ import * as path30 from "path";
12824
13079
  var pluginsCheck;
12825
- var init_plugins = __esm({
13080
+ var init_plugins2 = __esm({
12826
13081
  "src/core/doctor/checks/plugins.ts"() {
12827
13082
  "use strict";
12828
13083
  pluginsCheck = {
@@ -12839,7 +13094,7 @@ var init_plugins = __esm({
12839
13094
  fix: async () => {
12840
13095
  fs28.mkdirSync(ctx.pluginsDir, { recursive: true });
12841
13096
  fs28.writeFileSync(
12842
- path29.join(ctx.pluginsDir, "package.json"),
13097
+ path30.join(ctx.pluginsDir, "package.json"),
12843
13098
  JSON.stringify({ name: "openacp-plugins", private: true, dependencies: {} }, null, 2)
12844
13099
  );
12845
13100
  return { success: true, message: "initialized plugins directory" };
@@ -12848,7 +13103,7 @@ var init_plugins = __esm({
12848
13103
  return results;
12849
13104
  }
12850
13105
  results.push({ status: "pass", message: "Plugins directory exists" });
12851
- const pkgPath = path29.join(ctx.pluginsDir, "package.json");
13106
+ const pkgPath = path30.join(ctx.pluginsDir, "package.json");
12852
13107
  if (!fs28.existsSync(pkgPath)) {
12853
13108
  results.push({
12854
13109
  status: "warn",
@@ -12995,8 +13250,8 @@ var init_daemon = __esm({
12995
13250
 
12996
13251
  // src/core/doctor/checks/tunnel.ts
12997
13252
  import * as fs30 from "fs";
12998
- import * as path30 from "path";
12999
- import * as os12 from "os";
13253
+ import * as path31 from "path";
13254
+ import * as os13 from "os";
13000
13255
  import { execFileSync as execFileSync4 } from "child_process";
13001
13256
  var tunnelCheck;
13002
13257
  var init_tunnel3 = __esm({
@@ -13018,8 +13273,8 @@ var init_tunnel3 = __esm({
13018
13273
  const provider = ctx.config.tunnel.provider;
13019
13274
  results.push({ status: "pass", message: `Tunnel provider: ${provider}` });
13020
13275
  if (provider === "cloudflare") {
13021
- const binName = os12.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
13022
- const binPath = path30.join(ctx.dataDir, "bin", binName);
13276
+ const binName = os13.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
13277
+ const binPath = path31.join(ctx.dataDir, "bin", binName);
13023
13278
  let found = false;
13024
13279
  if (fs30.existsSync(binPath)) {
13025
13280
  found = true;
@@ -13068,7 +13323,7 @@ __export(doctor_exports, {
13068
13323
  DoctorEngine: () => DoctorEngine
13069
13324
  });
13070
13325
  import * as fs31 from "fs";
13071
- import * as path31 from "path";
13326
+ import * as path32 from "path";
13072
13327
  var ALL_CHECKS, CHECK_TIMEOUT_MS, DoctorEngine;
13073
13328
  var init_doctor = __esm({
13074
13329
  "src/core/doctor/index.ts"() {
@@ -13080,7 +13335,7 @@ var init_doctor = __esm({
13080
13335
  init_telegram();
13081
13336
  init_storage();
13082
13337
  init_workspace2();
13083
- init_plugins();
13338
+ init_plugins2();
13084
13339
  init_daemon();
13085
13340
  init_tunnel3();
13086
13341
  ALL_CHECKS = [
@@ -13150,7 +13405,7 @@ var init_doctor = __esm({
13150
13405
  }
13151
13406
  async buildContext() {
13152
13407
  const dataDir = this.dataDir;
13153
- const configPath = process.env.OPENACP_CONFIG_PATH || path31.join(dataDir, "config.json");
13408
+ const configPath = process.env.OPENACP_CONFIG_PATH || path32.join(dataDir, "config.json");
13154
13409
  let config = null;
13155
13410
  let rawConfig = null;
13156
13411
  try {
@@ -13161,16 +13416,16 @@ var init_doctor = __esm({
13161
13416
  config = cm.get();
13162
13417
  } catch {
13163
13418
  }
13164
- const logsDir = config ? expandHome3(config.logging.logDir) : path31.join(dataDir, "logs");
13419
+ const logsDir = config ? expandHome3(config.logging.logDir) : path32.join(dataDir, "logs");
13165
13420
  return {
13166
13421
  config,
13167
13422
  rawConfig,
13168
13423
  configPath,
13169
13424
  dataDir,
13170
- sessionsPath: path31.join(dataDir, "sessions.json"),
13171
- pidPath: path31.join(dataDir, "openacp.pid"),
13172
- portFilePath: path31.join(dataDir, "api.port"),
13173
- pluginsDir: path31.join(dataDir, "plugins"),
13425
+ sessionsPath: path32.join(dataDir, "sessions.json"),
13426
+ pidPath: path32.join(dataDir, "openacp.pid"),
13427
+ portFilePath: path32.join(dataDir, "api.port"),
13428
+ pluginsDir: path32.join(dataDir, "plugins"),
13174
13429
  logsDir
13175
13430
  };
13176
13431
  }
@@ -16913,7 +17168,7 @@ __export(settings_manager_exports, {
16913
17168
  SettingsManager: () => SettingsManager
16914
17169
  });
16915
17170
  import fs32 from "fs";
16916
- import path32 from "path";
17171
+ import path33 from "path";
16917
17172
  var SettingsManager, SettingsAPIImpl;
16918
17173
  var init_settings_manager = __esm({
16919
17174
  "src/core/plugin/settings-manager.ts"() {
@@ -16950,7 +17205,7 @@ var init_settings_manager = __esm({
16950
17205
  };
16951
17206
  }
16952
17207
  getSettingsPath(pluginName) {
16953
- return path32.join(this.basePath, pluginName, "settings.json");
17208
+ return path33.join(this.basePath, pluginName, "settings.json");
16954
17209
  }
16955
17210
  async getPluginSettings(pluginName) {
16956
17211
  return this.loadSettings(pluginName);
@@ -16978,7 +17233,7 @@ var init_settings_manager = __esm({
16978
17233
  }
16979
17234
  }
16980
17235
  writeFile(data) {
16981
- const dir = path32.dirname(this.settingsPath);
17236
+ const dir = path33.dirname(this.settingsPath);
16982
17237
  fs32.mkdirSync(dir, { recursive: true });
16983
17238
  fs32.writeFileSync(this.settingsPath, JSON.stringify(data, null, 2));
16984
17239
  this.cache = data;
@@ -17077,10 +17332,10 @@ var install_context_exports = {};
17077
17332
  __export(install_context_exports, {
17078
17333
  createInstallContext: () => createInstallContext
17079
17334
  });
17080
- import path33 from "path";
17335
+ import path34 from "path";
17081
17336
  function createInstallContext(opts) {
17082
17337
  const { pluginName, settingsManager, basePath, legacyConfig, instanceRoot } = opts;
17083
- const dataDir = path33.join(basePath, pluginName, "data");
17338
+ const dataDir = path34.join(basePath, pluginName, "data");
17084
17339
  return {
17085
17340
  pluginName,
17086
17341
  terminal: createTerminalIO(),
@@ -17108,13 +17363,13 @@ __export(api_client_exports, {
17108
17363
  removeStalePortFile: () => removeStalePortFile
17109
17364
  });
17110
17365
  import * as fs33 from "fs";
17111
- import * as path34 from "path";
17112
- import * as os13 from "os";
17366
+ import * as path35 from "path";
17367
+ import * as os14 from "os";
17113
17368
  function defaultPortFile(root) {
17114
- return path34.join(root ?? DEFAULT_ROOT, "api.port");
17369
+ return path35.join(root ?? DEFAULT_ROOT, "api.port");
17115
17370
  }
17116
17371
  function defaultSecretFile(root) {
17117
- return path34.join(root ?? DEFAULT_ROOT, "api-secret");
17372
+ return path35.join(root ?? DEFAULT_ROOT, "api-secret");
17118
17373
  }
17119
17374
  function readApiPort(portFilePath, instanceRoot) {
17120
17375
  const filePath = portFilePath ?? defaultPortFile(instanceRoot);
@@ -17154,7 +17409,7 @@ var DEFAULT_ROOT;
17154
17409
  var init_api_client = __esm({
17155
17410
  "src/cli/api-client.ts"() {
17156
17411
  "use strict";
17157
- DEFAULT_ROOT = path34.join(os13.homedir(), ".openacp");
17412
+ DEFAULT_ROOT = path35.join(os14.homedir(), ".openacp");
17158
17413
  }
17159
17414
  });
17160
17415
 
@@ -17202,16 +17457,16 @@ var init_suggest = __esm({
17202
17457
 
17203
17458
  // src/cli/instance-hint.ts
17204
17459
  import fs34 from "fs";
17205
- import path35 from "path";
17206
- import os14 from "os";
17460
+ import path36 from "path";
17461
+ import os15 from "os";
17207
17462
  function printInstanceHint(root) {
17208
17463
  const globalRoot = getGlobalRoot();
17209
17464
  const isGlobal = root === globalRoot;
17210
- const displayPath = root.replace(os14.homedir(), "~");
17465
+ const displayPath = root.replace(os15.homedir(), "~");
17211
17466
  const label = isGlobal ? "global" : "local";
17212
17467
  console.log(` Workspace: ${label} \u2014 ${displayPath}`);
17213
17468
  if (isGlobal) {
17214
- const localRoot = path35.join(process.cwd(), ".openacp");
17469
+ const localRoot = path36.join(process.cwd(), ".openacp");
17215
17470
  if (fs34.existsSync(localRoot)) {
17216
17471
  console.log(` \x1B[2mhint: local workspace exists in current directory \u2014 use --local to use it\x1B[0m`);
17217
17472
  }
@@ -17243,22 +17498,22 @@ __export(daemon_exports, {
17243
17498
  });
17244
17499
  import { spawn as spawn6 } from "child_process";
17245
17500
  import * as fs35 from "fs";
17246
- import * as path36 from "path";
17247
- import * as os15 from "os";
17501
+ import * as path37 from "path";
17502
+ import * as os16 from "os";
17248
17503
  function getPidPath(root) {
17249
17504
  const base = root ?? DEFAULT_ROOT2;
17250
- return path36.join(base, "openacp.pid");
17505
+ return path37.join(base, "openacp.pid");
17251
17506
  }
17252
17507
  function getLogDir(root) {
17253
17508
  const base = root ?? DEFAULT_ROOT2;
17254
- return path36.join(base, "logs");
17509
+ return path37.join(base, "logs");
17255
17510
  }
17256
17511
  function getRunningMarker(root) {
17257
17512
  const base = root ?? DEFAULT_ROOT2;
17258
- return path36.join(base, "running");
17513
+ return path37.join(base, "running");
17259
17514
  }
17260
17515
  function writePidFile(pidPath, pid) {
17261
- const dir = path36.dirname(pidPath);
17516
+ const dir = path37.dirname(pidPath);
17262
17517
  fs35.mkdirSync(dir, { recursive: true });
17263
17518
  fs35.writeFileSync(pidPath, String(pid));
17264
17519
  }
@@ -17307,8 +17562,8 @@ function startDaemon(pidPath = getPidPath(), logDir2, instanceRoot) {
17307
17562
  }
17308
17563
  const resolvedLogDir = logDir2 ? expandHome3(logDir2) : getLogDir(instanceRoot);
17309
17564
  fs35.mkdirSync(resolvedLogDir, { recursive: true });
17310
- const logFile = path36.join(resolvedLogDir, "openacp.log");
17311
- const cliPath = path36.resolve(process.argv[1]);
17565
+ const logFile = path37.join(resolvedLogDir, "openacp.log");
17566
+ const cliPath = path37.resolve(process.argv[1]);
17312
17567
  const nodePath = process.execPath;
17313
17568
  const out = fs35.openSync(logFile, "a");
17314
17569
  const err = fs35.openSync(logFile, "a");
@@ -17392,7 +17647,7 @@ async function stopDaemon(pidPath = getPidPath(), instanceRoot) {
17392
17647
  }
17393
17648
  function markRunning(root) {
17394
17649
  const marker = getRunningMarker(root);
17395
- fs35.mkdirSync(path36.dirname(marker), { recursive: true });
17650
+ fs35.mkdirSync(path37.dirname(marker), { recursive: true });
17396
17651
  fs35.writeFileSync(marker, "");
17397
17652
  }
17398
17653
  function clearRunning(root) {
@@ -17409,7 +17664,7 @@ var init_daemon2 = __esm({
17409
17664
  "src/cli/daemon.ts"() {
17410
17665
  "use strict";
17411
17666
  init_config2();
17412
- DEFAULT_ROOT2 = path36.join(os15.homedir(), ".openacp");
17667
+ DEFAULT_ROOT2 = path37.join(os16.homedir(), ".openacp");
17413
17668
  }
17414
17669
  });
17415
17670
 
@@ -17418,12 +17673,12 @@ var stop_exports = {};
17418
17673
  __export(stop_exports, {
17419
17674
  cmdStop: () => cmdStop
17420
17675
  });
17421
- import path38 from "path";
17422
- import os17 from "os";
17676
+ import path39 from "path";
17677
+ import os18 from "os";
17423
17678
  async function cmdStop(args2 = [], instanceRoot) {
17424
17679
  const json = isJsonMode(args2);
17425
17680
  if (json) await muteForJson();
17426
- const root = instanceRoot ?? path38.join(os17.homedir(), ".openacp");
17681
+ const root = instanceRoot ?? path39.join(os18.homedir(), ".openacp");
17427
17682
  if (!json && wantsHelp(args2)) {
17428
17683
  console.log(`
17429
17684
  \x1B[1mopenacp stop\x1B[0m \u2014 Stop the background daemon
@@ -17765,7 +18020,7 @@ var init_mcp_manager = __esm({
17765
18020
 
17766
18021
  // src/core/utils/debug-tracer.ts
17767
18022
  import fs37 from "fs";
17768
- import path39 from "path";
18023
+ import path40 from "path";
17769
18024
  function createDebugTracer(sessionId, workingDirectory) {
17770
18025
  if (!DEBUG_ENABLED) return null;
17771
18026
  return new DebugTracer(sessionId, workingDirectory);
@@ -17779,7 +18034,7 @@ var init_debug_tracer = __esm({
17779
18034
  constructor(sessionId, workingDirectory) {
17780
18035
  this.sessionId = sessionId;
17781
18036
  this.workingDirectory = workingDirectory;
17782
- this.logDir = path39.join(workingDirectory, ".log");
18037
+ this.logDir = path40.join(workingDirectory, ".log");
17783
18038
  }
17784
18039
  dirCreated = false;
17785
18040
  logDir;
@@ -17789,7 +18044,7 @@ var init_debug_tracer = __esm({
17789
18044
  fs37.mkdirSync(this.logDir, { recursive: true });
17790
18045
  this.dirCreated = true;
17791
18046
  }
17792
- const filePath = path39.join(this.logDir, `${this.sessionId}_${layer}.jsonl`);
18047
+ const filePath = path40.join(this.logDir, `${this.sessionId}_${layer}.jsonl`);
17793
18048
  const seen = /* @__PURE__ */ new WeakSet();
17794
18049
  const line = JSON.stringify({ ts: Date.now(), ...data }, (_key, value) => {
17795
18050
  if (typeof value === "object" && value !== null) {
@@ -17813,16 +18068,16 @@ var init_debug_tracer = __esm({
17813
18068
  import { spawn as spawn8, execFileSync as execFileSync5 } from "child_process";
17814
18069
  import { Transform } from "stream";
17815
18070
  import fs38 from "fs";
17816
- import path40 from "path";
18071
+ import path41 from "path";
17817
18072
  import { ClientSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
17818
18073
  import { PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
17819
18074
  function findPackageRoot(startDir) {
17820
18075
  let dir = startDir;
17821
- while (dir !== path40.dirname(dir)) {
17822
- if (fs38.existsSync(path40.join(dir, "package.json"))) {
18076
+ while (dir !== path41.dirname(dir)) {
18077
+ if (fs38.existsSync(path41.join(dir, "package.json"))) {
17823
18078
  return dir;
17824
18079
  }
17825
- dir = path40.dirname(dir);
18080
+ dir = path41.dirname(dir);
17826
18081
  }
17827
18082
  return startDir;
17828
18083
  }
@@ -17834,8 +18089,8 @@ function resolveAgentCommand(cmd) {
17834
18089
  }
17835
18090
  for (const root of searchRoots) {
17836
18091
  const packageDirs = [
17837
- path40.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
17838
- path40.resolve(root, "node_modules", cmd, "dist", "index.js")
18092
+ path41.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
18093
+ path41.resolve(root, "node_modules", cmd, "dist", "index.js")
17839
18094
  ];
17840
18095
  for (const jsPath of packageDirs) {
17841
18096
  if (fs38.existsSync(jsPath)) {
@@ -17844,7 +18099,7 @@ function resolveAgentCommand(cmd) {
17844
18099
  }
17845
18100
  }
17846
18101
  for (const root of searchRoots) {
17847
- const localBin = path40.resolve(root, "node_modules", ".bin", cmd);
18102
+ const localBin = path41.resolve(root, "node_modules", ".bin", cmd);
17848
18103
  if (fs38.existsSync(localBin)) {
17849
18104
  const content = fs38.readFileSync(localBin, "utf-8");
17850
18105
  if (content.startsWith("#!/usr/bin/env node")) {
@@ -17852,7 +18107,7 @@ function resolveAgentCommand(cmd) {
17852
18107
  }
17853
18108
  const match = content.match(/"([^"]+\.js)"/);
17854
18109
  if (match) {
17855
- const target = path40.resolve(path40.dirname(localBin), match[1]);
18110
+ const target = path41.resolve(path41.dirname(localBin), match[1]);
17856
18111
  if (fs38.existsSync(target)) {
17857
18112
  return { command: process.execPath, args: [target] };
17858
18113
  }
@@ -18253,7 +18508,7 @@ ${stderr}`
18253
18508
  writePath = result.path;
18254
18509
  writeContent = result.content;
18255
18510
  }
18256
- await fs38.promises.mkdir(path40.dirname(writePath), { recursive: true });
18511
+ await fs38.promises.mkdir(path41.dirname(writePath), { recursive: true });
18257
18512
  await fs38.promises.writeFile(writePath, writeContent, "utf-8");
18258
18513
  return {};
18259
18514
  },
@@ -19865,7 +20120,7 @@ var init_message_transformer = __esm({
19865
20120
 
19866
20121
  // src/core/sessions/session-store.ts
19867
20122
  import fs40 from "fs";
19868
- import path41 from "path";
20123
+ import path42 from "path";
19869
20124
  var log30, DEBOUNCE_MS2, JsonFileSessionStore;
19870
20125
  var init_session_store = __esm({
19871
20126
  "src/core/sessions/session-store.ts"() {
@@ -19941,7 +20196,7 @@ var init_session_store = __esm({
19941
20196
  version: 1,
19942
20197
  sessions: Object.fromEntries(this.records)
19943
20198
  };
19944
- const dir = path41.dirname(this.filePath);
20199
+ const dir = path42.dirname(this.filePath);
19945
20200
  if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19946
20201
  fs40.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
19947
20202
  }
@@ -20516,8 +20771,8 @@ __export(agent_store_exports, {
20516
20771
  AgentStore: () => AgentStore
20517
20772
  });
20518
20773
  import * as fs41 from "fs";
20519
- import * as path42 from "path";
20520
- import * as os18 from "os";
20774
+ import * as path43 from "path";
20775
+ import * as os19 from "os";
20521
20776
  import { z as z7 } from "zod";
20522
20777
  var log33, InstalledAgentSchema, AgentStoreSchema, AgentStore;
20523
20778
  var init_agent_store = __esm({
@@ -20545,7 +20800,7 @@ var init_agent_store = __esm({
20545
20800
  data = { version: 1, installed: {} };
20546
20801
  filePath;
20547
20802
  constructor(filePath) {
20548
- this.filePath = filePath ?? path42.join(os18.homedir(), ".openacp", "agents.json");
20803
+ this.filePath = filePath ?? path43.join(os19.homedir(), ".openacp", "agents.json");
20549
20804
  }
20550
20805
  load() {
20551
20806
  if (!fs41.existsSync(this.filePath)) {
@@ -20587,7 +20842,7 @@ var init_agent_store = __esm({
20587
20842
  return key in this.data.installed;
20588
20843
  }
20589
20844
  save() {
20590
- fs41.mkdirSync(path42.dirname(this.filePath), { recursive: true });
20845
+ fs41.mkdirSync(path43.dirname(this.filePath), { recursive: true });
20591
20846
  const tmpPath = this.filePath + ".tmp";
20592
20847
  fs41.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2));
20593
20848
  fs41.renameSync(tmpPath, this.filePath);
@@ -20598,8 +20853,8 @@ var init_agent_store = __esm({
20598
20853
 
20599
20854
  // src/core/agents/agent-installer.ts
20600
20855
  import * as fs42 from "fs";
20601
- import * as path43 from "path";
20602
- import * as os19 from "os";
20856
+ import * as path44 from "path";
20857
+ import * as os20 from "os";
20603
20858
  function getPlatformKey() {
20604
20859
  const platform2 = PLATFORM_MAP[process.platform] ?? process.platform;
20605
20860
  const arch = ARCH_MAP[process.arch] ?? process.arch;
@@ -20650,7 +20905,7 @@ function buildInstalledAgent(registryId, name, version, dist, binaryPath) {
20650
20905
  binaryPath: null
20651
20906
  };
20652
20907
  }
20653
- const absCmd = path43.resolve(binaryPath, dist.cmd);
20908
+ const absCmd = path44.resolve(binaryPath, dist.cmd);
20654
20909
  return {
20655
20910
  registryId,
20656
20911
  name,
@@ -20723,7 +20978,7 @@ Install it with: pip install uv`;
20723
20978
  return { ok: true, agentKey, setupSteps: setup?.setupSteps };
20724
20979
  }
20725
20980
  async function downloadAndExtract(agentId, archiveUrl, progress, agentsDir) {
20726
- const destDir = path43.join(agentsDir ?? DEFAULT_AGENTS_DIR, agentId);
20981
+ const destDir = path44.join(agentsDir ?? DEFAULT_AGENTS_DIR, agentId);
20727
20982
  fs42.mkdirSync(destDir, { recursive: true });
20728
20983
  await progress?.onStep("Downloading...");
20729
20984
  log34.info({ agentId, url: archiveUrl }, "Downloading agent binary");
@@ -20767,15 +21022,15 @@ function validateExtractedPaths(destDir) {
20767
21022
  for (const entry of entries) {
20768
21023
  const dirent = entry;
20769
21024
  const parentPath = dirent.parentPath ?? dirent.path ?? destDir;
20770
- const fullPath = path43.join(parentPath, entry.name);
21025
+ const fullPath = path44.join(parentPath, entry.name);
20771
21026
  let realPath;
20772
21027
  try {
20773
21028
  realPath = fs42.realpathSync(fullPath);
20774
21029
  } catch {
20775
21030
  const linkTarget = fs42.readlinkSync(fullPath);
20776
- realPath = path43.resolve(path43.dirname(fullPath), linkTarget);
21031
+ realPath = path44.resolve(path44.dirname(fullPath), linkTarget);
20777
21032
  }
20778
- if (!realPath.startsWith(realDest + path43.sep) && realPath !== realDest) {
21033
+ if (!realPath.startsWith(realDest + path44.sep) && realPath !== realDest) {
20779
21034
  fs42.rmSync(destDir, { recursive: true, force: true });
20780
21035
  throw new Error(`Archive contains unsafe path: ${entry.name}`);
20781
21036
  }
@@ -20783,7 +21038,7 @@ function validateExtractedPaths(destDir) {
20783
21038
  }
20784
21039
  async function extractTarGz(buffer, destDir) {
20785
21040
  const { execFileSync: execFileSync8 } = await import("child_process");
20786
- const tmpFile = path43.join(destDir, "_archive.tar.gz");
21041
+ const tmpFile = path44.join(destDir, "_archive.tar.gz");
20787
21042
  fs42.writeFileSync(tmpFile, buffer);
20788
21043
  try {
20789
21044
  execFileSync8("tar", ["xzf", tmpFile, "-C", destDir], { stdio: "pipe" });
@@ -20794,7 +21049,7 @@ async function extractTarGz(buffer, destDir) {
20794
21049
  }
20795
21050
  async function extractZip(buffer, destDir) {
20796
21051
  const { execFileSync: execFileSync8 } = await import("child_process");
20797
- const tmpFile = path43.join(destDir, "_archive.zip");
21052
+ const tmpFile = path44.join(destDir, "_archive.zip");
20798
21053
  fs42.writeFileSync(tmpFile, buffer);
20799
21054
  try {
20800
21055
  execFileSync8("unzip", ["-o", tmpFile, "-d", destDir], { stdio: "pipe" });
@@ -20819,7 +21074,7 @@ var init_agent_installer = __esm({
20819
21074
  init_log();
20820
21075
  init_agent_dependencies();
20821
21076
  log34 = createChildLogger({ module: "agent-installer" });
20822
- DEFAULT_AGENTS_DIR = path43.join(os19.homedir(), ".openacp", "agents");
21077
+ DEFAULT_AGENTS_DIR = path44.join(os20.homedir(), ".openacp", "agents");
20823
21078
  ARCH_MAP = {
20824
21079
  arm64: "aarch64",
20825
21080
  x64: "x86_64"
@@ -20838,8 +21093,8 @@ __export(agent_catalog_exports, {
20838
21093
  AgentCatalog: () => AgentCatalog
20839
21094
  });
20840
21095
  import * as fs43 from "fs";
20841
- import * as path44 from "path";
20842
- import * as os20 from "os";
21096
+ import * as path45 from "path";
21097
+ import * as os21 from "os";
20843
21098
  var log35, REGISTRY_URL2, DEFAULT_TTL_HOURS, AgentCatalog;
20844
21099
  var init_agent_catalog = __esm({
20845
21100
  "src/core/agents/agent-catalog.ts"() {
@@ -20858,7 +21113,7 @@ var init_agent_catalog = __esm({
20858
21113
  agentsDir;
20859
21114
  constructor(store, cachePath, agentsDir) {
20860
21115
  this.store = store ?? new AgentStore();
20861
- this.cachePath = cachePath ?? path44.join(os20.homedir(), ".openacp", "registry-cache.json");
21116
+ this.cachePath = cachePath ?? path45.join(os21.homedir(), ".openacp", "registry-cache.json");
20862
21117
  this.agentsDir = agentsDir;
20863
21118
  }
20864
21119
  load() {
@@ -20879,7 +21134,7 @@ var init_agent_catalog = __esm({
20879
21134
  ttlHours: DEFAULT_TTL_HOURS,
20880
21135
  data
20881
21136
  };
20882
- fs43.mkdirSync(path44.dirname(this.cachePath), { recursive: true });
21137
+ fs43.mkdirSync(path45.dirname(this.cachePath), { recursive: true });
20883
21138
  fs43.writeFileSync(this.cachePath, JSON.stringify(cache, null, 2));
20884
21139
  log35.info({ count: this.registryAgents.length }, "Registry updated");
20885
21140
  } catch (err) {
@@ -21065,9 +21320,9 @@ var init_agent_catalog = __esm({
21065
21320
  }
21066
21321
  try {
21067
21322
  const candidates = [
21068
- path44.join(import.meta.dirname, "data", "registry-snapshot.json"),
21069
- path44.join(import.meta.dirname, "..", "data", "registry-snapshot.json"),
21070
- path44.join(import.meta.dirname, "..", "..", "data", "registry-snapshot.json")
21323
+ path45.join(import.meta.dirname, "data", "registry-snapshot.json"),
21324
+ path45.join(import.meta.dirname, "..", "data", "registry-snapshot.json"),
21325
+ path45.join(import.meta.dirname, "..", "..", "data", "registry-snapshot.json")
21071
21326
  ];
21072
21327
  for (const candidate of candidates) {
21073
21328
  if (fs43.existsSync(candidate)) {
@@ -21365,7 +21620,7 @@ var init_error_tracker = __esm({
21365
21620
 
21366
21621
  // src/core/plugin/plugin-storage.ts
21367
21622
  import fs44 from "fs";
21368
- import path45 from "path";
21623
+ import path46 from "path";
21369
21624
  var PluginStorageImpl;
21370
21625
  var init_plugin_storage = __esm({
21371
21626
  "src/core/plugin/plugin-storage.ts"() {
@@ -21375,8 +21630,8 @@ var init_plugin_storage = __esm({
21375
21630
  dataDir;
21376
21631
  writeChain = Promise.resolve();
21377
21632
  constructor(baseDir) {
21378
- this.dataDir = path45.join(baseDir, "data");
21379
- this.kvPath = path45.join(baseDir, "kv.json");
21633
+ this.dataDir = path46.join(baseDir, "data");
21634
+ this.kvPath = path46.join(baseDir, "kv.json");
21380
21635
  fs44.mkdirSync(baseDir, { recursive: true });
21381
21636
  }
21382
21637
  readKv() {
@@ -21422,8 +21677,8 @@ var init_plugin_storage = __esm({
21422
21677
  });
21423
21678
 
21424
21679
  // src/core/plugin/plugin-context.ts
21425
- import path46 from "path";
21426
- import os21 from "os";
21680
+ import path47 from "path";
21681
+ import os22 from "os";
21427
21682
  function requirePermission(permissions, required, action) {
21428
21683
  if (!permissions.includes(required)) {
21429
21684
  throw new Error(`Plugin does not have '${required}' permission required for ${action}`);
@@ -21442,7 +21697,7 @@ function createPluginContext(opts) {
21442
21697
  config,
21443
21698
  core
21444
21699
  } = opts;
21445
- const instanceRoot = opts.instanceRoot ?? path46.join(os21.homedir(), ".openacp");
21700
+ const instanceRoot = opts.instanceRoot ?? path47.join(os22.homedir(), ".openacp");
21446
21701
  const registeredListeners = [];
21447
21702
  const registeredCommands = [];
21448
21703
  const noopLog = {
@@ -21665,7 +21920,7 @@ var init_lifecycle_manager = __esm({
21665
21920
  log;
21666
21921
  settingsManager;
21667
21922
  pluginRegistry;
21668
- instanceRoot;
21923
+ _instanceRoot;
21669
21924
  contexts = /* @__PURE__ */ new Map();
21670
21925
  loadOrder = [];
21671
21926
  _loaded = /* @__PURE__ */ new Set();
@@ -21679,6 +21934,14 @@ var init_lifecycle_manager = __esm({
21679
21934
  get registry() {
21680
21935
  return this.pluginRegistry;
21681
21936
  }
21937
+ /** Plugin definitions currently in load order (loaded + failed). */
21938
+ get plugins() {
21939
+ return [...this.loadOrder];
21940
+ }
21941
+ /** Root directory of this OpenACP instance (e.g. ~/.openacp). */
21942
+ get instanceRoot() {
21943
+ return this._instanceRoot;
21944
+ }
21682
21945
  constructor(opts) {
21683
21946
  this.serviceRegistry = opts?.serviceRegistry ?? new ServiceRegistry();
21684
21947
  this.middlewareChain = opts?.middlewareChain ?? new MiddlewareChain();
@@ -21698,7 +21961,7 @@ var init_lifecycle_manager = __esm({
21698
21961
  this.log = opts?.log;
21699
21962
  this.settingsManager = opts?.settingsManager;
21700
21963
  this.pluginRegistry = opts?.pluginRegistry;
21701
- this.instanceRoot = opts?.instanceRoot;
21964
+ this._instanceRoot = opts?.instanceRoot;
21702
21965
  }
21703
21966
  getPluginLogger(pluginName) {
21704
21967
  if (this.log && typeof this.log.child === "function") {
@@ -21809,7 +22072,7 @@ var init_lifecycle_manager = __esm({
21809
22072
  config: this.config,
21810
22073
  core: this.core,
21811
22074
  log: this.log,
21812
- instanceRoot: this.instanceRoot
22075
+ instanceRoot: this._instanceRoot
21813
22076
  });
21814
22077
  try {
21815
22078
  await withTimeout(plugin2.setup(ctx), SETUP_TIMEOUT_MS, `${plugin2.name}.setup()`);
@@ -22285,8 +22548,8 @@ var init_core_items = __esm({
22285
22548
  });
22286
22549
 
22287
22550
  // src/core/core.ts
22288
- import path47 from "path";
22289
- import os22 from "os";
22551
+ import path48 from "path";
22552
+ import os23 from "os";
22290
22553
  var log39, OpenACPCore;
22291
22554
  var init_core = __esm({
22292
22555
  "src/core/core.ts"() {
@@ -22354,6 +22617,9 @@ var init_core = __esm({
22354
22617
  get contextManager() {
22355
22618
  return this.getService("context");
22356
22619
  }
22620
+ get settingsManager() {
22621
+ return this.lifecycleManager.settingsManager;
22622
+ }
22357
22623
  constructor(configManager, ctx) {
22358
22624
  this.configManager = configManager;
22359
22625
  this.instanceContext = ctx;
@@ -22365,7 +22631,7 @@ var init_core = __esm({
22365
22631
  );
22366
22632
  this.agentCatalog.load();
22367
22633
  this.agentManager = new AgentManager(this.agentCatalog);
22368
- const storePath = ctx?.paths.sessions ?? path47.join(os22.homedir(), ".openacp", "sessions.json");
22634
+ const storePath = ctx?.paths.sessions ?? path48.join(os23.homedir(), ".openacp", "sessions.json");
22369
22635
  this.sessionStore = new JsonFileSessionStore(
22370
22636
  storePath,
22371
22637
  config.sessionStore.ttlDays
@@ -22389,7 +22655,7 @@ var init_core = __esm({
22389
22655
  sessions: this.sessionManager,
22390
22656
  config: this.configManager,
22391
22657
  core: this,
22392
- storagePath: ctx?.paths.pluginsData ?? path47.join(os22.homedir(), ".openacp", "plugins", "data"),
22658
+ storagePath: ctx?.paths.pluginsData ?? path48.join(os23.homedir(), ".openacp", "plugins", "data"),
22393
22659
  instanceRoot: ctx?.root,
22394
22660
  log: createChildLogger({ module: "plugin" })
22395
22661
  });
@@ -22421,22 +22687,36 @@ var init_core = __esm({
22421
22687
  log39.info({ level: value }, "Log level changed at runtime");
22422
22688
  }
22423
22689
  if (configPath.startsWith("speech.")) {
22424
- const speechSvc = this.speechService;
22690
+ const speechSvc = this.lifecycleManager.serviceRegistry.get("speech");
22425
22691
  if (speechSvc) {
22426
- const newConfig = this.configManager.get();
22427
- const newSpeechConfig = newConfig.speech ?? {
22428
- stt: { provider: null, providers: {} },
22429
- tts: { provider: null, providers: {} }
22430
- };
22431
- speechSvc.refreshProviders(newSpeechConfig);
22432
- log39.info("Speech service config updated at runtime");
22692
+ const settingsMgr = this.settingsManager;
22693
+ if (settingsMgr) {
22694
+ const pluginCfg = await settingsMgr.loadSettings("@openacp/speech");
22695
+ const groqApiKey = pluginCfg.groqApiKey;
22696
+ const sttProviders = {};
22697
+ if (groqApiKey) {
22698
+ sttProviders.groq = { apiKey: groqApiKey };
22699
+ }
22700
+ const newSpeechConfig = {
22701
+ stt: {
22702
+ provider: groqApiKey ? "groq" : null,
22703
+ providers: sttProviders
22704
+ },
22705
+ tts: {
22706
+ provider: pluginCfg.ttsProvider ?? null,
22707
+ providers: {}
22708
+ }
22709
+ };
22710
+ speechSvc.refreshProviders(newSpeechConfig);
22711
+ log39.info("Speech service config updated at runtime (from plugin settings)");
22712
+ }
22433
22713
  }
22434
22714
  }
22435
22715
  }
22436
22716
  );
22437
22717
  registerCoreMenuItems(this.menuRegistry);
22438
22718
  if (ctx?.root) {
22439
- this.assistantRegistry.setInstanceRoot(path47.dirname(ctx.root));
22719
+ this.assistantRegistry.setInstanceRoot(path48.dirname(ctx.root));
22440
22720
  }
22441
22721
  this.assistantRegistry.register(createSessionsSection(this));
22442
22722
  this.assistantRegistry.register(createAgentsSection(this));
@@ -22530,7 +22810,7 @@ var init_core = __esm({
22530
22810
  if (!result) return;
22531
22811
  message = result;
22532
22812
  }
22533
- const access2 = this.securityGuard.checkAccess(message);
22813
+ const access2 = await this.securityGuard.checkAccess(message);
22534
22814
  if (!access2.allowed) {
22535
22815
  log39.warn({ userId: message.userId, reason: access2.reason }, "Access denied");
22536
22816
  if (access2.reason.includes("Session limit")) {
@@ -23993,8 +24273,8 @@ __export(autostart_exports, {
23993
24273
  });
23994
24274
  import { execFileSync as execFileSync7 } from "child_process";
23995
24275
  import * as fs45 from "fs";
23996
- import * as path48 from "path";
23997
- import * as os23 from "os";
24276
+ import * as path49 from "path";
24277
+ import * as os24 from "os";
23998
24278
  function isAutoStartSupported() {
23999
24279
  return process.platform === "darwin" || process.platform === "linux";
24000
24280
  }
@@ -24006,7 +24286,7 @@ function escapeSystemdValue(str) {
24006
24286
  return `"${escaped}"`;
24007
24287
  }
24008
24288
  function generateLaunchdPlist(nodePath, cliPath, logDir2) {
24009
- const logFile = path48.join(logDir2, "openacp.log");
24289
+ const logFile = path49.join(logDir2, "openacp.log");
24010
24290
  return `<?xml version="1.0" encoding="UTF-8"?>
24011
24291
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
24012
24292
  <plist version="1.0">
@@ -24051,12 +24331,12 @@ function installAutoStart(logDir2) {
24051
24331
  return { success: false, error: "Auto-start not supported on this platform" };
24052
24332
  }
24053
24333
  const nodePath = process.execPath;
24054
- const cliPath = path48.resolve(process.argv[1]);
24055
- const resolvedLogDir = logDir2.startsWith("~") ? path48.join(os23.homedir(), logDir2.slice(1)) : logDir2;
24334
+ const cliPath = path49.resolve(process.argv[1]);
24335
+ const resolvedLogDir = logDir2.startsWith("~") ? path49.join(os24.homedir(), logDir2.slice(1)) : logDir2;
24056
24336
  try {
24057
24337
  if (process.platform === "darwin") {
24058
24338
  const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir);
24059
- const dir = path48.dirname(LAUNCHD_PLIST_PATH);
24339
+ const dir = path49.dirname(LAUNCHD_PLIST_PATH);
24060
24340
  fs45.mkdirSync(dir, { recursive: true });
24061
24341
  fs45.writeFileSync(LAUNCHD_PLIST_PATH, plist);
24062
24342
  execFileSync7("launchctl", ["load", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
@@ -24065,7 +24345,7 @@ function installAutoStart(logDir2) {
24065
24345
  }
24066
24346
  if (process.platform === "linux") {
24067
24347
  const unit = generateSystemdUnit(nodePath, cliPath);
24068
- const dir = path48.dirname(SYSTEMD_SERVICE_PATH);
24348
+ const dir = path49.dirname(SYSTEMD_SERVICE_PATH);
24069
24349
  fs45.mkdirSync(dir, { recursive: true });
24070
24350
  fs45.writeFileSync(SYSTEMD_SERVICE_PATH, unit);
24071
24351
  execFileSync7("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
@@ -24131,8 +24411,8 @@ var init_autostart = __esm({
24131
24411
  init_log();
24132
24412
  log40 = createChildLogger({ module: "autostart" });
24133
24413
  LAUNCHD_LABEL = "com.openacp.daemon";
24134
- LAUNCHD_PLIST_PATH = path48.join(os23.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
24135
- SYSTEMD_SERVICE_PATH = path48.join(os23.homedir(), ".config", "systemd", "user", "openacp.service");
24414
+ LAUNCHD_PLIST_PATH = path49.join(os24.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
24415
+ SYSTEMD_SERVICE_PATH = path49.join(os24.homedir(), ".config", "systemd", "user", "openacp.service");
24136
24416
  }
24137
24417
  });
24138
24418
 
@@ -24251,7 +24531,7 @@ var init_setup_integrations = __esm({
24251
24531
  });
24252
24532
 
24253
24533
  // src/core/setup/setup-channels.ts
24254
- import * as path49 from "path";
24534
+ import * as path50 from "path";
24255
24535
  import * as clack7 from "@clack/prompts";
24256
24536
  function getChannelStatuses(config) {
24257
24537
  const statuses = [];
@@ -24317,7 +24597,7 @@ async function configureViaPlugin(channelId) {
24317
24597
  if (plugin2?.configure) {
24318
24598
  const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
24319
24599
  const { createInstallContext: createInstallContext2 } = await Promise.resolve().then(() => (init_install_context(), install_context_exports));
24320
- const basePath = path49.join(getGlobalRoot(), "plugins", "data");
24600
+ const basePath = path50.join(getGlobalRoot(), "plugins", "data");
24321
24601
  const settingsManager = new SettingsManager2(basePath);
24322
24602
  const ctx = createInstallContext2({
24323
24603
  pluginName: plugin2.name,
@@ -24399,55 +24679,70 @@ __export(instance_copy_exports, {
24399
24679
  copyInstance: () => copyInstance
24400
24680
  });
24401
24681
  import fs46 from "fs";
24402
- import path50 from "path";
24682
+ import path51 from "path";
24403
24683
  async function copyInstance(src, dst, opts) {
24404
24684
  const { inheritableKeys = {}, onProgress } = opts;
24405
24685
  fs46.mkdirSync(dst, { recursive: true });
24406
- const configSrc = path50.join(src, "config.json");
24686
+ const configSrc = path51.join(src, "config.json");
24407
24687
  if (fs46.existsSync(configSrc)) {
24408
24688
  onProgress?.("Configuration", "start");
24409
24689
  const config = JSON.parse(fs46.readFileSync(configSrc, "utf-8"));
24410
24690
  delete config.instanceName;
24411
- if (config.api) delete config.api.port;
24412
- if (config.tunnel) delete config.tunnel.port;
24413
- fs46.writeFileSync(path50.join(dst, "config.json"), JSON.stringify(config, null, 2));
24691
+ delete config.security;
24692
+ delete config.tunnel;
24693
+ delete config.api;
24694
+ delete config.speech;
24695
+ delete config.usage;
24696
+ if (config.channels && typeof config.channels === "object") {
24697
+ const CORE_CHANNEL_KEYS = /* @__PURE__ */ new Set(["enabled", "outputMode", "adapter", "displayVerbosity"]);
24698
+ for (const ch of Object.values(config.channels)) {
24699
+ if (ch && typeof ch === "object") {
24700
+ for (const key of Object.keys(ch)) {
24701
+ if (!CORE_CHANNEL_KEYS.has(key)) {
24702
+ delete ch[key];
24703
+ }
24704
+ }
24705
+ }
24706
+ }
24707
+ }
24708
+ fs46.writeFileSync(path51.join(dst, "config.json"), JSON.stringify(config, null, 2));
24414
24709
  onProgress?.("Configuration", "done");
24415
24710
  }
24416
- const pluginsSrc = path50.join(src, "plugins.json");
24711
+ const pluginsSrc = path51.join(src, "plugins.json");
24417
24712
  if (fs46.existsSync(pluginsSrc)) {
24418
24713
  onProgress?.("Plugin list", "start");
24419
- fs46.copyFileSync(pluginsSrc, path50.join(dst, "plugins.json"));
24714
+ fs46.copyFileSync(pluginsSrc, path51.join(dst, "plugins.json"));
24420
24715
  onProgress?.("Plugin list", "done");
24421
24716
  }
24422
- const pluginsDir = path50.join(src, "plugins");
24717
+ const pluginsDir = path51.join(src, "plugins");
24423
24718
  if (fs46.existsSync(pluginsDir)) {
24424
24719
  onProgress?.("Plugins", "start");
24425
- const dstPlugins = path50.join(dst, "plugins");
24720
+ const dstPlugins = path51.join(dst, "plugins");
24426
24721
  fs46.mkdirSync(dstPlugins, { recursive: true });
24427
- const pkgJson = path50.join(pluginsDir, "package.json");
24428
- if (fs46.existsSync(pkgJson)) fs46.copyFileSync(pkgJson, path50.join(dstPlugins, "package.json"));
24429
- const nodeModules = path50.join(pluginsDir, "node_modules");
24430
- if (fs46.existsSync(nodeModules)) fs46.cpSync(nodeModules, path50.join(dstPlugins, "node_modules"), { recursive: true });
24722
+ const pkgJson = path51.join(pluginsDir, "package.json");
24723
+ if (fs46.existsSync(pkgJson)) fs46.copyFileSync(pkgJson, path51.join(dstPlugins, "package.json"));
24724
+ const nodeModules = path51.join(pluginsDir, "node_modules");
24725
+ if (fs46.existsSync(nodeModules)) fs46.cpSync(nodeModules, path51.join(dstPlugins, "node_modules"), { recursive: true });
24431
24726
  onProgress?.("Plugins", "done");
24432
24727
  }
24433
- const agentsJson = path50.join(src, "agents.json");
24728
+ const agentsJson = path51.join(src, "agents.json");
24434
24729
  if (fs46.existsSync(agentsJson)) {
24435
24730
  onProgress?.("Agents", "start");
24436
- fs46.copyFileSync(agentsJson, path50.join(dst, "agents.json"));
24437
- const agentsDir = path50.join(src, "agents");
24438
- if (fs46.existsSync(agentsDir)) fs46.cpSync(agentsDir, path50.join(dst, "agents"), { recursive: true });
24731
+ fs46.copyFileSync(agentsJson, path51.join(dst, "agents.json"));
24732
+ const agentsDir = path51.join(src, "agents");
24733
+ if (fs46.existsSync(agentsDir)) fs46.cpSync(agentsDir, path51.join(dst, "agents"), { recursive: true });
24439
24734
  onProgress?.("Agents", "done");
24440
24735
  }
24441
- const binDir = path50.join(src, "bin");
24736
+ const binDir = path51.join(src, "bin");
24442
24737
  if (fs46.existsSync(binDir)) {
24443
24738
  onProgress?.("Tools", "start");
24444
- fs46.cpSync(binDir, path50.join(dst, "bin"), { recursive: true });
24739
+ fs46.cpSync(binDir, path51.join(dst, "bin"), { recursive: true });
24445
24740
  onProgress?.("Tools", "done");
24446
24741
  }
24447
- const pluginDataSrc = path50.join(src, "plugins", "data");
24742
+ const pluginDataSrc = path51.join(src, "plugins", "data");
24448
24743
  if (fs46.existsSync(pluginDataSrc)) {
24449
24744
  onProgress?.("Preferences", "start");
24450
- copyPluginSettings(pluginDataSrc, path50.join(dst, "plugins", "data"), inheritableKeys);
24745
+ copyPluginSettings(pluginDataSrc, path51.join(dst, "plugins", "data"), inheritableKeys);
24451
24746
  onProgress?.("Preferences", "done");
24452
24747
  }
24453
24748
  }
@@ -24462,10 +24757,10 @@ function copyPluginSettings(srcData, dstData, inheritableKeys) {
24462
24757
  if (key in settings) filtered[key] = settings[key];
24463
24758
  }
24464
24759
  if (Object.keys(filtered).length > 0) {
24465
- const relative = path50.relative(srcData, path50.dirname(settingsPath));
24466
- const dstDir = path50.join(dstData, relative);
24760
+ const relative = path51.relative(srcData, path51.dirname(settingsPath));
24761
+ const dstDir = path51.join(dstData, relative);
24467
24762
  fs46.mkdirSync(dstDir, { recursive: true });
24468
- fs46.writeFileSync(path50.join(dstDir, "settings.json"), JSON.stringify(filtered, null, 2));
24763
+ fs46.writeFileSync(path51.join(dstDir, "settings.json"), JSON.stringify(filtered, null, 2));
24469
24764
  }
24470
24765
  } catch {
24471
24766
  }
@@ -24476,15 +24771,15 @@ function walkPluginDirs(base, cb) {
24476
24771
  for (const entry of fs46.readdirSync(base, { withFileTypes: true })) {
24477
24772
  if (!entry.isDirectory()) continue;
24478
24773
  if (entry.name.startsWith("@")) {
24479
- const scopeDir = path50.join(base, entry.name);
24774
+ const scopeDir = path51.join(base, entry.name);
24480
24775
  for (const sub of fs46.readdirSync(scopeDir, { withFileTypes: true })) {
24481
24776
  if (!sub.isDirectory()) continue;
24482
24777
  const pluginName = `${entry.name}/${sub.name}`;
24483
- const settingsPath = path50.join(scopeDir, sub.name, "settings.json");
24778
+ const settingsPath = path51.join(scopeDir, sub.name, "settings.json");
24484
24779
  if (fs46.existsSync(settingsPath)) cb(pluginName, settingsPath);
24485
24780
  }
24486
24781
  } else {
24487
- const settingsPath = path50.join(base, entry.name, "settings.json");
24782
+ const settingsPath = path51.join(base, entry.name, "settings.json");
24488
24783
  if (fs46.existsSync(settingsPath)) cb(entry.name, settingsPath);
24489
24784
  }
24490
24785
  }
@@ -24497,14 +24792,14 @@ var init_instance_copy = __esm({
24497
24792
 
24498
24793
  // src/core/setup/git-protect.ts
24499
24794
  import fs47 from "fs";
24500
- import path51 from "path";
24795
+ import path52 from "path";
24501
24796
  function protectLocalInstance(projectDir) {
24502
24797
  ensureGitignore(projectDir);
24503
24798
  ensureClaudeMd(projectDir);
24504
24799
  printSecurityWarning();
24505
24800
  }
24506
24801
  function ensureGitignore(projectDir) {
24507
- const gitignorePath = path51.join(projectDir, ".gitignore");
24802
+ const gitignorePath = path52.join(projectDir, ".gitignore");
24508
24803
  const entry = ".openacp";
24509
24804
  if (fs47.existsSync(gitignorePath)) {
24510
24805
  const content = fs47.readFileSync(gitignorePath, "utf-8");
@@ -24524,7 +24819,7 @@ ${entry}
24524
24819
  }
24525
24820
  }
24526
24821
  function ensureClaudeMd(projectDir) {
24527
- const claudeMdPath = path51.join(projectDir, "CLAUDE.md");
24822
+ const claudeMdPath = path52.join(projectDir, "CLAUDE.md");
24528
24823
  const marker = "## Local OpenACP Workspace";
24529
24824
  if (fs47.existsSync(claudeMdPath)) {
24530
24825
  const content = fs47.readFileSync(claudeMdPath, "utf-8");
@@ -24567,9 +24862,9 @@ var init_git_protect = __esm({
24567
24862
  });
24568
24863
 
24569
24864
  // src/core/setup/wizard.ts
24570
- import * as path52 from "path";
24865
+ import * as path53 from "path";
24571
24866
  import * as fs48 from "fs";
24572
- import * as os24 from "os";
24867
+ import * as os25 from "os";
24573
24868
  import * as clack8 from "@clack/prompts";
24574
24869
  import { randomUUID as randomUUID2 } from "crypto";
24575
24870
  async function fetchCommunityAdapters() {
@@ -24591,7 +24886,7 @@ function checkDesktopAppInstalled() {
24591
24886
  const platform2 = process.platform;
24592
24887
  if (platform2 === "darwin") {
24593
24888
  candidates.push("/Applications/OpenACP.app");
24594
- candidates.push(`${os24.homedir()}/Applications/OpenACP.app`);
24889
+ candidates.push(`${os25.homedir()}/Applications/OpenACP.app`);
24595
24890
  } else if (platform2 === "win32") {
24596
24891
  const localAppData = process.env["LOCALAPPDATA"] ?? "";
24597
24892
  candidates.push(`${localAppData}\\Programs\\OpenACP\\OpenACP.exe`);
@@ -24599,7 +24894,7 @@ function checkDesktopAppInstalled() {
24599
24894
  } else {
24600
24895
  candidates.push("/usr/bin/openacp-desktop");
24601
24896
  candidates.push("/usr/local/bin/openacp-desktop");
24602
- candidates.push(`${os24.homedir()}/.local/bin/openacp-desktop`);
24897
+ candidates.push(`${os25.homedir()}/.local/bin/openacp-desktop`);
24603
24898
  }
24604
24899
  return candidates.some((p2) => fs48.existsSync(p2));
24605
24900
  }
@@ -24616,8 +24911,8 @@ async function runSetup(configManager, opts) {
24616
24911
  const isGlobal = instanceRoot === getGlobalRoot();
24617
24912
  let instanceName = opts?.instanceName;
24618
24913
  if (!instanceName) {
24619
- const defaultName = isGlobal ? "Global workspace" : path52.basename(path52.dirname(instanceRoot));
24620
- const locationHint = isGlobal ? "global (~/.openacp)" : `local (${instanceRoot.replace(/\/.openacp$/, "").replace(os24.homedir(), "~")})`;
24914
+ const defaultName = isGlobal ? "Global workspace" : path53.basename(path53.dirname(instanceRoot));
24915
+ const locationHint = isGlobal ? "global (~/.openacp)" : `local (${instanceRoot.replace(/\/.openacp$/, "").replace(os25.homedir(), "~")})`;
24621
24916
  const nameResult = await clack8.text({
24622
24917
  message: `Name for this workspace (${locationHint})`,
24623
24918
  initialValue: defaultName,
@@ -24627,13 +24922,13 @@ async function runSetup(configManager, opts) {
24627
24922
  instanceName = nameResult.trim();
24628
24923
  }
24629
24924
  const globalRoot = getGlobalRoot();
24630
- const registryPath = path52.join(globalRoot, "instances.json");
24925
+ const registryPath = path53.join(globalRoot, "instances.json");
24631
24926
  const instanceRegistry = new InstanceRegistry(registryPath);
24632
24927
  await instanceRegistry.load();
24633
24928
  let didCopy = false;
24634
24929
  if (opts?.from) {
24635
- const fromRoot = path52.join(opts.from, ".openacp");
24636
- if (fs48.existsSync(path52.join(fromRoot, "config.json"))) {
24930
+ const fromRoot = path53.join(opts.from, ".openacp");
24931
+ if (fs48.existsSync(path53.join(fromRoot, "config.json"))) {
24637
24932
  const inheritableMap = buildInheritableKeysMap();
24638
24933
  await copyInstance(fromRoot, instanceRoot, { inheritableKeys: inheritableMap });
24639
24934
  didCopy = true;
@@ -24644,7 +24939,7 @@ async function runSetup(configManager, opts) {
24644
24939
  }
24645
24940
  if (!didCopy) {
24646
24941
  const existingInstances = instanceRegistry.list().filter(
24647
- (e) => fs48.existsSync(path52.join(e.root, "config.json")) && e.root !== instanceRoot
24942
+ (e) => fs48.existsSync(path53.join(e.root, "config.json")) && e.root !== instanceRoot
24648
24943
  );
24649
24944
  if (existingInstances.length > 0) {
24650
24945
  let singleLabel;
@@ -24652,12 +24947,12 @@ async function runSetup(configManager, opts) {
24652
24947
  const e = existingInstances[0];
24653
24948
  let name = e.id;
24654
24949
  try {
24655
- const cfg = JSON.parse(fs48.readFileSync(path52.join(e.root, "config.json"), "utf-8"));
24950
+ const cfg = JSON.parse(fs48.readFileSync(path53.join(e.root, "config.json"), "utf-8"));
24656
24951
  if (cfg.instanceName) name = cfg.instanceName;
24657
24952
  } catch {
24658
24953
  }
24659
24954
  const isGlobalEntry = e.root === getGlobalRoot();
24660
- const displayPath = e.root.replace(os24.homedir(), "~");
24955
+ const displayPath = e.root.replace(os25.homedir(), "~");
24661
24956
  const type = isGlobalEntry ? "global" : "local";
24662
24957
  singleLabel = `${name} workspace (${type} \u2014 ${displayPath})`;
24663
24958
  }
@@ -24677,12 +24972,12 @@ async function runSetup(configManager, opts) {
24677
24972
  options: existingInstances.map((e) => {
24678
24973
  let name = e.id;
24679
24974
  try {
24680
- const cfg = JSON.parse(fs48.readFileSync(path52.join(e.root, "config.json"), "utf-8"));
24975
+ const cfg = JSON.parse(fs48.readFileSync(path53.join(e.root, "config.json"), "utf-8"));
24681
24976
  if (cfg.instanceName) name = cfg.instanceName;
24682
24977
  } catch {
24683
24978
  }
24684
24979
  const isGlobalEntry = e.root === getGlobalRoot();
24685
- const displayPath = e.root.replace(os24.homedir(), "~");
24980
+ const displayPath = e.root.replace(os25.homedir(), "~");
24686
24981
  const type = isGlobalEntry ? "global" : "local";
24687
24982
  return { value: e.root, label: `${name} workspace (${type} \u2014 ${displayPath})` };
24688
24983
  })
@@ -24774,9 +25069,9 @@ async function runSetup(configManager, opts) {
24774
25069
  if (channelId.startsWith("official:")) {
24775
25070
  const npmPackage = channelId.slice("official:".length);
24776
25071
  const { execFileSync: execFileSync8 } = await import("child_process");
24777
- const pluginsDir = path52.join(instanceRoot, "plugins");
24778
- const nodeModulesDir = path52.join(pluginsDir, "node_modules");
24779
- const installedPath = path52.join(nodeModulesDir, npmPackage);
25072
+ const pluginsDir = path53.join(instanceRoot, "plugins");
25073
+ const nodeModulesDir = path53.join(pluginsDir, "node_modules");
25074
+ const installedPath = path53.join(nodeModulesDir, npmPackage);
24780
25075
  if (!fs48.existsSync(installedPath)) {
24781
25076
  try {
24782
25077
  clack8.log.step(`Installing ${npmPackage}...`);
@@ -24790,9 +25085,9 @@ async function runSetup(configManager, opts) {
24790
25085
  }
24791
25086
  }
24792
25087
  try {
24793
- const installedPkgPath = path52.join(nodeModulesDir, npmPackage, "package.json");
25088
+ const installedPkgPath = path53.join(nodeModulesDir, npmPackage, "package.json");
24794
25089
  const installedPkg = JSON.parse(fs48.readFileSync(installedPkgPath, "utf-8"));
24795
- const pluginModule = await import(path52.join(nodeModulesDir, npmPackage, installedPkg.main ?? "dist/index.js"));
25090
+ const pluginModule = await import(path53.join(nodeModulesDir, npmPackage, installedPkg.main ?? "dist/index.js"));
24796
25091
  const plugin2 = pluginModule.default;
24797
25092
  if (plugin2?.install) {
24798
25093
  const installCtx = createInstallContext2({
@@ -24822,8 +25117,8 @@ async function runSetup(configManager, opts) {
24822
25117
  if (channelId.startsWith("community:")) {
24823
25118
  const npmPackage = channelId.slice("community:".length);
24824
25119
  const { execFileSync: execFileSync8 } = await import("child_process");
24825
- const pluginsDir = path52.join(instanceRoot, "plugins");
24826
- const nodeModulesDir = path52.join(pluginsDir, "node_modules");
25120
+ const pluginsDir = path53.join(instanceRoot, "plugins");
25121
+ const nodeModulesDir = path53.join(pluginsDir, "node_modules");
24827
25122
  try {
24828
25123
  execFileSync8("npm", ["install", npmPackage, "--prefix", pluginsDir, "--save"], {
24829
25124
  stdio: "inherit",
@@ -24835,9 +25130,9 @@ async function runSetup(configManager, opts) {
24835
25130
  }
24836
25131
  try {
24837
25132
  const { readFileSync: readFileSync19 } = await import("fs");
24838
- const installedPkgPath = path52.join(nodeModulesDir, npmPackage, "package.json");
25133
+ const installedPkgPath = path53.join(nodeModulesDir, npmPackage, "package.json");
24839
25134
  const installedPkg = JSON.parse(readFileSync19(installedPkgPath, "utf-8"));
24840
- const pluginModule = await import(path52.join(nodeModulesDir, npmPackage, installedPkg.main ?? "dist/index.js"));
25135
+ const pluginModule = await import(path53.join(nodeModulesDir, npmPackage, installedPkg.main ?? "dist/index.js"));
24841
25136
  const plugin2 = pluginModule.default;
24842
25137
  if (plugin2?.install) {
24843
25138
  const installCtx = createInstallContext2({
@@ -24892,7 +25187,7 @@ async function runSetup(configManager, opts) {
24892
25187
  security,
24893
25188
  logging: {
24894
25189
  level: "info",
24895
- logDir: path52.join(instanceRoot, "logs"),
25190
+ logDir: path53.join(instanceRoot, "logs"),
24896
25191
  maxFileSize: "10m",
24897
25192
  maxFiles: 7,
24898
25193
  sessionLogRetentionDays: 30
@@ -24944,9 +25239,9 @@ async function runSetup(configManager, opts) {
24944
25239
  instanceRegistry.register(id, instanceRoot);
24945
25240
  await instanceRegistry.save();
24946
25241
  }
24947
- const isLocal = instanceRoot !== path52.join(getGlobalRoot());
25242
+ const isLocal = instanceRoot !== path53.join(getGlobalRoot());
24948
25243
  if (isLocal) {
24949
- const projectDir = path52.dirname(instanceRoot);
25244
+ const projectDir = path53.dirname(instanceRoot);
24950
25245
  protectLocalInstance(projectDir);
24951
25246
  }
24952
25247
  clack8.outro(`Config saved to ${configManager.getConfigPath()}`);
@@ -25234,7 +25529,7 @@ __export(dev_loader_exports, {
25234
25529
  DevPluginLoader: () => DevPluginLoader
25235
25530
  });
25236
25531
  import fs49 from "fs";
25237
- import path53 from "path";
25532
+ import path54 from "path";
25238
25533
  var loadCounter, DevPluginLoader;
25239
25534
  var init_dev_loader = __esm({
25240
25535
  "src/core/plugin/dev-loader.ts"() {
@@ -25243,11 +25538,11 @@ var init_dev_loader = __esm({
25243
25538
  DevPluginLoader = class {
25244
25539
  pluginPath;
25245
25540
  constructor(pluginPath) {
25246
- this.pluginPath = path53.resolve(pluginPath);
25541
+ this.pluginPath = path54.resolve(pluginPath);
25247
25542
  }
25248
25543
  async load() {
25249
- const distIndex = path53.join(this.pluginPath, "dist", "index.js");
25250
- const srcIndex = path53.join(this.pluginPath, "src", "index.ts");
25544
+ const distIndex = path54.join(this.pluginPath, "dist", "index.js");
25545
+ const srcIndex = path54.join(this.pluginPath, "src", "index.ts");
25251
25546
  if (!fs49.existsSync(distIndex) && !fs49.existsSync(srcIndex)) {
25252
25547
  throw new Error(`Plugin not found at ${this.pluginPath}. Expected dist/index.js or src/index.ts`);
25253
25548
  }
@@ -25266,7 +25561,7 @@ var init_dev_loader = __esm({
25266
25561
  return this.pluginPath;
25267
25562
  }
25268
25563
  getDistPath() {
25269
- return path53.join(this.pluginPath, "dist");
25564
+ return path54.join(this.pluginPath, "dist");
25270
25565
  }
25271
25566
  };
25272
25567
  }
@@ -25278,13 +25573,13 @@ __export(main_exports, {
25278
25573
  RESTART_EXIT_CODE: () => RESTART_EXIT_CODE,
25279
25574
  startServer: () => startServer
25280
25575
  });
25281
- import path54 from "path";
25576
+ import path55 from "path";
25282
25577
  import { randomUUID as randomUUID3 } from "crypto";
25283
25578
  import fs50 from "fs";
25284
25579
  async function startServer(opts) {
25285
25580
  const globalRoot = getGlobalRoot();
25286
25581
  if (!opts?.instanceContext) {
25287
- const reg = new InstanceRegistry(path54.join(globalRoot, "instances.json"));
25582
+ const reg = new InstanceRegistry(path55.join(globalRoot, "instances.json"));
25288
25583
  reg.load();
25289
25584
  const entry = reg.getByRoot(globalRoot);
25290
25585
  opts = { ...opts, instanceContext: createInstanceContext({ id: entry?.id ?? randomUUID3(), root: globalRoot, isGlobal: true }) };
@@ -25325,6 +25620,7 @@ async function startServer(opts) {
25325
25620
  const config = configManager.get();
25326
25621
  initLogger(config.logging);
25327
25622
  log.debug({ configPath: configManager.getConfigPath() }, "Config loaded");
25623
+ await configManager.applyEnvToPluginSettings(settingsManager);
25328
25624
  if (pluginRegistry.list().size === 0) {
25329
25625
  await autoRegisterBuiltinPlugins(settingsManager, pluginRegistry, configManager);
25330
25626
  }
@@ -25368,22 +25664,22 @@ async function startServer(opts) {
25368
25664
  try {
25369
25665
  let modulePath;
25370
25666
  if (name.startsWith("/") || name.startsWith(".")) {
25371
- const resolved = path54.resolve(name);
25372
- const pkgPath = path54.join(resolved, "package.json");
25667
+ const resolved = path55.resolve(name);
25668
+ const pkgPath = path55.join(resolved, "package.json");
25373
25669
  const pkg = JSON.parse(await fs50.promises.readFile(pkgPath, "utf-8"));
25374
- modulePath = path54.join(resolved, pkg.main || "dist/index.js");
25670
+ modulePath = path55.join(resolved, pkg.main || "dist/index.js");
25375
25671
  } else {
25376
- const nodeModulesDir = path54.join(ctx.paths.plugins, "node_modules");
25377
- let pkgDir = path54.join(nodeModulesDir, name);
25378
- if (!fs50.existsSync(path54.join(pkgDir, "package.json"))) {
25672
+ const nodeModulesDir = path55.join(ctx.paths.plugins, "node_modules");
25673
+ let pkgDir = path55.join(nodeModulesDir, name);
25674
+ if (!fs50.existsSync(path55.join(pkgDir, "package.json"))) {
25379
25675
  let found = false;
25380
25676
  const scopes = fs50.existsSync(nodeModulesDir) ? fs50.readdirSync(nodeModulesDir).filter((d) => d.startsWith("@")) : [];
25381
25677
  for (const scope of scopes) {
25382
- const scopeDir = path54.join(nodeModulesDir, scope);
25678
+ const scopeDir = path55.join(nodeModulesDir, scope);
25383
25679
  const pkgs = fs50.readdirSync(scopeDir);
25384
25680
  for (const pkg2 of pkgs) {
25385
- const candidateDir = path54.join(scopeDir, pkg2);
25386
- const candidatePkgPath = path54.join(candidateDir, "package.json");
25681
+ const candidateDir = path55.join(scopeDir, pkg2);
25682
+ const candidatePkgPath = path55.join(candidateDir, "package.json");
25387
25683
  if (fs50.existsSync(candidatePkgPath)) {
25388
25684
  try {
25389
25685
  const candidatePkg = JSON.parse(fs50.readFileSync(candidatePkgPath, "utf-8"));
@@ -25400,9 +25696,9 @@ async function startServer(opts) {
25400
25696
  if (found) break;
25401
25697
  }
25402
25698
  }
25403
- const pkgJsonPath = path54.join(pkgDir, "package.json");
25699
+ const pkgJsonPath = path55.join(pkgDir, "package.json");
25404
25700
  const pkg = JSON.parse(await fs50.promises.readFile(pkgJsonPath, "utf-8"));
25405
- modulePath = path54.join(pkgDir, pkg.main || "dist/index.js");
25701
+ modulePath = path55.join(pkgDir, pkg.main || "dist/index.js");
25406
25702
  }
25407
25703
  log.debug({ plugin: name, modulePath }, "Loading community plugin");
25408
25704
  const mod = await import(modulePath);
@@ -25565,7 +25861,7 @@ async function startServer(opts) {
25565
25861
  await core.start();
25566
25862
  try {
25567
25863
  const globalRoot2 = getGlobalRoot();
25568
- const registryPath = path54.join(globalRoot2, "instances.json");
25864
+ const registryPath = path55.join(globalRoot2, "instances.json");
25569
25865
  const instanceReg = new InstanceRegistry(registryPath);
25570
25866
  await instanceReg.load();
25571
25867
  if (!instanceReg.getByRoot(ctx.root)) {
@@ -25728,13 +26024,13 @@ var restart_exports = {};
25728
26024
  __export(restart_exports, {
25729
26025
  cmdRestart: () => cmdRestart
25730
26026
  });
25731
- import path55 from "path";
25732
- import os25 from "os";
26027
+ import path56 from "path";
26028
+ import os26 from "os";
25733
26029
  import { randomUUID as randomUUID4 } from "crypto";
25734
26030
  async function cmdRestart(args2 = [], instanceRoot) {
25735
26031
  const json = isJsonMode(args2);
25736
26032
  if (json) await muteForJson();
25737
- const root = instanceRoot ?? path55.join(os25.homedir(), ".openacp");
26033
+ const root = instanceRoot ?? path56.join(os26.homedir(), ".openacp");
25738
26034
  if (!json && wantsHelp(args2)) {
25739
26035
  console.log(`
25740
26036
  \x1B[1mopenacp restart\x1B[0m \u2014 Restart the background daemon
@@ -25782,7 +26078,7 @@ Stops the running daemon (if any) and starts a new one.
25782
26078
  printInstanceHint(root);
25783
26079
  console.log("Starting in foreground mode...");
25784
26080
  const { startServer: startServer2 } = await Promise.resolve().then(() => (init_main(), main_exports));
25785
- const reg = new InstanceRegistry(path55.join(getGlobalRoot(), "instances.json"));
26081
+ const reg = new InstanceRegistry(path56.join(getGlobalRoot(), "instances.json"));
25786
26082
  reg.load();
25787
26083
  const existingEntry = reg.getByRoot(root);
25788
26084
  const ctx = createInstanceContext({
@@ -25798,7 +26094,7 @@ Stops the running daemon (if any) and starts a new one.
25798
26094
  console.error(result.error);
25799
26095
  process.exit(1);
25800
26096
  }
25801
- if (json) jsonSuccess({ pid: result.pid, instanceId: path55.basename(root), dir: root });
26097
+ if (json) jsonSuccess({ pid: result.pid, instanceId: path56.basename(root), dir: root });
25802
26098
  printInstanceHint(root);
25803
26099
  console.log(`OpenACP daemon started (PID ${result.pid})`);
25804
26100
  }
@@ -25822,8 +26118,8 @@ __export(status_exports, {
25822
26118
  readInstanceInfo: () => readInstanceInfo
25823
26119
  });
25824
26120
  import fs51 from "fs";
25825
- import path56 from "path";
25826
- import os26 from "os";
26121
+ import path57 from "path";
26122
+ import os27 from "os";
25827
26123
  async function cmdStatus(args2 = [], instanceRoot) {
25828
26124
  const json = isJsonMode(args2);
25829
26125
  if (json) await muteForJson();
@@ -25840,7 +26136,7 @@ async function cmdStatus(args2 = [], instanceRoot) {
25840
26136
  await showSingleInstance(root, json);
25841
26137
  }
25842
26138
  async function showAllInstances(json = false) {
25843
- const registryPath = path56.join(getGlobalRoot(), "instances.json");
26139
+ const registryPath = path57.join(getGlobalRoot(), "instances.json");
25844
26140
  const registry = new InstanceRegistry(registryPath);
25845
26141
  await registry.load();
25846
26142
  const instances = registry.list();
@@ -25875,7 +26171,7 @@ async function showAllInstances(json = false) {
25875
26171
  const mode = info.pid ? info.runMode === "daemon" ? "daemon" : "fg" : "\u2014";
25876
26172
  const api = info.apiPort ? String(info.apiPort) : "\u2014";
25877
26173
  const tunnel = info.tunnelPort ? String(info.tunnelPort) : "\u2014";
25878
- const dir = entry.root.replace(/\/.openacp$/, "").replace(os26.homedir(), "~");
26174
+ const dir = entry.root.replace(/\/.openacp$/, "").replace(os27.homedir(), "~");
25879
26175
  const channels = info.channels.join(", ") || "\u2014";
25880
26176
  const name = info.name ?? entry.id;
25881
26177
  console.log(` ${status.padEnd(10)} ${entry.id.padEnd(16)} ${name.padEnd(16)} ${dir.padEnd(20)} ${mode.padEnd(8)} ${channels.padEnd(10)} ${api.padEnd(6)} ${tunnel}`);
@@ -25883,7 +26179,7 @@ async function showAllInstances(json = false) {
25883
26179
  console.log("");
25884
26180
  }
25885
26181
  async function showInstanceById(id, json = false) {
25886
- const registryPath = path56.join(getGlobalRoot(), "instances.json");
26182
+ const registryPath = path57.join(getGlobalRoot(), "instances.json");
25887
26183
  const registry = new InstanceRegistry(registryPath);
25888
26184
  await registry.load();
25889
26185
  const entry = registry.get(id);
@@ -25898,7 +26194,7 @@ async function showSingleInstance(root, json = false) {
25898
26194
  const info = readInstanceInfo(root);
25899
26195
  if (json) {
25900
26196
  jsonSuccess({
25901
- id: path56.basename(root),
26197
+ id: path57.basename(root),
25902
26198
  name: info.name,
25903
26199
  status: info.pid ? "online" : "offline",
25904
26200
  pid: info.pid,
@@ -25929,13 +26225,13 @@ function readInstanceInfo(root) {
25929
26225
  channels: []
25930
26226
  };
25931
26227
  try {
25932
- const config = JSON.parse(fs51.readFileSync(path56.join(root, "config.json"), "utf-8"));
26228
+ const config = JSON.parse(fs51.readFileSync(path57.join(root, "config.json"), "utf-8"));
25933
26229
  result.name = config.instanceName ?? null;
25934
26230
  result.runMode = config.runMode ?? null;
25935
26231
  } catch {
25936
26232
  }
25937
26233
  try {
25938
- const pid = parseInt(fs51.readFileSync(path56.join(root, "openacp.pid"), "utf-8").trim());
26234
+ const pid = parseInt(fs51.readFileSync(path57.join(root, "openacp.pid"), "utf-8").trim());
25939
26235
  if (!isNaN(pid)) {
25940
26236
  process.kill(pid, 0);
25941
26237
  result.pid = pid;
@@ -25943,19 +26239,19 @@ function readInstanceInfo(root) {
25943
26239
  } catch {
25944
26240
  }
25945
26241
  try {
25946
- const port = parseInt(fs51.readFileSync(path56.join(root, "api.port"), "utf-8").trim());
26242
+ const port = parseInt(fs51.readFileSync(path57.join(root, "api.port"), "utf-8").trim());
25947
26243
  if (!isNaN(port)) result.apiPort = port;
25948
26244
  } catch {
25949
26245
  }
25950
26246
  try {
25951
- const tunnels = JSON.parse(fs51.readFileSync(path56.join(root, "tunnels.json"), "utf-8"));
26247
+ const tunnels = JSON.parse(fs51.readFileSync(path57.join(root, "tunnels.json"), "utf-8"));
25952
26248
  const entries = Object.values(tunnels);
25953
26249
  const systemEntry = entries.find((t) => t.type === "system");
25954
26250
  if (systemEntry?.port) result.tunnelPort = systemEntry.port;
25955
26251
  } catch {
25956
26252
  }
25957
26253
  try {
25958
- const plugins = JSON.parse(fs51.readFileSync(path56.join(root, "plugins.json"), "utf-8"));
26254
+ const plugins = JSON.parse(fs51.readFileSync(path57.join(root, "plugins.json"), "utf-8"));
25959
26255
  const adapters = ["@openacp/telegram", "@openacp/discord", "@openacp/slack"];
25960
26256
  for (const name of adapters) {
25961
26257
  if (plugins.installed?.[name] && plugins.installed[name].enabled !== false) {
@@ -25970,7 +26266,7 @@ function formatInstanceStatus(root) {
25970
26266
  const info = readInstanceInfo(root);
25971
26267
  if (!info.pid) return null;
25972
26268
  const isGlobal = root === getGlobalRoot();
25973
- const displayPath = root.replace(os26.homedir(), "~");
26269
+ const displayPath = root.replace(os27.homedir(), "~");
25974
26270
  const label = isGlobal ? "global" : "local";
25975
26271
  const lines = [];
25976
26272
  lines.push(` PID: ${info.pid}`);
@@ -26039,7 +26335,7 @@ var config_editor_exports = {};
26039
26335
  __export(config_editor_exports, {
26040
26336
  runConfigEditor: () => runConfigEditor
26041
26337
  });
26042
- import * as path57 from "path";
26338
+ import * as path58 from "path";
26043
26339
  import * as clack9 from "@clack/prompts";
26044
26340
  async function select8(opts) {
26045
26341
  const result = await clack9.select({
@@ -26070,11 +26366,19 @@ async function input(opts) {
26070
26366
  }
26071
26367
  return result;
26072
26368
  }
26073
- async function editTelegram(config, updates) {
26369
+ async function editTelegram(config, updates, settingsManager) {
26074
26370
  const tg = config.channels?.telegram ?? {};
26075
- const currentToken = tg.botToken ?? "";
26076
- const currentChatId = tg.chatId ?? 0;
26077
- const currentEnabled = tg.enabled ?? false;
26371
+ let currentToken = tg.botToken ?? "";
26372
+ let currentChatId = tg.chatId ?? 0;
26373
+ let currentEnabled = tg.enabled ?? false;
26374
+ if (settingsManager) {
26375
+ const ps = await settingsManager.loadSettings("@openacp/telegram");
26376
+ if (Object.keys(ps).length > 0) {
26377
+ currentToken = ps.botToken ?? currentToken;
26378
+ currentChatId = ps.chatId ?? currentChatId;
26379
+ currentEnabled = ps.enabled ?? currentEnabled;
26380
+ }
26381
+ }
26078
26382
  console.log(header("Telegram"));
26079
26383
  console.log(` Enabled : ${currentEnabled ? ok2("yes") : dim2("no")}`);
26080
26384
  const tokenDisplay = currentToken.length > 12 ? currentToken.slice(0, 6) + "..." + currentToken.slice(-6) : currentToken || dim2("(not set)");
@@ -26089,7 +26393,11 @@ async function editTelegram(config, updates) {
26089
26393
  return updates.channels.telegram;
26090
26394
  };
26091
26395
  while (true) {
26092
- const isEnabled = (() => {
26396
+ const isEnabled = await (async () => {
26397
+ if (settingsManager) {
26398
+ const ps = await settingsManager.loadSettings("@openacp/telegram");
26399
+ if ("enabled" in ps) return ps.enabled;
26400
+ }
26093
26401
  const ch = updates.channels;
26094
26402
  const tgUp = ch?.telegram;
26095
26403
  if (tgUp && "enabled" in tgUp) return tgUp.enabled;
@@ -26106,8 +26414,12 @@ async function editTelegram(config, updates) {
26106
26414
  });
26107
26415
  if (choice === "back") break;
26108
26416
  if (choice === "toggle") {
26109
- const tgUp = ensureTelegramUpdates();
26110
- tgUp.enabled = !isEnabled;
26417
+ if (settingsManager) {
26418
+ await settingsManager.updatePluginSettings("@openacp/telegram", { enabled: !isEnabled });
26419
+ } else {
26420
+ const tgUp = ensureTelegramUpdates();
26421
+ tgUp.enabled = !isEnabled;
26422
+ }
26111
26423
  console.log(!isEnabled ? ok2("Telegram enabled") : ok2("Telegram disabled"));
26112
26424
  }
26113
26425
  if (choice === "token") {
@@ -26127,9 +26439,13 @@ async function editTelegram(config, updates) {
26127
26439
  } catch {
26128
26440
  console.log(warn2("Telegram validator not available \u2014 skipping validation"));
26129
26441
  }
26130
- const tgUp = ensureTelegramUpdates();
26131
- tgUp.botToken = token.trim();
26132
- tgUp.enabled = true;
26442
+ if (settingsManager) {
26443
+ await settingsManager.updatePluginSettings("@openacp/telegram", { botToken: token.trim(), enabled: true });
26444
+ } else {
26445
+ const tgUp = ensureTelegramUpdates();
26446
+ tgUp.botToken = token.trim();
26447
+ tgUp.enabled = true;
26448
+ }
26133
26449
  }
26134
26450
  if (choice === "chatid") {
26135
26451
  const chatIdStr = await input({
@@ -26144,8 +26460,8 @@ async function editTelegram(config, updates) {
26144
26460
  const chatId = Number(chatIdStr.trim());
26145
26461
  const tokenForValidation = (() => {
26146
26462
  const ch = updates.channels;
26147
- const tgUp2 = ch?.telegram;
26148
- if (typeof tgUp2?.botToken === "string") return tgUp2.botToken;
26463
+ const tgUp = ch?.telegram;
26464
+ if (typeof tgUp?.botToken === "string") return tgUp.botToken;
26149
26465
  return currentToken;
26150
26466
  })();
26151
26467
  try {
@@ -26159,8 +26475,12 @@ async function editTelegram(config, updates) {
26159
26475
  } catch {
26160
26476
  console.log(warn2("Telegram validator not available \u2014 skipping validation"));
26161
26477
  }
26162
- const tgUp = ensureTelegramUpdates();
26163
- tgUp.chatId = chatId;
26478
+ if (settingsManager) {
26479
+ await settingsManager.updatePluginSettings("@openacp/telegram", { chatId });
26480
+ } else {
26481
+ const tgUp = ensureTelegramUpdates();
26482
+ tgUp.chatId = chatId;
26483
+ }
26164
26484
  }
26165
26485
  }
26166
26486
  }
@@ -26200,7 +26520,7 @@ async function editDiscord(_config, _updates) {
26200
26520
  const { createInstallContext: createInstallContext2 } = await Promise.resolve().then(() => (init_install_context(), install_context_exports));
26201
26521
  const { getGlobalRoot: getGlobalRoot2 } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
26202
26522
  const root = getGlobalRoot2();
26203
- const basePath = path57.join(root, "plugins");
26523
+ const basePath = path58.join(root, "plugins");
26204
26524
  const settingsManager = new SettingsManager2(basePath);
26205
26525
  const ctx = createInstallContext2({
26206
26526
  pluginName: plugin2.name,
@@ -26213,7 +26533,7 @@ async function editDiscord(_config, _updates) {
26213
26533
  console.log(warn2("This plugin does not have a configure() method yet."));
26214
26534
  }
26215
26535
  }
26216
- async function editChannels(config, updates) {
26536
+ async function editChannels(config, updates, settingsManager) {
26217
26537
  const tgEnabled = config.channels?.telegram?.enabled !== false && config.channels?.telegram;
26218
26538
  const dcEnabled = config.channels?.discord?.enabled !== false && config.channels?.discord;
26219
26539
  console.log(header("Channels"));
@@ -26230,7 +26550,7 @@ async function editChannels(config, updates) {
26230
26550
  ]
26231
26551
  });
26232
26552
  if (choice === "back") break;
26233
- if (choice === "telegram") await editTelegram(config, updates);
26553
+ if (choice === "telegram") await editTelegram(config, updates, settingsManager);
26234
26554
  if (choice === "discord") await editDiscord(config, updates);
26235
26555
  }
26236
26556
  }
@@ -26277,8 +26597,18 @@ async function editWorkspace(config, updates) {
26277
26597
  updates.workspace = { baseDir: newDir.trim() };
26278
26598
  console.log(ok2(`Workspace set to ${newDir.trim()}`));
26279
26599
  }
26280
- async function editSecurity(config, updates) {
26281
- const sec = config.security ?? { allowedUserIds: [], maxConcurrentSessions: 20, sessionTimeoutMinutes: 60 };
26600
+ async function editSecurity(config, updates, settingsManager) {
26601
+ let sec = config.security ?? { allowedUserIds: [], maxConcurrentSessions: 20, sessionTimeoutMinutes: 60 };
26602
+ if (settingsManager) {
26603
+ const ps = await settingsManager.loadSettings("@openacp/security");
26604
+ if (Object.keys(ps).length > 0) {
26605
+ sec = {
26606
+ allowedUserIds: ps.allowedUserIds ?? sec.allowedUserIds,
26607
+ maxConcurrentSessions: ps.maxConcurrentSessions ?? sec.maxConcurrentSessions,
26608
+ sessionTimeoutMinutes: ps.sessionTimeoutMinutes ?? sec.sessionTimeoutMinutes
26609
+ };
26610
+ }
26611
+ }
26282
26612
  console.log(header("Security"));
26283
26613
  console.log(` Allowed user IDs : ${sec.allowedUserIds?.length ? sec.allowedUserIds.join(", ") : dim2("(all users allowed)")}`);
26284
26614
  console.log(` Max concurrent sessions : ${sec.maxConcurrentSessions}`);
@@ -26304,8 +26634,12 @@ async function editSecurity(config, updates) {
26304
26634
  return true;
26305
26635
  }
26306
26636
  });
26307
- if (!updates.security) updates.security = {};
26308
- updates.security.maxConcurrentSessions = Number(val.trim());
26637
+ if (settingsManager) {
26638
+ await settingsManager.updatePluginSettings("@openacp/security", { maxConcurrentSessions: Number(val.trim()) });
26639
+ } else {
26640
+ if (!updates.security) updates.security = {};
26641
+ updates.security.maxConcurrentSessions = Number(val.trim());
26642
+ }
26309
26643
  console.log(ok2(`Max concurrent sessions set to ${val.trim()}`));
26310
26644
  }
26311
26645
  if (choice === "timeout") {
@@ -26318,8 +26652,12 @@ async function editSecurity(config, updates) {
26318
26652
  return true;
26319
26653
  }
26320
26654
  });
26321
- if (!updates.security) updates.security = {};
26322
- updates.security.sessionTimeoutMinutes = Number(val.trim());
26655
+ if (settingsManager) {
26656
+ await settingsManager.updatePluginSettings("@openacp/security", { sessionTimeoutMinutes: Number(val.trim()) });
26657
+ } else {
26658
+ if (!updates.security) updates.security = {};
26659
+ updates.security.sessionTimeoutMinutes = Number(val.trim());
26660
+ }
26323
26661
  console.log(ok2(`Session timeout set to ${val.trim()} minutes`));
26324
26662
  }
26325
26663
  }
@@ -26444,8 +26782,14 @@ async function editRunMode(config, updates) {
26444
26782
  }
26445
26783
  }
26446
26784
  }
26447
- async function editApi(config, updates) {
26448
- const api = config.api ?? { port: 21420, host: "127.0.0.1" };
26785
+ async function editApi(config, updates, settingsManager) {
26786
+ let api = config.api ?? { port: 21420, host: "127.0.0.1" };
26787
+ if (settingsManager) {
26788
+ const ps = await settingsManager.loadSettings("@openacp/api-server");
26789
+ if (Object.keys(ps).length > 0) {
26790
+ api = { port: ps.port ?? api.port, host: ps.host ?? api.host };
26791
+ }
26792
+ }
26449
26793
  console.log(header("API"));
26450
26794
  console.log(` Port : ${api.port}`);
26451
26795
  console.log(` Host : ${api.host} ${dim2("(localhost only)")}`);
@@ -26459,11 +26803,21 @@ async function editApi(config, updates) {
26459
26803
  return true;
26460
26804
  }
26461
26805
  });
26462
- updates.api = { port: Number(newPort.trim()) };
26806
+ if (settingsManager) {
26807
+ await settingsManager.updatePluginSettings("@openacp/api-server", { port: Number(newPort.trim()) });
26808
+ } else {
26809
+ updates.api = { port: Number(newPort.trim()) };
26810
+ }
26463
26811
  console.log(ok2(`API port set to ${newPort.trim()}`));
26464
26812
  }
26465
- async function editTunnel(config, updates) {
26466
- const tunnel = config.tunnel ?? { enabled: false, port: 3100, provider: "cloudflare", options: {}, storeTtlMinutes: 60, auth: { enabled: false } };
26813
+ async function editTunnel(config, updates, settingsManager) {
26814
+ let tunnel = config.tunnel ?? { enabled: false, port: 3100, provider: "cloudflare", options: {}, storeTtlMinutes: 60, auth: { enabled: false } };
26815
+ if (settingsManager) {
26816
+ const ps = await settingsManager.loadSettings("@openacp/tunnel");
26817
+ if (Object.keys(ps).length > 0) {
26818
+ tunnel = { ...tunnel, ...ps };
26819
+ }
26820
+ }
26467
26821
  const currentUpdates = updates.tunnel ?? {};
26468
26822
  const getVal = (key, fallback) => key in currentUpdates ? currentUpdates[key] : tunnel[key] ?? fallback;
26469
26823
  console.log(header("Tunnel"));
@@ -26490,6 +26844,9 @@ async function editTunnel(config, updates) {
26490
26844
  const tun = updates.tunnel;
26491
26845
  if (choice === "toggle") {
26492
26846
  const current = getVal("enabled", false);
26847
+ if (settingsManager) {
26848
+ await settingsManager.updatePluginSettings("@openacp/tunnel", { enabled: !current });
26849
+ }
26493
26850
  tun.enabled = !current;
26494
26851
  console.log(!current ? ok2("Tunnel enabled") : ok2("Tunnel disabled"));
26495
26852
  }
@@ -26503,6 +26860,9 @@ async function editTunnel(config, updates) {
26503
26860
  { name: "Tailscale Funnel", value: "tailscale" }
26504
26861
  ]
26505
26862
  });
26863
+ if (settingsManager) {
26864
+ await settingsManager.updatePluginSettings("@openacp/tunnel", { provider, options: {} });
26865
+ }
26506
26866
  tun.provider = provider;
26507
26867
  tun.options = {};
26508
26868
  console.log(ok2(`Provider set to ${provider}`));
@@ -26517,6 +26877,9 @@ async function editTunnel(config, updates) {
26517
26877
  return true;
26518
26878
  }
26519
26879
  });
26880
+ if (settingsManager) {
26881
+ await settingsManager.updatePluginSettings("@openacp/tunnel", { port: Number(val.trim()) });
26882
+ }
26520
26883
  tun.port = Number(val.trim());
26521
26884
  console.log(ok2(`Tunnel port set to ${val.trim()}`));
26522
26885
  }
@@ -26524,11 +26887,17 @@ async function editTunnel(config, updates) {
26524
26887
  const provider = getVal("provider", "cloudflare");
26525
26888
  const currentOptions = getVal("options", {});
26526
26889
  await editProviderOptions(provider, currentOptions, tun);
26890
+ if (settingsManager) {
26891
+ await settingsManager.updatePluginSettings("@openacp/tunnel", { options: tun.options });
26892
+ }
26527
26893
  }
26528
26894
  if (choice === "auth") {
26529
26895
  const currentAuth = getVal("auth", { enabled: false });
26530
26896
  if (currentAuth.enabled) {
26531
26897
  tun.auth = { enabled: false };
26898
+ if (settingsManager) {
26899
+ await settingsManager.updatePluginSettings("@openacp/tunnel", { auth: { enabled: false } });
26900
+ }
26532
26901
  console.log(ok2("Tunnel auth disabled"));
26533
26902
  } else {
26534
26903
  const token = await input({
@@ -26536,6 +26905,9 @@ async function editTunnel(config, updates) {
26536
26905
  default: ""
26537
26906
  });
26538
26907
  tun.auth = token.trim() ? { enabled: true, token: token.trim() } : { enabled: true };
26908
+ if (settingsManager) {
26909
+ await settingsManager.updatePluginSettings("@openacp/tunnel", { auth: tun.auth });
26910
+ }
26539
26911
  console.log(ok2("Tunnel auth enabled"));
26540
26912
  }
26541
26913
  }
@@ -26601,7 +26973,7 @@ async function editProviderOptions(provider, currentOptions, tun) {
26601
26973
  console.log(dim2(`No configurable options for provider "${provider}"`));
26602
26974
  }
26603
26975
  }
26604
- async function runConfigEditor(configManager, mode = "file", apiPort) {
26976
+ async function runConfigEditor(configManager, mode = "file", apiPort, settingsManager) {
26605
26977
  await configManager.load();
26606
26978
  const config = configManager.get();
26607
26979
  const updates = {};
@@ -26636,14 +27008,14 @@ ${c2.cyan}${c2.bold}OpenACP Config Editor${c2.reset}`);
26636
27008
  break;
26637
27009
  }
26638
27010
  const sectionUpdates = {};
26639
- if (choice === "channels") await editChannels(config, sectionUpdates);
27011
+ if (choice === "channels") await editChannels(config, sectionUpdates, settingsManager);
26640
27012
  else if (choice === "agent") await editAgent(config, sectionUpdates);
26641
27013
  else if (choice === "workspace") await editWorkspace(config, sectionUpdates);
26642
- else if (choice === "security") await editSecurity(config, sectionUpdates);
27014
+ else if (choice === "security") await editSecurity(config, sectionUpdates, settingsManager);
26643
27015
  else if (choice === "logging") await editLogging(config, sectionUpdates);
26644
27016
  else if (choice === "runMode") await editRunMode(config, sectionUpdates);
26645
- else if (choice === "api") await editApi(config, sectionUpdates);
26646
- else if (choice === "tunnel") await editTunnel(config, sectionUpdates);
27017
+ else if (choice === "api") await editApi(config, sectionUpdates, settingsManager);
27018
+ else if (choice === "tunnel") await editTunnel(config, sectionUpdates, settingsManager);
26647
27019
  if (mode === "api" && Object.keys(sectionUpdates).length > 0) {
26648
27020
  await sendConfigViaApi(apiPort, sectionUpdates);
26649
27021
  await configManager.load();
@@ -26663,17 +27035,17 @@ ${c2.cyan}${c2.bold}OpenACP Config Editor${c2.reset}`);
26663
27035
  async function sendConfigViaApi(port, updates) {
26664
27036
  const { apiCall: call } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
26665
27037
  const paths = flattenToPaths(updates);
26666
- for (const { path: path67, value } of paths) {
27038
+ for (const { path: path68, value } of paths) {
26667
27039
  const res = await call(port, "/api/config", {
26668
27040
  method: "PATCH",
26669
27041
  headers: { "Content-Type": "application/json" },
26670
- body: JSON.stringify({ path: path67, value })
27042
+ body: JSON.stringify({ path: path68, value })
26671
27043
  });
26672
27044
  const data = await res.json();
26673
27045
  if (!res.ok) {
26674
- console.log(warn2(`Failed to update ${path67}: ${data.error}`));
27046
+ console.log(warn2(`Failed to update ${path68}: ${data.error}`));
26675
27047
  } else if (data.needsRestart) {
26676
- console.log(warn2(`${path67} updated \u2014 restart required`));
27048
+ console.log(warn2(`${path68} updated \u2014 restart required`));
26677
27049
  }
26678
27050
  }
26679
27051
  }
@@ -26782,35 +27154,35 @@ __export(instance_prompt_exports, {
26782
27154
  promptForInstance: () => promptForInstance
26783
27155
  });
26784
27156
  import fs56 from "fs";
26785
- import path65 from "path";
26786
- import os31 from "os";
27157
+ import path66 from "path";
27158
+ import os32 from "os";
26787
27159
  async function promptForInstance(opts) {
26788
27160
  const globalRoot = getGlobalRoot();
26789
- const globalConfigExists = fs56.existsSync(path65.join(globalRoot, "config.json"));
27161
+ const globalConfigExists = fs56.existsSync(path66.join(globalRoot, "config.json"));
26790
27162
  const cwd = process.cwd();
26791
- const localRoot = path65.join(cwd, ".openacp");
27163
+ const localRoot = path66.join(cwd, ".openacp");
26792
27164
  if (!globalConfigExists) return globalRoot;
26793
27165
  const isTTY = process.stdin.isTTY && process.stdout.isTTY;
26794
27166
  if (!isTTY) return globalRoot;
26795
- const registryPath = path65.join(globalRoot, "instances.json");
27167
+ const registryPath = path66.join(globalRoot, "instances.json");
26796
27168
  const registry = new InstanceRegistry(registryPath);
26797
27169
  registry.load();
26798
27170
  const instances = registry.list().filter((e) => fs56.existsSync(e.root));
26799
27171
  const instanceOptions = instances.map((e) => {
26800
27172
  let name = e.id;
26801
27173
  try {
26802
- const raw = fs56.readFileSync(path65.join(e.root, "config.json"), "utf-8");
27174
+ const raw = fs56.readFileSync(path66.join(e.root, "config.json"), "utf-8");
26803
27175
  const parsed = JSON.parse(raw);
26804
27176
  if (parsed.instanceName) name = parsed.instanceName;
26805
27177
  } catch {
26806
27178
  }
26807
27179
  const isGlobal = e.root === globalRoot;
26808
- const displayPath = e.root.replace(os31.homedir(), "~");
27180
+ const displayPath = e.root.replace(os32.homedir(), "~");
26809
27181
  const type = isGlobal ? "global" : "local";
26810
27182
  return { value: e.root, label: `${name} workspace (${type} \u2014 ${displayPath})` };
26811
27183
  });
26812
27184
  if (instanceOptions.length === 0) {
26813
- const globalDisplay = globalRoot.replace(os31.homedir(), "~");
27185
+ const globalDisplay = globalRoot.replace(os32.homedir(), "~");
26814
27186
  instanceOptions.push({ value: globalRoot, label: `Global workspace (${globalDisplay})` });
26815
27187
  }
26816
27188
  if (instanceOptions.length === 1 && !opts.allowCreate) {
@@ -26821,7 +27193,7 @@ async function promptForInstance(opts) {
26821
27193
  label: o.label
26822
27194
  }));
26823
27195
  if (opts.allowCreate) {
26824
- const localDisplay = localRoot.replace(os31.homedir(), "~");
27196
+ const localDisplay = localRoot.replace(os32.homedir(), "~");
26825
27197
  options.push({ value: localRoot, label: `New local workspace (${localDisplay})` });
26826
27198
  }
26827
27199
  const clack10 = await import("@clack/prompts");
@@ -26847,7 +27219,7 @@ var init_instance_prompt = __esm({
26847
27219
 
26848
27220
  // src/cli.ts
26849
27221
  import { setDefaultAutoSelectFamily } from "net";
26850
- import path66 from "path";
27222
+ import path67 from "path";
26851
27223
 
26852
27224
  // src/cli/commands/help.ts
26853
27225
  function printHelp() {
@@ -27086,11 +27458,11 @@ Shows all plugins registered in the plugin registry.
27086
27458
  `);
27087
27459
  return;
27088
27460
  }
27089
- const os32 = await import("os");
27090
- const path67 = await import("path");
27461
+ const os33 = await import("os");
27462
+ const path68 = await import("path");
27091
27463
  const { PluginRegistry: PluginRegistry2 } = await Promise.resolve().then(() => (init_plugin_registry(), plugin_registry_exports));
27092
- const root = instanceRoot ?? path67.join(os32.homedir(), ".openacp");
27093
- const registryPath = path67.join(root, "plugins.json");
27464
+ const root = instanceRoot ?? path68.join(os33.homedir(), ".openacp");
27465
+ const registryPath = path68.join(root, "plugins.json");
27094
27466
  const registry = new PluginRegistry2(registryPath);
27095
27467
  await registry.load();
27096
27468
  const plugins = registry.list();
@@ -27225,11 +27597,11 @@ async function cmdPlugin(args2 = [], instanceRoot) {
27225
27597
  }
27226
27598
  async function setPluginEnabled(name, enabled, instanceRoot, json = false) {
27227
27599
  if (json) await muteForJson();
27228
- const os32 = await import("os");
27229
- const path67 = await import("path");
27600
+ const os33 = await import("os");
27601
+ const path68 = await import("path");
27230
27602
  const { PluginRegistry: PluginRegistry2 } = await Promise.resolve().then(() => (init_plugin_registry(), plugin_registry_exports));
27231
- const root = instanceRoot ?? path67.join(os32.homedir(), ".openacp");
27232
- const registryPath = path67.join(root, "plugins.json");
27603
+ const root = instanceRoot ?? path68.join(os33.homedir(), ".openacp");
27604
+ const registryPath = path68.join(root, "plugins.json");
27233
27605
  const registry = new PluginRegistry2(registryPath);
27234
27606
  await registry.load();
27235
27607
  const entry = registry.get(name);
@@ -27244,8 +27616,8 @@ async function setPluginEnabled(name, enabled, instanceRoot, json = false) {
27244
27616
  console.log(`Plugin ${name} ${enabled ? "enabled" : "disabled"}. Restart to apply.`);
27245
27617
  }
27246
27618
  async function configurePlugin(name, instanceRoot) {
27247
- const os32 = await import("os");
27248
- const path67 = await import("path");
27619
+ const os33 = await import("os");
27620
+ const path68 = await import("path");
27249
27621
  const { corePlugins: corePlugins2 } = await Promise.resolve().then(() => (init_core_plugins(), core_plugins_exports));
27250
27622
  const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
27251
27623
  const { createInstallContext: createInstallContext2 } = await Promise.resolve().then(() => (init_install_context(), install_context_exports));
@@ -27254,8 +27626,8 @@ async function configurePlugin(name, instanceRoot) {
27254
27626
  console.error(`Plugin "${name}" not found.`);
27255
27627
  process.exit(1);
27256
27628
  }
27257
- const root = instanceRoot ?? path67.join(os32.homedir(), ".openacp");
27258
- const basePath = path67.join(root, "plugins", "data");
27629
+ const root = instanceRoot ?? path68.join(os33.homedir(), ".openacp");
27630
+ const basePath = path68.join(root, "plugins", "data");
27259
27631
  const settingsManager = new SettingsManager2(basePath);
27260
27632
  const ctx = createInstallContext2({ pluginName: name, settingsManager, basePath });
27261
27633
  if (plugin2.configure) {
@@ -27268,14 +27640,14 @@ async function configurePlugin(name, instanceRoot) {
27268
27640
  }
27269
27641
  async function installPlugin(input2, instanceRoot, json = false) {
27270
27642
  if (json) await muteForJson();
27271
- const os32 = await import("os");
27272
- const path67 = await import("path");
27643
+ const os33 = await import("os");
27644
+ const path68 = await import("path");
27273
27645
  const { execFileSync: execFileSync8 } = await import("child_process");
27274
27646
  const { getCurrentVersion: getCurrentVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
27275
27647
  const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
27276
27648
  const { createInstallContext: createInstallContext2 } = await Promise.resolve().then(() => (init_install_context(), install_context_exports));
27277
27649
  const { PluginRegistry: PluginRegistry2 } = await Promise.resolve().then(() => (init_plugin_registry(), plugin_registry_exports));
27278
- const root = instanceRoot ?? path67.join(os32.homedir(), ".openacp");
27650
+ const root = instanceRoot ?? path68.join(os33.homedir(), ".openacp");
27279
27651
  let pkgName;
27280
27652
  let pkgVersion;
27281
27653
  if (input2.startsWith("@")) {
@@ -27320,9 +27692,9 @@ async function installPlugin(input2, instanceRoot, json = false) {
27320
27692
  if (!json) console.log(`Installing ${installSpec}...`);
27321
27693
  const { corePlugins: corePlugins2 } = await Promise.resolve().then(() => (init_core_plugins(), core_plugins_exports));
27322
27694
  const builtinPlugin = corePlugins2.find((p2) => p2.name === pkgName);
27323
- const basePath = path67.join(root, "plugins", "data");
27695
+ const basePath = path68.join(root, "plugins", "data");
27324
27696
  const settingsManager = new SettingsManager2(basePath);
27325
- const registryPath = path67.join(root, "plugins.json");
27697
+ const registryPath = path68.join(root, "plugins.json");
27326
27698
  const pluginRegistry = new PluginRegistry2(registryPath);
27327
27699
  await pluginRegistry.load();
27328
27700
  if (builtinPlugin) {
@@ -27342,8 +27714,8 @@ async function installPlugin(input2, instanceRoot, json = false) {
27342
27714
  console.log(`\u2713 ${builtinPlugin.name} installed! Restart to activate.`);
27343
27715
  return;
27344
27716
  }
27345
- const pluginsDir = path67.join(root, "plugins");
27346
- const nodeModulesDir = path67.join(pluginsDir, "node_modules");
27717
+ const pluginsDir = path68.join(root, "plugins");
27718
+ const nodeModulesDir = path68.join(pluginsDir, "node_modules");
27347
27719
  try {
27348
27720
  execFileSync8("npm", ["install", installSpec, "--prefix", pluginsDir, "--save"], {
27349
27721
  stdio: json ? "pipe" : "inherit",
@@ -27357,8 +27729,8 @@ async function installPlugin(input2, instanceRoot, json = false) {
27357
27729
  const cliVersion = getCurrentVersion2();
27358
27730
  const isLocalPath = pkgName.startsWith("/") || pkgName.startsWith(".");
27359
27731
  try {
27360
- const pluginRoot = isLocalPath ? path67.resolve(pkgName) : path67.join(nodeModulesDir, pkgName);
27361
- const installedPkgPath = path67.join(pluginRoot, "package.json");
27732
+ const pluginRoot = isLocalPath ? path68.resolve(pkgName) : path68.join(nodeModulesDir, pkgName);
27733
+ const installedPkgPath = path68.join(pluginRoot, "package.json");
27362
27734
  const { readFileSync: readFileSync19 } = await import("fs");
27363
27735
  const installedPkg = JSON.parse(readFileSync19(installedPkgPath, "utf-8"));
27364
27736
  const minVersion = installedPkg.engines?.openacp?.replace(/[>=^~\s]/g, "");
@@ -27373,7 +27745,7 @@ async function installPlugin(input2, instanceRoot, json = false) {
27373
27745
  }
27374
27746
  }
27375
27747
  }
27376
- const pluginModule = await import(path67.join(pluginRoot, installedPkg.main ?? "dist/index.js"));
27748
+ const pluginModule = await import(path68.join(pluginRoot, installedPkg.main ?? "dist/index.js"));
27377
27749
  const plugin2 = pluginModule.default;
27378
27750
  if (plugin2?.install) {
27379
27751
  const ctx = createInstallContext2({ pluginName: plugin2.name ?? pkgName, settingsManager, basePath });
@@ -27403,12 +27775,12 @@ async function installPlugin(input2, instanceRoot, json = false) {
27403
27775
  }
27404
27776
  async function uninstallPlugin(name, purge, instanceRoot, json = false) {
27405
27777
  if (json) await muteForJson();
27406
- const os32 = await import("os");
27407
- const path67 = await import("path");
27778
+ const os33 = await import("os");
27779
+ const path68 = await import("path");
27408
27780
  const fs57 = await import("fs");
27409
27781
  const { PluginRegistry: PluginRegistry2 } = await Promise.resolve().then(() => (init_plugin_registry(), plugin_registry_exports));
27410
- const root = instanceRoot ?? path67.join(os32.homedir(), ".openacp");
27411
- const registryPath = path67.join(root, "plugins.json");
27782
+ const root = instanceRoot ?? path68.join(os33.homedir(), ".openacp");
27783
+ const registryPath = path68.join(root, "plugins.json");
27412
27784
  const registry = new PluginRegistry2(registryPath);
27413
27785
  await registry.load();
27414
27786
  const entry = registry.get(name);
@@ -27428,7 +27800,7 @@ async function uninstallPlugin(name, purge, instanceRoot, json = false) {
27428
27800
  if (plugin2?.uninstall) {
27429
27801
  const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
27430
27802
  const { createInstallContext: createInstallContext2 } = await Promise.resolve().then(() => (init_install_context(), install_context_exports));
27431
- const basePath = path67.join(root, "plugins", "data");
27803
+ const basePath = path68.join(root, "plugins", "data");
27432
27804
  const settingsManager = new SettingsManager2(basePath);
27433
27805
  const ctx = createInstallContext2({ pluginName: name, settingsManager, basePath });
27434
27806
  await plugin2.uninstall(ctx, { purge });
@@ -27436,7 +27808,7 @@ async function uninstallPlugin(name, purge, instanceRoot, json = false) {
27436
27808
  } catch {
27437
27809
  }
27438
27810
  if (purge) {
27439
- const pluginDir = path67.join(root, "plugins", name);
27811
+ const pluginDir = path68.join(root, "plugins", name);
27440
27812
  fs57.rmSync(pluginDir, { recursive: true, force: true });
27441
27813
  }
27442
27814
  registry.remove(name);
@@ -28266,13 +28638,13 @@ init_version();
28266
28638
  init_helpers();
28267
28639
  init_output();
28268
28640
  init_instance_hint();
28269
- import path37 from "path";
28270
- import os16 from "os";
28641
+ import path38 from "path";
28642
+ import os17 from "os";
28271
28643
  import fs36 from "fs";
28272
28644
  async function cmdStart(args2 = [], instanceRoot) {
28273
28645
  const json = isJsonMode(args2);
28274
28646
  if (json) await muteForJson();
28275
- const root = instanceRoot ?? path37.join(os16.homedir(), ".openacp");
28647
+ const root = instanceRoot ?? path38.join(os17.homedir(), ".openacp");
28276
28648
  if (!json && wantsHelp(args2)) {
28277
28649
  console.log(`
28278
28650
  \x1B[1mopenacp start\x1B[0m \u2014 Start OpenACP as a background daemon
@@ -28298,7 +28670,7 @@ Requires an existing config \u2014 run 'openacp' first to set up.
28298
28670
  await checkAndPromptUpdate();
28299
28671
  const { startDaemon: startDaemon2, getPidPath: getPidPath2 } = await Promise.resolve().then(() => (init_daemon2(), daemon_exports));
28300
28672
  const { ConfigManager: ConfigManager2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
28301
- const cm = new ConfigManager2(path37.join(root, "config.json"));
28673
+ const cm = new ConfigManager2(path38.join(root, "config.json"));
28302
28674
  if (await cm.exists()) {
28303
28675
  await cm.load();
28304
28676
  const config = cm.get();
@@ -28309,11 +28681,11 @@ Requires an existing config \u2014 run 'openacp' first to set up.
28309
28681
  process.exit(1);
28310
28682
  }
28311
28683
  if (json) {
28312
- let instanceId = path37.basename(root);
28684
+ let instanceId = path38.basename(root);
28313
28685
  try {
28314
28686
  const { getGlobalRoot: getGlobalRoot2 } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
28315
28687
  const { InstanceRegistry: InstanceRegistry2 } = await Promise.resolve().then(() => (init_instance_registry(), instance_registry_exports));
28316
- const reg = new InstanceRegistry2(path37.join(getGlobalRoot2(), "instances.json"));
28688
+ const reg = new InstanceRegistry2(path38.join(getGlobalRoot2(), "instances.json"));
28317
28689
  reg.load();
28318
28690
  const entry = reg.getByRoot(root);
28319
28691
  if (entry) instanceId = entry.id;
@@ -28321,7 +28693,7 @@ Requires an existing config \u2014 run 'openacp' first to set up.
28321
28693
  }
28322
28694
  let port = null;
28323
28695
  try {
28324
- const portStr = fs36.readFileSync(path37.join(root, "api.port"), "utf-8").trim();
28696
+ const portStr = fs36.readFileSync(path38.join(root, "api.port"), "utf-8").trim();
28325
28697
  port = parseInt(portStr) || null;
28326
28698
  } catch {
28327
28699
  port = config.api.port ?? null;
@@ -28330,7 +28702,7 @@ Requires an existing config \u2014 run 'openacp' first to set up.
28330
28702
  pid: result.pid,
28331
28703
  instanceId,
28332
28704
  name: config.instanceName ?? null,
28333
- directory: path37.dirname(root),
28705
+ directory: path38.dirname(root),
28334
28706
  dir: root,
28335
28707
  port
28336
28708
  });
@@ -28467,16 +28839,20 @@ the API for live updates. When stopped, edits config file directly.
28467
28839
  }
28468
28840
  const { runConfigEditor: runConfigEditor2 } = await Promise.resolve().then(() => (init_config_editor(), config_editor_exports));
28469
28841
  const { ConfigManager: ConfigManager2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
28842
+ const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
28843
+ const { getGlobalRoot: getGlobalRoot2 } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
28470
28844
  const cm = new ConfigManager2(instanceRoot ? pathMod.join(instanceRoot, "config.json") : void 0);
28471
28845
  if (!await cm.exists()) {
28472
28846
  console.error('No config found. Run "openacp" first to set up.');
28473
28847
  process.exit(1);
28474
28848
  }
28849
+ const root = instanceRoot ?? getGlobalRoot2();
28850
+ const settingsManager = new SettingsManager2(pathMod.join(root, "plugins"));
28475
28851
  const port = readApiPort(void 0, instanceRoot);
28476
28852
  if (port !== null) {
28477
- await runConfigEditor2(cm, "api", port);
28853
+ await runConfigEditor2(cm, "api", port, settingsManager);
28478
28854
  } else {
28479
- await runConfigEditor2(cm, "file");
28855
+ await runConfigEditor2(cm, "file", void 0, settingsManager);
28480
28856
  }
28481
28857
  }
28482
28858
 
@@ -28497,9 +28873,9 @@ start fresh with the setup wizard. The daemon must be stopped first.
28497
28873
  `);
28498
28874
  return;
28499
28875
  }
28500
- const os32 = await import("os");
28501
- const path67 = await import("path");
28502
- const root = instanceRoot ?? path67.join(os32.homedir(), ".openacp");
28876
+ const os33 = await import("os");
28877
+ const path68 = await import("path");
28878
+ const root = instanceRoot ?? path68.join(os33.homedir(), ".openacp");
28503
28879
  const { getStatus: getStatus2, getPidPath: getPidPath2 } = await Promise.resolve().then(() => (init_daemon2(), daemon_exports));
28504
28880
  const status = getStatus2(getPidPath2(root));
28505
28881
  if (status.running) {
@@ -28657,11 +29033,11 @@ init_instance_context();
28657
29033
  init_status();
28658
29034
  init_output();
28659
29035
  init_helpers();
28660
- import path58 from "path";
28661
- import os27 from "os";
29036
+ import path59 from "path";
29037
+ import os28 from "os";
28662
29038
  import fs52 from "fs";
28663
29039
  async function buildInstanceListEntries() {
28664
- const registryPath = path58.join(getGlobalRoot(), "instances.json");
29040
+ const registryPath = path59.join(getGlobalRoot(), "instances.json");
28665
29041
  const registry = new InstanceRegistry(registryPath);
28666
29042
  registry.load();
28667
29043
  return registry.list().map((entry) => {
@@ -28669,7 +29045,7 @@ async function buildInstanceListEntries() {
28669
29045
  return {
28670
29046
  id: entry.id,
28671
29047
  name: info.name,
28672
- directory: path58.dirname(entry.root),
29048
+ directory: path59.dirname(entry.root),
28673
29049
  root: entry.root,
28674
29050
  status: info.pid ? "running" : "stopped",
28675
29051
  port: info.apiPort
@@ -28719,7 +29095,7 @@ async function cmdInstancesList(args2) {
28719
29095
  for (const e of entries) {
28720
29096
  const status = e.status === "running" ? "\u25CF running" : "\u25CB stopped";
28721
29097
  const port = e.port ? `:${e.port}` : "\u2014";
28722
- const dir = e.directory.replace(os27.homedir(), "~");
29098
+ const dir = e.directory.replace(os28.homedir(), "~");
28723
29099
  const name = (e.name ?? e.id).padEnd(16);
28724
29100
  console.log(` ${status.padEnd(10)} ${e.id.padEnd(16)} ${name} ${dir} ${port}`);
28725
29101
  }
@@ -28742,9 +29118,9 @@ async function cmdInstancesCreate(args2) {
28742
29118
  const agentIdx = args2.indexOf("--agent");
28743
29119
  const agent = agentIdx !== -1 ? args2[agentIdx + 1] : void 0;
28744
29120
  const noInteractive = args2.includes("--no-interactive");
28745
- const resolvedDir = path58.resolve(rawDir.replace(/^~/, os27.homedir()));
28746
- const instanceRoot = path58.join(resolvedDir, ".openacp");
28747
- const registryPath = path58.join(getGlobalRoot(), "instances.json");
29121
+ const resolvedDir = path59.resolve(rawDir.replace(/^~/, os28.homedir()));
29122
+ const instanceRoot = path59.join(resolvedDir, ".openacp");
29123
+ const registryPath = path59.join(getGlobalRoot(), "instances.json");
28748
29124
  const registry = new InstanceRegistry(registryPath);
28749
29125
  registry.load();
28750
29126
  if (fs52.existsSync(instanceRoot)) {
@@ -28754,8 +29130,8 @@ async function cmdInstancesCreate(args2) {
28754
29130
  console.error(`Error: Instance already exists at ${resolvedDir} (id: ${existing.id})`);
28755
29131
  process.exit(1);
28756
29132
  }
28757
- const configPath = path58.join(instanceRoot, "config.json");
28758
- let name2 = path58.basename(resolvedDir);
29133
+ const configPath = path59.join(instanceRoot, "config.json");
29134
+ let name2 = path59.basename(resolvedDir);
28759
29135
  try {
28760
29136
  const config = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
28761
29137
  name2 = config.instanceName ?? name2;
@@ -28770,15 +29146,15 @@ async function cmdInstancesCreate(args2) {
28770
29146
  const name = instanceName ?? `openacp-${registry.list().length + 1}`;
28771
29147
  const id = registry.uniqueId(generateSlug(name));
28772
29148
  if (rawFrom) {
28773
- const fromRoot = path58.join(path58.resolve(rawFrom.replace(/^~/, os27.homedir())), ".openacp");
28774
- if (!fs52.existsSync(path58.join(fromRoot, "config.json"))) {
29149
+ const fromRoot = path59.join(path59.resolve(rawFrom.replace(/^~/, os28.homedir())), ".openacp");
29150
+ if (!fs52.existsSync(path59.join(fromRoot, "config.json"))) {
28775
29151
  console.error(`Error: No OpenACP instance found at ${rawFrom}`);
28776
29152
  process.exit(1);
28777
29153
  }
28778
29154
  fs52.mkdirSync(instanceRoot, { recursive: true });
28779
29155
  const { copyInstance: copyInstance2 } = await Promise.resolve().then(() => (init_instance_copy(), instance_copy_exports));
28780
29156
  await copyInstance2(fromRoot, instanceRoot, {});
28781
- const configPath = path58.join(instanceRoot, "config.json");
29157
+ const configPath = path59.join(instanceRoot, "config.json");
28782
29158
  try {
28783
29159
  const config = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
28784
29160
  config.instanceName = name;
@@ -28789,14 +29165,14 @@ async function cmdInstancesCreate(args2) {
28789
29165
  fs52.mkdirSync(instanceRoot, { recursive: true });
28790
29166
  const config = { instanceName: name, runMode: "daemon" };
28791
29167
  if (agent) config.defaultAgent = agent;
28792
- fs52.writeFileSync(path58.join(instanceRoot, "config.json"), JSON.stringify(config, null, 2));
28793
- fs52.writeFileSync(path58.join(instanceRoot, "plugins.json"), JSON.stringify({ version: 1, installed: {} }, null, 2));
29168
+ fs52.writeFileSync(path59.join(instanceRoot, "config.json"), JSON.stringify(config, null, 2));
29169
+ fs52.writeFileSync(path59.join(instanceRoot, "plugins.json"), JSON.stringify({ version: 1, installed: {} }, null, 2));
28794
29170
  } else {
28795
29171
  fs52.mkdirSync(instanceRoot, { recursive: true });
28796
29172
  const config = { instanceName: name, runMode: "daemon" };
28797
29173
  if (agent) config.defaultAgent = agent;
28798
- fs52.writeFileSync(path58.join(instanceRoot, "config.json"), JSON.stringify(config, null, 2));
28799
- fs52.writeFileSync(path58.join(instanceRoot, "plugins.json"), JSON.stringify({ version: 1, installed: {} }, null, 2));
29174
+ fs52.writeFileSync(path59.join(instanceRoot, "config.json"), JSON.stringify(config, null, 2));
29175
+ fs52.writeFileSync(path59.join(instanceRoot, "plugins.json"), JSON.stringify({ version: 1, installed: {} }, null, 2));
28800
29176
  console.log(`Instance created at ${resolvedDir}. Run 'openacp setup' inside that directory to configure it.`);
28801
29177
  }
28802
29178
  registry.register(id, instanceRoot);
@@ -28808,7 +29184,7 @@ async function outputInstance(json, { id, root }) {
28808
29184
  const entry = {
28809
29185
  id,
28810
29186
  name: info.name,
28811
- directory: path58.dirname(root),
29187
+ directory: path59.dirname(root),
28812
29188
  root,
28813
29189
  status: info.pid ? "running" : "stopped",
28814
29190
  port: info.apiPort
@@ -28817,7 +29193,7 @@ async function outputInstance(json, { id, root }) {
28817
29193
  jsonSuccess(entry);
28818
29194
  return;
28819
29195
  }
28820
- console.log(`Instance created: ${info.name ?? id} at ${path58.dirname(root)}`);
29196
+ console.log(`Instance created: ${info.name ?? id} at ${path59.dirname(root)}`);
28821
29197
  }
28822
29198
 
28823
29199
  // src/cli/commands/integrate.ts
@@ -29580,16 +29956,16 @@ Options:
29580
29956
  }
29581
29957
 
29582
29958
  // src/cli/commands/onboard.ts
29583
- import * as path59 from "path";
29959
+ import * as path60 from "path";
29584
29960
  async function cmdOnboard(instanceRoot) {
29585
29961
  const { ConfigManager: ConfigManager2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
29586
29962
  const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
29587
29963
  const { PluginRegistry: PluginRegistry2 } = await Promise.resolve().then(() => (init_plugin_registry(), plugin_registry_exports));
29588
29964
  const { getGlobalRoot: getGlobalRoot2 } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
29589
29965
  const OPENACP_DIR = instanceRoot ?? getGlobalRoot2();
29590
- const PLUGINS_DATA_DIR = path59.join(OPENACP_DIR, "plugins", "data");
29591
- const REGISTRY_PATH = path59.join(OPENACP_DIR, "plugins.json");
29592
- const cm = new ConfigManager2(path59.join(OPENACP_DIR, "config.json"));
29966
+ const PLUGINS_DATA_DIR = path60.join(OPENACP_DIR, "plugins", "data");
29967
+ const REGISTRY_PATH = path60.join(OPENACP_DIR, "plugins.json");
29968
+ const cm = new ConfigManager2(path60.join(OPENACP_DIR, "config.json"));
29593
29969
  const settingsManager = new SettingsManager2(PLUGINS_DATA_DIR);
29594
29970
  const pluginRegistry = new PluginRegistry2(REGISTRY_PATH);
29595
29971
  await pluginRegistry.load();
@@ -29608,17 +29984,17 @@ init_instance_context();
29608
29984
  init_instance_registry();
29609
29985
  init_instance_hint();
29610
29986
  init_output();
29611
- import path60 from "path";
29612
- import os28 from "os";
29987
+ import path61 from "path";
29988
+ import os29 from "os";
29613
29989
  import fs53 from "fs";
29614
29990
  import { randomUUID as randomUUID5 } from "crypto";
29615
29991
  async function cmdDefault(command2, instanceRoot) {
29616
29992
  const args2 = command2 ? [command2] : [];
29617
29993
  const json = isJsonMode(args2);
29618
29994
  if (json) await muteForJson();
29619
- const root = instanceRoot ?? path60.join(os28.homedir(), ".openacp");
29620
- const pluginsDataDir = path60.join(root, "plugins", "data");
29621
- const registryPath = path60.join(root, "plugins.json");
29995
+ const root = instanceRoot ?? path61.join(os29.homedir(), ".openacp");
29996
+ const pluginsDataDir = path61.join(root, "plugins", "data");
29997
+ const registryPath = path61.join(root, "plugins.json");
29622
29998
  const forceForeground = command2 === "--foreground";
29623
29999
  if (command2 && !command2.startsWith("-")) {
29624
30000
  const { suggestMatch: suggestMatch2 } = await Promise.resolve().then(() => (init_suggest(), suggest_exports));
@@ -29650,7 +30026,7 @@ async function cmdDefault(command2, instanceRoot) {
29650
30026
  }
29651
30027
  await checkAndPromptUpdate();
29652
30028
  const { ConfigManager: ConfigManager2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
29653
- const configPath = path60.join(root, "config.json");
30029
+ const configPath = path61.join(root, "config.json");
29654
30030
  const cm = new ConfigManager2(configPath);
29655
30031
  if (!await cm.exists()) {
29656
30032
  const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
@@ -29678,9 +30054,9 @@ async function cmdDefault(command2, instanceRoot) {
29678
30054
  process.exit(1);
29679
30055
  }
29680
30056
  if (json) {
29681
- let instanceId2 = path60.basename(root);
30057
+ let instanceId2 = path61.basename(root);
29682
30058
  try {
29683
- const reg2 = new InstanceRegistry(path60.join(getGlobalRoot(), "instances.json"));
30059
+ const reg2 = new InstanceRegistry(path61.join(getGlobalRoot(), "instances.json"));
29684
30060
  reg2.load();
29685
30061
  const entry = reg2.getByRoot(root);
29686
30062
  if (entry) instanceId2 = entry.id;
@@ -29688,7 +30064,7 @@ async function cmdDefault(command2, instanceRoot) {
29688
30064
  }
29689
30065
  let port = null;
29690
30066
  try {
29691
- const portStr = fs53.readFileSync(path60.join(root, "api.port"), "utf-8").trim();
30067
+ const portStr = fs53.readFileSync(path61.join(root, "api.port"), "utf-8").trim();
29692
30068
  port = parseInt(portStr) || null;
29693
30069
  } catch {
29694
30070
  port = config.api.port ?? null;
@@ -29697,7 +30073,7 @@ async function cmdDefault(command2, instanceRoot) {
29697
30073
  pid: result.pid,
29698
30074
  instanceId: instanceId2,
29699
30075
  name: config.instanceName ?? null,
29700
- directory: path60.dirname(root),
30076
+ directory: path61.dirname(root),
29701
30077
  dir: root,
29702
30078
  port
29703
30079
  });
@@ -29710,7 +30086,7 @@ async function cmdDefault(command2, instanceRoot) {
29710
30086
  markRunning2(root);
29711
30087
  printInstanceHint(root);
29712
30088
  const { startServer: startServer2 } = await Promise.resolve().then(() => (init_main(), main_exports));
29713
- const reg = new InstanceRegistry(path60.join(getGlobalRoot(), "instances.json"));
30089
+ const reg = new InstanceRegistry(path61.join(getGlobalRoot(), "instances.json"));
29714
30090
  reg.load();
29715
30091
  const existingEntry = reg.getByRoot(root);
29716
30092
  const instanceId = existingEntry?.id ?? randomUUID5();
@@ -29722,7 +30098,7 @@ async function cmdDefault(command2, instanceRoot) {
29722
30098
  if (json) {
29723
30099
  let port = null;
29724
30100
  try {
29725
- const portStr = fs53.readFileSync(path60.join(root, "api.port"), "utf-8").trim();
30101
+ const portStr = fs53.readFileSync(path61.join(root, "api.port"), "utf-8").trim();
29726
30102
  port = parseInt(portStr) || null;
29727
30103
  } catch {
29728
30104
  port = config.api.port ?? null;
@@ -29731,7 +30107,7 @@ async function cmdDefault(command2, instanceRoot) {
29731
30107
  pid: process.pid,
29732
30108
  instanceId,
29733
30109
  name: config.instanceName ?? null,
29734
- directory: path60.dirname(root),
30110
+ directory: path61.dirname(root),
29735
30111
  dir: root,
29736
30112
  port
29737
30113
  });
@@ -29800,7 +30176,7 @@ async function showAlreadyRunningMenu(root) {
29800
30176
  // src/cli/commands/dev.ts
29801
30177
  init_helpers();
29802
30178
  import fs54 from "fs";
29803
- import path61 from "path";
30179
+ import path62 from "path";
29804
30180
  async function cmdDev(args2 = []) {
29805
30181
  if (wantsHelp(args2)) {
29806
30182
  console.log(`
@@ -29827,12 +30203,12 @@ async function cmdDev(args2 = []) {
29827
30203
  console.error("Error: missing plugin path. Usage: openacp dev <plugin-path>");
29828
30204
  process.exit(1);
29829
30205
  }
29830
- const pluginPath = path61.resolve(pluginPathArg);
30206
+ const pluginPath = path62.resolve(pluginPathArg);
29831
30207
  if (!fs54.existsSync(pluginPath)) {
29832
30208
  console.error(`Error: plugin path does not exist: ${pluginPath}`);
29833
30209
  process.exit(1);
29834
30210
  }
29835
- const tsconfigPath = path61.join(pluginPath, "tsconfig.json");
30211
+ const tsconfigPath = path62.join(pluginPath, "tsconfig.json");
29836
30212
  const hasTsconfig = fs54.existsSync(tsconfigPath);
29837
30213
  if (hasTsconfig) {
29838
30214
  console.log("Compiling plugin TypeScript...");
@@ -29874,10 +30250,10 @@ async function cmdDev(args2 = []) {
29874
30250
 
29875
30251
  // src/cli/commands/attach.ts
29876
30252
  init_helpers();
29877
- import path62 from "path";
29878
- import os29 from "os";
30253
+ import path63 from "path";
30254
+ import os30 from "os";
29879
30255
  async function cmdAttach(args2 = [], instanceRoot) {
29880
- const root = instanceRoot ?? path62.join(os29.homedir(), ".openacp");
30256
+ const root = instanceRoot ?? path63.join(os30.homedir(), ".openacp");
29881
30257
  if (wantsHelp(args2)) {
29882
30258
  console.log(`
29883
30259
  \x1B[1mopenacp attach\x1B[0m \u2014 Attach to a running daemon
@@ -29911,15 +30287,15 @@ Press Ctrl+C to detach.
29911
30287
  console.log("");
29912
30288
  const { spawn: spawn9 } = await import("child_process");
29913
30289
  const { expandHome: expandHome4 } = await Promise.resolve().then(() => (init_config2(), config_exports));
29914
- let logDir2 = path62.join(root, "logs");
30290
+ let logDir2 = path63.join(root, "logs");
29915
30291
  try {
29916
- const configPath = path62.join(root, "config.json");
30292
+ const configPath = path63.join(root, "config.json");
29917
30293
  const { readFileSync: readFileSync19 } = await import("fs");
29918
30294
  const config = JSON.parse(readFileSync19(configPath, "utf-8"));
29919
30295
  if (config.logging?.logDir) logDir2 = expandHome4(config.logging.logDir);
29920
30296
  } catch {
29921
30297
  }
29922
- const logFile = path62.join(logDir2, "openacp.log");
30298
+ const logFile = path63.join(logDir2, "openacp.log");
29923
30299
  const tail = spawn9("tail", ["-f", "-n", "50", logFile], { stdio: "inherit" });
29924
30300
  tail.on("error", (err) => {
29925
30301
  console.error(`Cannot tail log file: ${err.message}`);
@@ -29931,8 +30307,8 @@ Press Ctrl+C to detach.
29931
30307
  init_api_client();
29932
30308
  init_instance_registry();
29933
30309
  init_output();
29934
- import path63 from "path";
29935
- import os30 from "os";
30310
+ import path64 from "path";
30311
+ import os31 from "os";
29936
30312
  import qrcode from "qrcode-terminal";
29937
30313
  async function cmdRemote(args2, instanceRoot) {
29938
30314
  const json = isJsonMode(args2);
@@ -29947,7 +30323,7 @@ async function cmdRemote(args2, instanceRoot) {
29947
30323
  const scopes = scopesRaw ? scopesRaw.split(",").map((s) => s.trim()) : void 0;
29948
30324
  let resolvedInstanceRoot2 = instanceRoot;
29949
30325
  if (instanceId) {
29950
- const registryPath = path63.join(os30.homedir(), ".openacp", "instances.json");
30326
+ const registryPath = path64.join(os31.homedir(), ".openacp", "instances.json");
29951
30327
  const registry = new InstanceRegistry(registryPath);
29952
30328
  await registry.load();
29953
30329
  const entry = registry.get(instanceId);
@@ -30085,7 +30461,7 @@ function extractFlag(args2, flag) {
30085
30461
  // src/cli/commands/setup.ts
30086
30462
  init_output();
30087
30463
  import * as fs55 from "fs";
30088
- import * as path64 from "path";
30464
+ import * as path65 from "path";
30089
30465
  function parseFlag(args2, flag) {
30090
30466
  const idx = args2.indexOf(flag);
30091
30467
  return idx !== -1 ? args2[idx + 1] : void 0;
@@ -30113,7 +30489,7 @@ async function cmdSetup(args2, instanceRoot) {
30113
30489
  }
30114
30490
  const runMode = rawRunMode;
30115
30491
  const defaultAgent = agentRaw.split(",")[0].trim();
30116
- const configPath = path64.join(instanceRoot, "config.json");
30492
+ const configPath = path65.join(instanceRoot, "config.json");
30117
30493
  let existing = {};
30118
30494
  if (fs55.existsSync(configPath)) {
30119
30495
  try {
@@ -30221,7 +30597,7 @@ async function main() {
30221
30597
  if (envRoot) {
30222
30598
  const { createInstanceContext: createInstanceContext2, getGlobalRoot: getGlobal } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
30223
30599
  const { InstanceRegistry: InstanceRegistry2 } = await Promise.resolve().then(() => (init_instance_registry(), instance_registry_exports));
30224
- const registry = new InstanceRegistry2(path66.join(getGlobal(), "instances.json"));
30600
+ const registry = new InstanceRegistry2(path67.join(getGlobal(), "instances.json"));
30225
30601
  await registry.load();
30226
30602
  const entry = registry.getByRoot(envRoot);
30227
30603
  const { randomUUID: randomUUID6 } = await import("crypto");