@openacp/cli 2026.327.5 → 2026.330.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/dist/adapter-4U6MC5ZS.js +13 -0
- package/dist/agent-catalog-SZQQERV7.js +10 -0
- package/dist/{agent-dependencies-WS7Z2DFW.js → agent-dependencies-ED2ZTUHG.js} +1 -2
- package/dist/{agent-registry-5LZT7CUB.js → agent-registry-YOGP656W.js} +1 -2
- package/dist/agent-store-5UHZH2XI.js +8 -0
- package/dist/{api-client-AQPNKXI2.js → api-client-XTLRRFPX.js} +1 -2
- package/dist/api-server-5VNYFWJE.js +7 -0
- package/dist/api-server-JLBDKCU4.js +10 -0
- package/dist/{autostart-6JS565RY.js → autostart-CUPZMKKC.js} +3 -4
- package/dist/{chunk-WIIZNPCR.js → chunk-2KT6TROD.js} +12 -33
- package/dist/chunk-2KT6TROD.js.map +1 -0
- package/dist/{chunk-PPSMUECX.js → chunk-2R5XM3ES.js} +2 -2
- package/dist/{chunk-SNPYTMPR.js → chunk-3EWTPOF7.js} +2 -2
- package/dist/{chunk-YEULD3SG.js → chunk-3NAFXVQM.js} +7 -2
- package/dist/{chunk-YEULD3SG.js.map → chunk-3NAFXVQM.js.map} +1 -1
- package/dist/{chunk-A6Y4GZM3.js → chunk-566W6INH.js} +2 -2
- package/dist/{chunk-ODUM3D6X.js → chunk-5HKQCYOI.js} +1 -39
- package/dist/chunk-5HKQCYOI.js.map +1 -0
- package/dist/{plugin-installer-QVJP6VKV.js → chunk-5WGVYX3C.js} +18 -5
- package/dist/chunk-5WGVYX3C.js.map +1 -0
- package/dist/{chunk-XWDW3XBE.js → chunk-5ZNBNIK3.js} +1331 -237
- package/dist/chunk-5ZNBNIK3.js.map +1 -0
- package/dist/{chunk-XIBG7LSL.js → chunk-7RKPIM3E.js} +482 -175
- package/dist/chunk-7RKPIM3E.js.map +1 -0
- package/dist/{chunk-WXVT3AOY.js → chunk-7ZCQF6QM.js} +8 -3
- package/dist/chunk-7ZCQF6QM.js.map +1 -0
- package/dist/{chunk-2YCW3QDV.js → chunk-BTJHGSLM.js} +8 -7
- package/dist/chunk-BTJHGSLM.js.map +1 -0
- package/dist/{chunk-P2G275VD.js → chunk-CFUJGWOP.js} +3 -3
- package/dist/{chunk-RBYBSSGO.js → chunk-FCTC7KDT.js} +2 -2
- package/dist/{chunk-QAQDGPB4.js → chunk-GEOXPGCO.js} +3 -3
- package/dist/{chunk-BLQUXO7S.js → chunk-IZ5UEZF7.js} +27 -2
- package/dist/chunk-IZ5UEZF7.js.map +1 -0
- package/dist/{chunk-4GMLGCF2.js → chunk-KDU3ZEWT.js} +2 -2
- package/dist/{chunk-QWP76EBW.js → chunk-MITTQMGZ.js} +16 -9
- package/dist/chunk-MITTQMGZ.js.map +1 -0
- package/dist/{chunk-HRKAXFWR.js → chunk-MPGEHTGE.js} +9 -9
- package/dist/{chunk-KMMEFXIE.js → chunk-PA6MNBG4.js} +41 -9
- package/dist/chunk-PA6MNBG4.js.map +1 -0
- package/dist/{chunk-BQ6FR32N.js → chunk-QWVHCTCA.js} +2 -2
- package/dist/{chunk-XMMAGAT4.js → chunk-R6KZYF7D.js} +8 -1
- package/dist/{chunk-XMMAGAT4.js.map → chunk-R6KZYF7D.js.map} +1 -1
- package/dist/{chunk-AD3X6DGK.js → chunk-TMVTSWVH.js} +75 -13
- package/dist/chunk-TMVTSWVH.js.map +1 -0
- package/dist/chunk-UWH7KIAA.js +701 -0
- package/dist/chunk-UWH7KIAA.js.map +1 -0
- package/dist/{chunk-SHTGQGAU.js → chunk-V2YZWYXT.js} +3 -3
- package/dist/{chunk-QVMEF6FB.js → chunk-W4LK6WJP.js} +38 -27
- package/dist/chunk-W4LK6WJP.js.map +1 -0
- package/dist/{chunk-S3ZGPPXY.js → chunk-YIGBJFJL.js} +9 -13
- package/dist/{chunk-S3ZGPPXY.js.map → chunk-YIGBJFJL.js.map} +1 -1
- package/dist/cli.js +133 -132
- package/dist/cli.js.map +1 -1
- package/dist/config-KN6NKKPF.js +20 -0
- package/dist/config-editor-76RVZS4B.js +10 -0
- package/dist/{config-registry-CUMNXFGK.js → config-registry-ZXAIJNYB.js} +2 -3
- package/dist/context-NXXW62NJ.js +9 -0
- package/dist/core-plugins-OCHKGCIZ.js +22 -0
- package/dist/{daemon-PXO5QPCR.js → daemon-XFEMMJSZ.js} +4 -5
- package/dist/{dev-loader-DRU3R7ZM.js → dev-loader-7P3HZCIA.js} +1 -3
- package/dist/{dev-loader-DRU3R7ZM.js.map → dev-loader-7P3HZCIA.js.map} +1 -1
- package/dist/doctor-AV6AUO22.js +9 -0
- package/dist/file-service-HHB3JQIO.js +8 -0
- package/dist/index.d.ts +141 -187
- package/dist/index.js +33 -29
- package/dist/index.js.map +1 -1
- package/dist/{install-cloudflared-AN24L4DP.js → install-cloudflared-JRJ4BSOM.js} +3 -4
- package/dist/{install-cloudflared-AN24L4DP.js.map → install-cloudflared-JRJ4BSOM.js.map} +1 -1
- package/dist/{install-context-XPWTFT3J.js → install-context-EHYV5WRY.js} +2 -3
- package/dist/{install-context-XPWTFT3J.js.map → install-context-EHYV5WRY.js.map} +1 -1
- package/dist/{install-jq-CRVDJGF3.js → install-jq-ISTGT263.js} +3 -4
- package/dist/{install-jq-CRVDJGF3.js.map → install-jq-ISTGT263.js.map} +1 -1
- package/dist/{integrate-G6CVXTGT.js → integrate-JIEZYDOR.js} +1 -2
- package/dist/{integrate-G6CVXTGT.js.map → integrate-JIEZYDOR.js.map} +1 -1
- package/dist/{log-LZ7FTRKG.js → log-YZ243M5G.js} +4 -3
- package/dist/{main-3GF3EQTE.js → main-L2M4NTJY.js} +135 -50
- package/dist/main-L2M4NTJY.js.map +1 -0
- package/dist/{menu-YDQ2LWAR.js → menu-ALFN37IR.js} +1 -2
- package/dist/notifications-MO23S7S3.js +8 -0
- package/dist/{plugin-create-5HQRF2ID.js → plugin-create-EHL76ZZG.js} +1 -2
- package/dist/{plugin-create-5HQRF2ID.js.map → plugin-create-EHL76ZZG.js.map} +1 -1
- package/dist/plugin-installer-VSTYZSXC.js +9 -0
- package/dist/{plugin-registry-WB3DR67H.js → plugin-registry-6J3YSFHF.js} +1 -2
- package/dist/{plugin-search-HQ4WQKOF.js → plugin-search-MGKAL5JM.js} +1 -2
- package/dist/{plugin-search-HQ4WQKOF.js.map → plugin-search-MGKAL5JM.js.map} +1 -1
- package/dist/{post-upgrade-3ADZRMYJ.js → post-upgrade-Y26S2ZQ7.js} +6 -7
- package/dist/{post-upgrade-3ADZRMYJ.js.map → post-upgrade-Y26S2ZQ7.js.map} +1 -1
- package/dist/{read-text-file-IRZM3QLM.js → read-text-file-DJBTITIB.js} +1 -2
- package/dist/{registry-client-AVGRE4CF.js → registry-client-GTBWLXYU.js} +1 -2
- package/dist/{security-YNRBW6S7.js → security-2BA265LN.js} +1 -2
- package/dist/{settings-manager-MD2U4ZV2.js → settings-manager-B4UN2LAC.js} +1 -2
- package/dist/{setup-A7VPW46C.js → setup-E6BNEYCS.js} +109 -83
- package/dist/setup-E6BNEYCS.js.map +1 -0
- package/dist/speech-SG62JYIF.js +9 -0
- package/dist/{suggest-7D6B542M.js → suggest-RST5VOHB.js} +1 -3
- package/dist/{suggest-7D6B542M.js.map → suggest-RST5VOHB.js.map} +1 -1
- package/dist/telegram-EAVRDNFU.js +7 -0
- package/dist/tunnel-HWJ27WDH.js +7 -0
- package/dist/{tunnel-service-QJPUYEKU.js → tunnel-service-ZMO4THKE.js} +90 -11
- package/dist/tunnel-service-ZMO4THKE.js.map +1 -0
- package/dist/{validators-WSTBNKRW.js → validators-GITLOFXC.js} +1 -2
- package/dist/{version-NQZBM5M7.js → version-AXXV6IV2.js} +1 -2
- package/package.json +1 -3
- package/dist/adapter-JQFQ3JAO.js +0 -15
- package/dist/adapter-UORRGHNH.js +0 -1030
- package/dist/adapter-UORRGHNH.js.map +0 -1
- package/dist/agent-catalog-YHBFERYO.js +0 -11
- package/dist/agent-store-VSHNY5GT.js +0 -9
- package/dist/api-server-7G3ZUZRM.js +0 -8
- package/dist/api-server-CAYNPUF2.js +0 -11
- package/dist/chunk-2YCW3QDV.js.map +0 -1
- package/dist/chunk-32LVIEPW.js +0 -477
- package/dist/chunk-32LVIEPW.js.map +0 -1
- package/dist/chunk-AD3X6DGK.js.map +0 -1
- package/dist/chunk-BLQUXO7S.js.map +0 -1
- package/dist/chunk-KMMEFXIE.js.map +0 -1
- package/dist/chunk-ODUM3D6X.js.map +0 -1
- package/dist/chunk-QVMEF6FB.js.map +0 -1
- package/dist/chunk-QWP76EBW.js.map +0 -1
- package/dist/chunk-VUNV25KB.js +0 -16
- package/dist/chunk-WIIZNPCR.js.map +0 -1
- package/dist/chunk-WXVT3AOY.js.map +0 -1
- package/dist/chunk-XIBG7LSL.js.map +0 -1
- package/dist/chunk-XWDW3XBE.js.map +0 -1
- package/dist/chunk-ZHGPZBS4.js +0 -49
- package/dist/chunk-ZHGPZBS4.js.map +0 -1
- package/dist/chunk-ZNSO2QVC.js +0 -124
- package/dist/chunk-ZNSO2QVC.js.map +0 -1
- package/dist/config-I4FMCJGZ.js +0 -15
- package/dist/config-editor-7PKW42GZ.js +0 -11
- package/dist/context-XM6E22LM.js +0 -10
- package/dist/core-plugins-Y5US6RED.js +0 -23
- package/dist/dist-UHQK5CXN.js +0 -21151
- package/dist/dist-UHQK5CXN.js.map +0 -1
- package/dist/doctor-QZQAP46W.js +0 -10
- package/dist/file-service-EUODJAIT.js +0 -9
- package/dist/main-3GF3EQTE.js.map +0 -1
- package/dist/notifications-D5BRDNSU.js +0 -9
- package/dist/plugin-installer-QVJP6VKV.js.map +0 -1
- package/dist/setup-A7VPW46C.js.map +0 -1
- package/dist/slack-2XNWBOWH.js +0 -8
- package/dist/speech-2GHQNRIO.js +0 -9
- package/dist/telegram-E65IWFBW.js +0 -8
- package/dist/tunnel-45HA72MB.js +0 -8
- package/dist/tunnel-service-QJPUYEKU.js.map +0 -1
- package/dist/version-NQZBM5M7.js.map +0 -1
- /package/dist/{adapter-JQFQ3JAO.js.map → adapter-4U6MC5ZS.js.map} +0 -0
- /package/dist/{agent-catalog-YHBFERYO.js.map → agent-catalog-SZQQERV7.js.map} +0 -0
- /package/dist/{agent-dependencies-WS7Z2DFW.js.map → agent-dependencies-ED2ZTUHG.js.map} +0 -0
- /package/dist/{agent-registry-5LZT7CUB.js.map → agent-registry-YOGP656W.js.map} +0 -0
- /package/dist/{agent-store-VSHNY5GT.js.map → agent-store-5UHZH2XI.js.map} +0 -0
- /package/dist/{api-client-AQPNKXI2.js.map → api-client-XTLRRFPX.js.map} +0 -0
- /package/dist/{api-server-7G3ZUZRM.js.map → api-server-5VNYFWJE.js.map} +0 -0
- /package/dist/{api-server-CAYNPUF2.js.map → api-server-JLBDKCU4.js.map} +0 -0
- /package/dist/{autostart-6JS565RY.js.map → autostart-CUPZMKKC.js.map} +0 -0
- /package/dist/{chunk-PPSMUECX.js.map → chunk-2R5XM3ES.js.map} +0 -0
- /package/dist/{chunk-SNPYTMPR.js.map → chunk-3EWTPOF7.js.map} +0 -0
- /package/dist/{chunk-A6Y4GZM3.js.map → chunk-566W6INH.js.map} +0 -0
- /package/dist/{chunk-P2G275VD.js.map → chunk-CFUJGWOP.js.map} +0 -0
- /package/dist/{chunk-RBYBSSGO.js.map → chunk-FCTC7KDT.js.map} +0 -0
- /package/dist/{chunk-QAQDGPB4.js.map → chunk-GEOXPGCO.js.map} +0 -0
- /package/dist/{chunk-4GMLGCF2.js.map → chunk-KDU3ZEWT.js.map} +0 -0
- /package/dist/{chunk-HRKAXFWR.js.map → chunk-MPGEHTGE.js.map} +0 -0
- /package/dist/{chunk-BQ6FR32N.js.map → chunk-QWVHCTCA.js.map} +0 -0
- /package/dist/{chunk-SHTGQGAU.js.map → chunk-V2YZWYXT.js.map} +0 -0
- /package/dist/{chunk-VUNV25KB.js.map → config-KN6NKKPF.js.map} +0 -0
- /package/dist/{config-I4FMCJGZ.js.map → config-editor-76RVZS4B.js.map} +0 -0
- /package/dist/{config-editor-7PKW42GZ.js.map → config-registry-ZXAIJNYB.js.map} +0 -0
- /package/dist/{config-registry-CUMNXFGK.js.map → context-NXXW62NJ.js.map} +0 -0
- /package/dist/{context-XM6E22LM.js.map → core-plugins-OCHKGCIZ.js.map} +0 -0
- /package/dist/{core-plugins-Y5US6RED.js.map → daemon-XFEMMJSZ.js.map} +0 -0
- /package/dist/{daemon-PXO5QPCR.js.map → doctor-AV6AUO22.js.map} +0 -0
- /package/dist/{doctor-QZQAP46W.js.map → file-service-HHB3JQIO.js.map} +0 -0
- /package/dist/{file-service-EUODJAIT.js.map → log-YZ243M5G.js.map} +0 -0
- /package/dist/{log-LZ7FTRKG.js.map → menu-ALFN37IR.js.map} +0 -0
- /package/dist/{menu-YDQ2LWAR.js.map → notifications-MO23S7S3.js.map} +0 -0
- /package/dist/{notifications-D5BRDNSU.js.map → plugin-installer-VSTYZSXC.js.map} +0 -0
- /package/dist/{plugin-registry-WB3DR67H.js.map → plugin-registry-6J3YSFHF.js.map} +0 -0
- /package/dist/{read-text-file-IRZM3QLM.js.map → read-text-file-DJBTITIB.js.map} +0 -0
- /package/dist/{registry-client-AVGRE4CF.js.map → registry-client-GTBWLXYU.js.map} +0 -0
- /package/dist/{security-YNRBW6S7.js.map → security-2BA265LN.js.map} +0 -0
- /package/dist/{settings-manager-MD2U4ZV2.js.map → settings-manager-B4UN2LAC.js.map} +0 -0
- /package/dist/{slack-2XNWBOWH.js.map → speech-SG62JYIF.js.map} +0 -0
- /package/dist/{speech-2GHQNRIO.js.map → telegram-EAVRDNFU.js.map} +0 -0
- /package/dist/{telegram-E65IWFBW.js.map → tunnel-HWJ27WDH.js.map} +0 -0
- /package/dist/{tunnel-45HA72MB.js.map → validators-GITLOFXC.js.map} +0 -0
- /package/dist/{validators-WSTBNKRW.js.map → version-AXXV6IV2.js.map} +0 -0
|
@@ -112,22 +112,48 @@ function createTunnelPlugin() {
|
|
|
112
112
|
},
|
|
113
113
|
async setup(ctx) {
|
|
114
114
|
const config = ctx.pluginConfig;
|
|
115
|
+
if (!config.enabled) {
|
|
116
|
+
ctx.log.info("Tunnel disabled");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
115
119
|
if (!config.provider) {
|
|
116
120
|
ctx.log.info("Tunnel disabled (no provider configured)");
|
|
117
121
|
return;
|
|
118
122
|
}
|
|
119
|
-
const { TunnelService } = await import("./tunnel-service-
|
|
123
|
+
const { TunnelService } = await import("./tunnel-service-ZMO4THKE.js");
|
|
120
124
|
const tunnelSvc = new TunnelService(config);
|
|
121
125
|
const publicUrl = await tunnelSvc.start();
|
|
122
126
|
service = tunnelSvc;
|
|
123
127
|
ctx.registerService("tunnel", tunnelSvc);
|
|
124
128
|
ctx.registerCommand({
|
|
125
129
|
name: "tunnel",
|
|
126
|
-
description: "
|
|
130
|
+
description: "Manage tunnels: /tunnel <port> [label] | /tunnel stop <port>",
|
|
127
131
|
category: "plugin",
|
|
128
|
-
handler: async () => {
|
|
132
|
+
handler: async (args) => {
|
|
133
|
+
const parts = args.raw.trim().split(/\s+/);
|
|
134
|
+
if (parts[0] === "stop" && parts[1]) {
|
|
135
|
+
const port = parseInt(parts[1], 10);
|
|
136
|
+
if (isNaN(port)) return { type: "error", message: "Invalid port number" };
|
|
137
|
+
try {
|
|
138
|
+
await tunnelSvc.stopTunnel(port);
|
|
139
|
+
return { type: "text", text: `Tunnel on port ${port} stopped.` };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
return { type: "error", message: err.message };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (parts[0] && parts[0] !== "") {
|
|
145
|
+
const port = parseInt(parts[0], 10);
|
|
146
|
+
if (isNaN(port)) return { type: "error", message: "Invalid port number" };
|
|
147
|
+
const label = parts.slice(1).join(" ") || void 0;
|
|
148
|
+
try {
|
|
149
|
+
const entry = await tunnelSvc.addTunnel(port, { label });
|
|
150
|
+
return { type: "text", text: `Tunnel created: ${entry.publicUrl ?? "starting..."}` };
|
|
151
|
+
} catch (err) {
|
|
152
|
+
return { type: "error", message: err.message };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
129
155
|
const url = tunnelSvc.getPublicUrl();
|
|
130
|
-
return { type: "text", text: `Tunnel
|
|
156
|
+
return { type: "text", text: url ? `Tunnel: ${url}` : "No tunnel active." };
|
|
131
157
|
}
|
|
132
158
|
});
|
|
133
159
|
ctx.registerCommand({
|
|
@@ -135,10 +161,16 @@ function createTunnelPlugin() {
|
|
|
135
161
|
description: "List active tunnels",
|
|
136
162
|
category: "plugin",
|
|
137
163
|
handler: async () => {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
164
|
+
const userTunnels = tunnelSvc.listTunnels();
|
|
165
|
+
const systemUrl = tunnelSvc.getPublicUrl();
|
|
166
|
+
const items = [
|
|
167
|
+
{ label: "System", detail: systemUrl },
|
|
168
|
+
...userTunnels.map((t) => ({
|
|
169
|
+
label: t.label ?? `Port ${t.port}`,
|
|
170
|
+
detail: `${t.publicUrl ?? t.status} (${t.provider})`
|
|
171
|
+
}))
|
|
172
|
+
];
|
|
173
|
+
return { type: "list", title: "Active Tunnels", items };
|
|
142
174
|
}
|
|
143
175
|
});
|
|
144
176
|
ctx.log.info(`Tunnel ready: ${publicUrl}`);
|
|
@@ -155,4 +187,4 @@ var tunnel_default = createTunnelPlugin();
|
|
|
155
187
|
export {
|
|
156
188
|
tunnel_default
|
|
157
189
|
};
|
|
158
|
-
//# sourceMappingURL=chunk-
|
|
190
|
+
//# sourceMappingURL=chunk-PA6MNBG4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/tunnel/index.ts"],"sourcesContent":["import type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js'\nimport type { TunnelConfig } from '../../core/config/config.js'\n\nfunction createTunnelPlugin(): OpenACPPlugin {\n let service: { stop(): Promise<void> } | null = null\n\n return {\n name: '@openacp/tunnel',\n version: '1.0.0',\n description: 'Expose local services to internet via tunnel providers',\n essential: false,\n permissions: ['services:register', 'kernel:access', 'commands:register'],\n\n async install(ctx: InstallContext) {\n const { terminal, settings, legacyConfig } = ctx\n\n // Migrate from legacy config if present\n if (legacyConfig) {\n const tunnelCfg = legacyConfig.tunnel as Record<string, unknown> | undefined\n if (tunnelCfg) {\n await settings.setAll({\n enabled: tunnelCfg.enabled ?? true,\n provider: tunnelCfg.provider ?? 'cloudflare',\n port: tunnelCfg.port ?? 3100,\n options: tunnelCfg.options ?? {},\n maxUserTunnels: tunnelCfg.maxUserTunnels ?? 5,\n storeTtlMinutes: tunnelCfg.storeTtlMinutes ?? 60,\n auth: tunnelCfg.auth ?? { enabled: false },\n })\n terminal.log.success('Tunnel settings migrated from legacy config')\n return\n }\n }\n\n // Interactive setup\n const provider = await terminal.select({\n message: 'Tunnel provider:',\n options: [\n { value: 'cloudflare', label: 'Cloudflare (cloudflared)', hint: 'Free, no account needed' },\n { value: 'ngrok', label: 'ngrok', hint: 'Requires auth token' },\n { value: 'bore', label: 'bore', hint: 'Self-hostable' },\n { value: 'tailscale', label: 'Tailscale Funnel' },\n ],\n })\n\n const portStr = await terminal.text({\n message: 'Local port to expose:',\n defaultValue: '3100',\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 1 || n > 65535) return 'Port must be 1-65535'\n return undefined\n },\n })\n\n let authToken = ''\n if (provider === 'ngrok') {\n authToken = await terminal.text({\n message: 'ngrok auth token:',\n validate: (v) => (!v.trim() ? 'Auth token cannot be empty' : undefined),\n })\n authToken = authToken.trim()\n }\n\n await settings.setAll({\n enabled: true,\n provider,\n port: Number(portStr.trim()),\n options: authToken ? { authtoken: authToken } : {},\n maxUserTunnels: 5,\n storeTtlMinutes: 60,\n auth: { enabled: false },\n })\n terminal.log.success('Tunnel settings saved')\n },\n\n async configure(ctx: InstallContext) {\n const { terminal, settings } = ctx\n const current = await settings.getAll()\n\n const choice = await terminal.select({\n message: 'What to configure?',\n options: [\n { value: 'provider', label: `Change provider (current: ${current.provider ?? 'none'})` },\n { value: 'port', label: `Change port (current: ${current.port ?? 3100})` },\n { value: 'toggle', label: `${current.enabled ? 'Disable' : 'Enable'} tunnel` },\n { value: 'done', label: 'Done' },\n ],\n })\n\n if (choice === 'provider') {\n const provider = await terminal.select({\n message: 'Tunnel provider:',\n options: [\n { value: 'cloudflare', label: 'Cloudflare' },\n { value: 'ngrok', label: 'ngrok' },\n { value: 'bore', label: 'bore' },\n { value: 'tailscale', label: 'Tailscale' },\n ],\n })\n await settings.set('provider', provider)\n terminal.log.success('Provider updated')\n } else if (choice === 'port') {\n const val = await terminal.text({\n message: 'New port:',\n defaultValue: String(current.port ?? 3100),\n validate: (v) => {\n const n = Number(v.trim())\n if (isNaN(n) || n < 1 || n > 65535) return 'Port must be 1-65535'\n return undefined\n },\n })\n await settings.set('port', Number(val.trim()))\n terminal.log.success('Port updated')\n } else if (choice === 'toggle') {\n const newState = !current.enabled\n await settings.set('enabled', newState)\n terminal.log.success(`Tunnel ${newState ? 'enabled' : 'disabled'}`)\n }\n },\n\n async uninstall(ctx: InstallContext, opts: { purge: boolean }) {\n if (opts.purge) {\n await ctx.settings.clear()\n ctx.terminal.log.success('Tunnel settings cleared')\n }\n },\n\n async setup(ctx) {\n const config = ctx.pluginConfig as Record<string, unknown>\n if (!config.enabled) {\n ctx.log.info('Tunnel disabled')\n return\n }\n if (!config.provider) {\n ctx.log.info('Tunnel disabled (no provider configured)')\n return\n }\n\n const { TunnelService } = await import('./tunnel-service.js')\n const tunnelSvc = new TunnelService(config as unknown as TunnelConfig)\n const publicUrl = await tunnelSvc.start()\n service = tunnelSvc\n\n ctx.registerService('tunnel', tunnelSvc)\n\n ctx.registerCommand({\n name: 'tunnel',\n description: 'Manage tunnels: /tunnel <port> [label] | /tunnel stop <port>',\n category: 'plugin',\n handler: async (args) => {\n const parts = args.raw.trim().split(/\\s+/)\n\n // /tunnel stop <port>\n if (parts[0] === 'stop' && parts[1]) {\n const port = parseInt(parts[1], 10)\n if (isNaN(port)) return { type: 'error', message: 'Invalid port number' }\n try {\n await tunnelSvc.stopTunnel(port)\n return { type: 'text', text: `Tunnel on port ${port} stopped.` }\n } catch (err) {\n return { type: 'error', message: (err as Error).message }\n }\n }\n\n // /tunnel <port> [label]\n if (parts[0] && parts[0] !== '') {\n const port = parseInt(parts[0], 10)\n if (isNaN(port)) return { type: 'error', message: 'Invalid port number' }\n const label = parts.slice(1).join(' ') || undefined\n try {\n const entry = await tunnelSvc.addTunnel(port, { label })\n return { type: 'text', text: `Tunnel created: ${entry.publicUrl ?? 'starting...'}` }\n } catch (err) {\n return { type: 'error', message: (err as Error).message }\n }\n }\n\n // /tunnel (no args) — show current tunnel URL\n const url = tunnelSvc.getPublicUrl()\n return { type: 'text', text: url ? `Tunnel: ${url}` : 'No tunnel active.' }\n },\n })\n\n ctx.registerCommand({\n name: 'tunnels',\n description: 'List active tunnels',\n category: 'plugin',\n handler: async () => {\n const userTunnels = tunnelSvc.listTunnels()\n const systemUrl = tunnelSvc.getPublicUrl()\n const items = [\n { label: 'System', detail: systemUrl },\n ...userTunnels.map(t => ({\n label: t.label ?? `Port ${t.port}`,\n detail: `${t.publicUrl ?? t.status} (${t.provider})`,\n })),\n ]\n return { type: 'list', title: 'Active Tunnels', items }\n },\n })\n\n ctx.log.info(`Tunnel ready: ${publicUrl}`)\n },\n\n async teardown() {\n if (service) {\n await service.stop()\n }\n },\n }\n}\n\nexport default createTunnelPlugin()\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
expandHome
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-W4LK6WJP.js";
|
|
4
4
|
|
|
5
5
|
// src/cli/daemon.ts
|
|
6
6
|
import { spawn } from "child_process";
|
|
@@ -169,4 +169,4 @@ export {
|
|
|
169
169
|
clearRunning,
|
|
170
170
|
shouldAutoStart
|
|
171
171
|
};
|
|
172
|
-
//# sourceMappingURL=chunk-
|
|
172
|
+
//# sourceMappingURL=chunk-QWVHCTCA.js.map
|
|
@@ -169,6 +169,12 @@ function createSessionLogger(sessionId, parentLogger) {
|
|
|
169
169
|
return parentLogger.child({ sessionId });
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
+
function closeSessionLogger(logger) {
|
|
173
|
+
const dest = logger.__sessionDest;
|
|
174
|
+
if (dest && typeof dest.destroy === "function") {
|
|
175
|
+
dest.destroy();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
172
178
|
async function shutdownLogger() {
|
|
173
179
|
if (!initialized) return;
|
|
174
180
|
const transport = currentTransport;
|
|
@@ -218,7 +224,8 @@ export {
|
|
|
218
224
|
setLogLevel,
|
|
219
225
|
createChildLogger,
|
|
220
226
|
createSessionLogger,
|
|
227
|
+
closeSessionLogger,
|
|
221
228
|
shutdownLogger,
|
|
222
229
|
cleanupOldSessionLogs
|
|
223
230
|
};
|
|
224
|
-
//# sourceMappingURL=chunk-
|
|
231
|
+
//# sourceMappingURL=chunk-R6KZYF7D.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/utils/log.ts"],"sourcesContent":["import pino from 'pino'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport type { LoggingConfig } from '../config/config.js'\n\nexport type Logger = pino.Logger\n\n// --- Default console-only logger (pre-init) ---\nlet rootLogger: pino.Logger = pino({\n level: 'debug',\n transport: { target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard' } },\n})\nlet initialized = false\nlet logDir: string | undefined\nlet currentTransport: ReturnType<typeof pino.transport> | undefined\n\nfunction expandHome(p: string): string {\n return p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : p\n}\n\n// --- Variadic wrapper for backward compatibility ---\nfunction wrapVariadic(logger: pino.Logger) {\n return {\n info: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.info(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.info(args.map(String).join(' '))\n }\n },\n warn: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.warn(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.warn(args.map(String).join(' '))\n }\n },\n error: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.error(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.error(args.map(String).join(' '))\n }\n },\n debug: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.debug(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.debug(args.map(String).join(' '))\n }\n },\n fatal: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.fatal(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.fatal(args.map(String).join(' '))\n }\n },\n child: (bindings: pino.Bindings) => logger.child(bindings),\n }\n}\n\nexport const log = wrapVariadic(rootLogger)\n\n// --- Mute/unmute (suppress pino output during interactive prompts) ---\n\nlet muteCount = 0\nlet savedLevel = 'info'\n\nexport function muteLogger(): void {\n if (muteCount === 0) {\n savedLevel = rootLogger.level\n rootLogger.level = 'silent'\n }\n muteCount++\n}\n\nexport function unmuteLogger(): void {\n muteCount--\n if (muteCount <= 0) {\n muteCount = 0\n rootLogger.level = savedLevel\n }\n}\n\n// --- Public API ---\n\nexport function initLogger(config: LoggingConfig): Logger {\n if (initialized) return rootLogger\n\n const resolvedLogDir = expandHome(config.logDir)\n logDir = resolvedLogDir\n\n try {\n fs.mkdirSync(resolvedLogDir, { recursive: true })\n fs.mkdirSync(path.join(resolvedLogDir, 'sessions'), { recursive: true })\n } catch (err) {\n console.error(`[WARN] Failed to create log directory ${resolvedLogDir}, falling back to console-only:`, err)\n return rootLogger\n }\n\n const transports = pino.transport({\n targets: [\n {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'HH:mm:ss',\n ignore: 'pid,hostname',\n singleLine: true,\n },\n level: config.level,\n },\n {\n target: 'pino-roll',\n options: {\n file: path.join(resolvedLogDir, 'openacp.log'),\n size: config.maxFileSize,\n limit: { count: config.maxFiles },\n },\n level: config.level,\n },\n ],\n })\n\n currentTransport = transports\n rootLogger = pino({ level: config.level }, transports)\n initialized = true\n\n // Update the default log wrapper to use the new root logger\n Object.assign(log, wrapVariadic(rootLogger))\n\n return rootLogger\n}\n\n/** Change log level at runtime. Pino transport targets respect parent level changes automatically. */\nexport function setLogLevel(level: string): void {\n rootLogger.level = level\n}\n\nexport function createChildLogger(context: { module: string; [key: string]: unknown }): Logger {\n // Return a proxy that always delegates to the current rootLogger.\n // This ensures child loggers created at module-level (before initLogger)\n // pick up the initialized logger with pino-pretty transport.\n return new Proxy({} as Logger, {\n get(_target, prop, receiver) {\n const child = rootLogger.child(context)\n const value = Reflect.get(child, prop, receiver)\n return typeof value === 'function' ? value.bind(child) : value\n },\n })\n}\n\nexport function createSessionLogger(sessionId: string, parentLogger: Logger): Logger {\n const sessionLogDir = logDir ? path.join(logDir, 'sessions') : undefined\n if (!sessionLogDir) {\n return parentLogger.child({ sessionId })\n }\n\n try {\n const sessionLogPath = path.join(sessionLogDir, `${sessionId}.log`)\n const dest = pino.destination(sessionLogPath)\n const sessionFileLogger = pino({ level: parentLogger.level }, dest).child({ sessionId })\n\n // Create a logger that writes to both parent (combined) and session file\n const combinedChild = parentLogger.child({ sessionId })\n const originalInfo = combinedChild.info.bind(combinedChild)\n const originalWarn = combinedChild.warn.bind(combinedChild)\n const originalError = combinedChild.error.bind(combinedChild)\n const originalDebug = combinedChild.debug.bind(combinedChild)\n const originalFatal = combinedChild.fatal.bind(combinedChild)\n\n // Proxy log methods to write to both destinations\n combinedChild.info = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.info(objOrMsg, ...rest)\n return originalInfo(objOrMsg, ...rest)\n }) as any\n combinedChild.warn = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.warn(objOrMsg, ...rest)\n return originalWarn(objOrMsg, ...rest)\n }) as any\n combinedChild.error = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.error(objOrMsg, ...rest)\n return originalError(objOrMsg, ...rest)\n }) as any\n combinedChild.debug = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.debug(objOrMsg, ...rest)\n return originalDebug(objOrMsg, ...rest)\n }) as any\n combinedChild.fatal = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.fatal(objOrMsg, ...rest)\n return originalFatal(objOrMsg, ...rest)\n }) as any\n\n // Store dest for cleanup\n ;(combinedChild as any).__sessionDest = dest\n\n return combinedChild\n } catch (err) {\n // Graceful degradation: session file failed, just use combined log\n parentLogger.warn({ sessionId, err }, 'Failed to create session log file, using combined log only')\n return parentLogger.child({ sessionId })\n }\n}\n\nexport async function shutdownLogger(): Promise<void> {\n if (!initialized) return\n\n const transport = currentTransport\n\n // Reset state immediately so re-init is possible\n rootLogger = pino({ level: 'debug' })\n Object.assign(log, wrapVariadic(rootLogger))\n currentTransport = undefined\n logDir = undefined\n initialized = false\n\n if (transport) {\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, 3000)\n transport.on('close', () => {\n clearTimeout(timeout)\n resolve()\n })\n transport.end()\n })\n }\n}\n\nexport async function cleanupOldSessionLogs(retentionDays: number): Promise<void> {\n if (!logDir) return\n\n const sessionsDir = path.join(logDir, 'sessions')\n try {\n const files = await fs.promises.readdir(sessionsDir)\n const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000\n\n for (const file of files) {\n try {\n const filePath = path.join(sessionsDir, file)\n const stat = await fs.promises.stat(filePath)\n if (stat.mtimeMs < cutoff) {\n await fs.promises.unlink(filePath)\n rootLogger.debug({ file }, 'Deleted old session log')\n }\n } catch (err) {\n rootLogger.warn({ file, err }, 'Failed to delete old session log')\n }\n }\n } catch {\n // Sessions directory doesn't exist — no-op\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/utils/log.ts"],"sourcesContent":["import pino from 'pino'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport type { LoggingConfig } from '../config/config.js'\n\nexport type Logger = pino.Logger\n\n// --- Default console-only logger (pre-init) ---\nlet rootLogger: pino.Logger = pino({\n level: 'debug',\n transport: { target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard' } },\n})\nlet initialized = false\nlet logDir: string | undefined\nlet currentTransport: ReturnType<typeof pino.transport> | undefined\n\nfunction expandHome(p: string): string {\n return p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : p\n}\n\n// --- Variadic wrapper for backward compatibility ---\nfunction wrapVariadic(logger: pino.Logger) {\n return {\n info: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.info(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.info(args.map(String).join(' '))\n }\n },\n warn: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.warn(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.warn(args.map(String).join(' '))\n }\n },\n error: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.error(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.error(args.map(String).join(' '))\n }\n },\n debug: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.debug(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.debug(args.map(String).join(' '))\n }\n },\n fatal: (...args: unknown[]) => {\n if (args.length === 0) return\n if (typeof args[0] === 'object' && args[0] !== null && !(args[0] instanceof Error)) {\n logger.fatal(args[0] as object, args.slice(1).join(' '))\n } else {\n logger.fatal(args.map(String).join(' '))\n }\n },\n child: (bindings: pino.Bindings) => logger.child(bindings),\n }\n}\n\nexport const log = wrapVariadic(rootLogger)\n\n// --- Mute/unmute (suppress pino output during interactive prompts) ---\n\nlet muteCount = 0\nlet savedLevel = 'info'\n\nexport function muteLogger(): void {\n if (muteCount === 0) {\n savedLevel = rootLogger.level\n rootLogger.level = 'silent'\n }\n muteCount++\n}\n\nexport function unmuteLogger(): void {\n muteCount--\n if (muteCount <= 0) {\n muteCount = 0\n rootLogger.level = savedLevel\n }\n}\n\n// --- Public API ---\n\nexport function initLogger(config: LoggingConfig): Logger {\n if (initialized) return rootLogger\n\n const resolvedLogDir = expandHome(config.logDir)\n logDir = resolvedLogDir\n\n try {\n fs.mkdirSync(resolvedLogDir, { recursive: true })\n fs.mkdirSync(path.join(resolvedLogDir, 'sessions'), { recursive: true })\n } catch (err) {\n console.error(`[WARN] Failed to create log directory ${resolvedLogDir}, falling back to console-only:`, err)\n return rootLogger\n }\n\n const transports = pino.transport({\n targets: [\n {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'HH:mm:ss',\n ignore: 'pid,hostname',\n singleLine: true,\n },\n level: config.level,\n },\n {\n target: 'pino-roll',\n options: {\n file: path.join(resolvedLogDir, 'openacp.log'),\n size: config.maxFileSize,\n limit: { count: config.maxFiles },\n },\n level: config.level,\n },\n ],\n })\n\n currentTransport = transports\n rootLogger = pino({ level: config.level }, transports)\n initialized = true\n\n // Update the default log wrapper to use the new root logger\n Object.assign(log, wrapVariadic(rootLogger))\n\n return rootLogger\n}\n\n/** Change log level at runtime. Pino transport targets respect parent level changes automatically. */\nexport function setLogLevel(level: string): void {\n rootLogger.level = level\n}\n\nexport function createChildLogger(context: { module: string; [key: string]: unknown }): Logger {\n // Return a proxy that always delegates to the current rootLogger.\n // This ensures child loggers created at module-level (before initLogger)\n // pick up the initialized logger with pino-pretty transport.\n return new Proxy({} as Logger, {\n get(_target, prop, receiver) {\n const child = rootLogger.child(context)\n const value = Reflect.get(child, prop, receiver)\n return typeof value === 'function' ? value.bind(child) : value\n },\n })\n}\n\nexport function createSessionLogger(sessionId: string, parentLogger: Logger): Logger {\n const sessionLogDir = logDir ? path.join(logDir, 'sessions') : undefined\n if (!sessionLogDir) {\n return parentLogger.child({ sessionId })\n }\n\n try {\n const sessionLogPath = path.join(sessionLogDir, `${sessionId}.log`)\n const dest = pino.destination(sessionLogPath)\n const sessionFileLogger = pino({ level: parentLogger.level }, dest).child({ sessionId })\n\n // Create a logger that writes to both parent (combined) and session file\n const combinedChild = parentLogger.child({ sessionId })\n const originalInfo = combinedChild.info.bind(combinedChild)\n const originalWarn = combinedChild.warn.bind(combinedChild)\n const originalError = combinedChild.error.bind(combinedChild)\n const originalDebug = combinedChild.debug.bind(combinedChild)\n const originalFatal = combinedChild.fatal.bind(combinedChild)\n\n // Proxy log methods to write to both destinations\n combinedChild.info = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.info(objOrMsg, ...rest)\n return originalInfo(objOrMsg, ...rest)\n }) as any\n combinedChild.warn = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.warn(objOrMsg, ...rest)\n return originalWarn(objOrMsg, ...rest)\n }) as any\n combinedChild.error = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.error(objOrMsg, ...rest)\n return originalError(objOrMsg, ...rest)\n }) as any\n combinedChild.debug = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.debug(objOrMsg, ...rest)\n return originalDebug(objOrMsg, ...rest)\n }) as any\n combinedChild.fatal = ((objOrMsg: any, ...rest: any[]) => {\n sessionFileLogger.fatal(objOrMsg, ...rest)\n return originalFatal(objOrMsg, ...rest)\n }) as any\n\n // Store dest for cleanup\n ;(combinedChild as any).__sessionDest = dest\n\n return combinedChild\n } catch (err) {\n // Graceful degradation: session file failed, just use combined log\n parentLogger.warn({ sessionId, err }, 'Failed to create session log file, using combined log only')\n return parentLogger.child({ sessionId })\n }\n}\n\nexport function closeSessionLogger(logger: Logger): void {\n const dest = (logger as any).__sessionDest\n if (dest && typeof dest.destroy === 'function') {\n dest.destroy()\n }\n}\n\nexport async function shutdownLogger(): Promise<void> {\n if (!initialized) return\n\n const transport = currentTransport\n\n // Reset state immediately so re-init is possible\n rootLogger = pino({ level: 'debug' })\n Object.assign(log, wrapVariadic(rootLogger))\n currentTransport = undefined\n logDir = undefined\n initialized = false\n\n if (transport) {\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, 3000)\n transport.on('close', () => {\n clearTimeout(timeout)\n resolve()\n })\n transport.end()\n })\n }\n}\n\nexport async function cleanupOldSessionLogs(retentionDays: number): Promise<void> {\n if (!logDir) return\n\n const sessionsDir = path.join(logDir, 'sessions')\n try {\n const files = await fs.promises.readdir(sessionsDir)\n const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000\n\n for (const file of files) {\n try {\n const filePath = path.join(sessionsDir, file)\n const stat = await fs.promises.stat(filePath)\n if (stat.mtimeMs < cutoff) {\n await fs.promises.unlink(filePath)\n rootLogger.debug({ file }, 'Deleted old session log')\n }\n } catch (err) {\n rootLogger.warn({ file, err }, 'Failed to delete old session log')\n }\n }\n } catch {\n // Sessions directory doesn't exist — no-op\n }\n}\n"],"mappings":";AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AAMf,IAAI,aAA0B,KAAK;AAAA,EACjC,OAAO;AAAA,EACP,WAAW,EAAE,QAAQ,eAAe,SAAS,EAAE,UAAU,MAAM,eAAe,eAAe,EAAE;AACjG,CAAC;AACD,IAAI,cAAc;AAClB,IAAI;AACJ,IAAI;AAEJ,SAAS,WAAW,GAAmB;AACrC,SAAO,EAAE,WAAW,GAAG,IAAI,KAAK,KAAK,GAAG,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC,IAAI;AACnE;AAGA,SAAS,aAAa,QAAqB;AACzC,SAAO;AAAA,IACL,MAAM,IAAI,SAAoB;AAC5B,UAAI,KAAK,WAAW,EAAG;AACvB,UAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,QAAQ,EAAE,KAAK,CAAC,aAAa,QAAQ;AAClF,eAAO,KAAK,KAAK,CAAC,GAAa,KAAK,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,MACxD,OAAO;AACL,eAAO,KAAK,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,MACxC;AAAA,IACF;AAAA,IACA,MAAM,IAAI,SAAoB;AAC5B,UAAI,KAAK,WAAW,EAAG;AACvB,UAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,QAAQ,EAAE,KAAK,CAAC,aAAa,QAAQ;AAClF,eAAO,KAAK,KAAK,CAAC,GAAa,KAAK,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,MACxD,OAAO;AACL,eAAO,KAAK,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,MACxC;AAAA,IACF;AAAA,IACA,OAAO,IAAI,SAAoB;AAC7B,UAAI,KAAK,WAAW,EAAG;AACvB,UAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,QAAQ,EAAE,KAAK,CAAC,aAAa,QAAQ;AAClF,eAAO,MAAM,KAAK,CAAC,GAAa,KAAK,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,MACzD,OAAO;AACL,eAAO,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,IACA,OAAO,IAAI,SAAoB;AAC7B,UAAI,KAAK,WAAW,EAAG;AACvB,UAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,QAAQ,EAAE,KAAK,CAAC,aAAa,QAAQ;AAClF,eAAO,MAAM,KAAK,CAAC,GAAa,KAAK,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,MACzD,OAAO;AACL,eAAO,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,IACA,OAAO,IAAI,SAAoB;AAC7B,UAAI,KAAK,WAAW,EAAG;AACvB,UAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,QAAQ,EAAE,KAAK,CAAC,aAAa,QAAQ;AAClF,eAAO,MAAM,KAAK,CAAC,GAAa,KAAK,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,MACzD,OAAO;AACL,eAAO,MAAM,KAAK,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,IACA,OAAO,CAAC,aAA4B,OAAO,MAAM,QAAQ;AAAA,EAC3D;AACF;AAEO,IAAM,MAAM,aAAa,UAAU;AAI1C,IAAI,YAAY;AAChB,IAAI,aAAa;AAEV,SAAS,aAAmB;AACjC,MAAI,cAAc,GAAG;AACnB,iBAAa,WAAW;AACxB,eAAW,QAAQ;AAAA,EACrB;AACA;AACF;AAEO,SAAS,eAAqB;AACnC;AACA,MAAI,aAAa,GAAG;AAClB,gBAAY;AACZ,eAAW,QAAQ;AAAA,EACrB;AACF;AAIO,SAAS,WAAW,QAA+B;AACxD,MAAI,YAAa,QAAO;AAExB,QAAM,iBAAiB,WAAW,OAAO,MAAM;AAC/C,WAAS;AAET,MAAI;AACF,OAAG,UAAU,gBAAgB,EAAE,WAAW,KAAK,CAAC;AAChD,OAAG,UAAU,KAAK,KAAK,gBAAgB,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EACzE,SAAS,KAAK;AACZ,YAAQ,MAAM,yCAAyC,cAAc,mCAAmC,GAAG;AAC3G,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,KAAK,UAAU;AAAA,IAChC,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,UAAU;AAAA,UACV,eAAe;AAAA,UACf,QAAQ;AAAA,UACR,YAAY;AAAA,QACd;AAAA,QACA,OAAO,OAAO;AAAA,MAChB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,MAAM,KAAK,KAAK,gBAAgB,aAAa;AAAA,UAC7C,MAAM,OAAO;AAAA,UACb,OAAO,EAAE,OAAO,OAAO,SAAS;AAAA,QAClC;AAAA,QACA,OAAO,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,EACF,CAAC;AAED,qBAAmB;AACnB,eAAa,KAAK,EAAE,OAAO,OAAO,MAAM,GAAG,UAAU;AACrD,gBAAc;AAGd,SAAO,OAAO,KAAK,aAAa,UAAU,CAAC;AAE3C,SAAO;AACT;AAGO,SAAS,YAAY,OAAqB;AAC/C,aAAW,QAAQ;AACrB;AAEO,SAAS,kBAAkB,SAA6D;AAI7F,SAAO,IAAI,MAAM,CAAC,GAAa;AAAA,IAC7B,IAAI,SAAS,MAAM,UAAU;AAC3B,YAAM,QAAQ,WAAW,MAAM,OAAO;AACtC,YAAM,QAAQ,QAAQ,IAAI,OAAO,MAAM,QAAQ;AAC/C,aAAO,OAAO,UAAU,aAAa,MAAM,KAAK,KAAK,IAAI;AAAA,IAC3D;AAAA,EACF,CAAC;AACH;AAEO,SAAS,oBAAoB,WAAmB,cAA8B;AACnF,QAAM,gBAAgB,SAAS,KAAK,KAAK,QAAQ,UAAU,IAAI;AAC/D,MAAI,CAAC,eAAe;AAClB,WAAO,aAAa,MAAM,EAAE,UAAU,CAAC;AAAA,EACzC;AAEA,MAAI;AACF,UAAM,iBAAiB,KAAK,KAAK,eAAe,GAAG,SAAS,MAAM;AAClE,UAAM,OAAO,KAAK,YAAY,cAAc;AAC5C,UAAM,oBAAoB,KAAK,EAAE,OAAO,aAAa,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC;AAGvF,UAAM,gBAAgB,aAAa,MAAM,EAAE,UAAU,CAAC;AACtD,UAAM,eAAe,cAAc,KAAK,KAAK,aAAa;AAC1D,UAAM,eAAe,cAAc,KAAK,KAAK,aAAa;AAC1D,UAAM,gBAAgB,cAAc,MAAM,KAAK,aAAa;AAC5D,UAAM,gBAAgB,cAAc,MAAM,KAAK,aAAa;AAC5D,UAAM,gBAAgB,cAAc,MAAM,KAAK,aAAa;AAG5D,kBAAc,QAAQ,CAAC,aAAkB,SAAgB;AACvD,wBAAkB,KAAK,UAAU,GAAG,IAAI;AACxC,aAAO,aAAa,UAAU,GAAG,IAAI;AAAA,IACvC;AACA,kBAAc,QAAQ,CAAC,aAAkB,SAAgB;AACvD,wBAAkB,KAAK,UAAU,GAAG,IAAI;AACxC,aAAO,aAAa,UAAU,GAAG,IAAI;AAAA,IACvC;AACA,kBAAc,SAAS,CAAC,aAAkB,SAAgB;AACxD,wBAAkB,MAAM,UAAU,GAAG,IAAI;AACzC,aAAO,cAAc,UAAU,GAAG,IAAI;AAAA,IACxC;AACA,kBAAc,SAAS,CAAC,aAAkB,SAAgB;AACxD,wBAAkB,MAAM,UAAU,GAAG,IAAI;AACzC,aAAO,cAAc,UAAU,GAAG,IAAI;AAAA,IACxC;AACA,kBAAc,SAAS,CAAC,aAAkB,SAAgB;AACxD,wBAAkB,MAAM,UAAU,GAAG,IAAI;AACzC,aAAO,cAAc,UAAU,GAAG,IAAI;AAAA,IACxC;AAGC,IAAC,cAAsB,gBAAgB;AAExC,WAAO;AAAA,EACT,SAAS,KAAK;AAEZ,iBAAa,KAAK,EAAE,WAAW,IAAI,GAAG,4DAA4D;AAClG,WAAO,aAAa,MAAM,EAAE,UAAU,CAAC;AAAA,EACzC;AACF;AAEO,SAAS,mBAAmB,QAAsB;AACvD,QAAM,OAAQ,OAAe;AAC7B,MAAI,QAAQ,OAAO,KAAK,YAAY,YAAY;AAC9C,SAAK,QAAQ;AAAA,EACf;AACF;AAEA,eAAsB,iBAAgC;AACpD,MAAI,CAAC,YAAa;AAElB,QAAM,YAAY;AAGlB,eAAa,KAAK,EAAE,OAAO,QAAQ,CAAC;AACpC,SAAO,OAAO,KAAK,aAAa,UAAU,CAAC;AAC3C,qBAAmB;AACnB,WAAS;AACT,gBAAc;AAEd,MAAI,WAAW;AACb,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,YAAM,UAAU,WAAW,SAAS,GAAI;AACxC,gBAAU,GAAG,SAAS,MAAM;AAC1B,qBAAa,OAAO;AACpB,gBAAQ;AAAA,MACV,CAAC;AACD,gBAAU,IAAI;AAAA,IAChB,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,sBAAsB,eAAsC;AAChF,MAAI,CAAC,OAAQ;AAEb,QAAM,cAAc,KAAK,KAAK,QAAQ,UAAU;AAChD,MAAI;AACF,UAAM,QAAQ,MAAM,GAAG,SAAS,QAAQ,WAAW;AACnD,UAAM,SAAS,KAAK,IAAI,IAAI,gBAAgB,KAAK,KAAK,KAAK;AAE3D,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,WAAW,KAAK,KAAK,aAAa,IAAI;AAC5C,cAAM,OAAO,MAAM,GAAG,SAAS,KAAK,QAAQ;AAC5C,YAAI,KAAK,UAAU,QAAQ;AACzB,gBAAM,GAAG,SAAS,OAAO,QAAQ;AACjC,qBAAW,MAAM,EAAE,KAAK,GAAG,yBAAyB;AAAA,QACtD;AAAA,MACF,SAAS,KAAK;AACZ,mBAAW,KAAK,EAAE,MAAM,IAAI,GAAG,kCAAkC;AAAA,MACnE;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACF;","names":[]}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
installNpmPlugin
|
|
3
|
+
} from "./chunk-5WGVYX3C.js";
|
|
4
|
+
import {
|
|
3
5
|
GroqSTT,
|
|
4
6
|
SpeechService
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2KT6TROD.js";
|
|
6
8
|
|
|
7
9
|
// src/plugins/speech/index.ts
|
|
10
|
+
var EDGE_TTS_PLUGIN = "@openacp/msedge-tts-plugin";
|
|
8
11
|
var speechPlugin = {
|
|
9
12
|
name: "@openacp/speech",
|
|
10
13
|
version: "1.0.0",
|
|
11
14
|
description: "Text-to-speech and speech-to-text with pluggable providers",
|
|
12
15
|
essential: false,
|
|
13
16
|
optionalPluginDependencies: { "@openacp/file-service": "^1.0.0" },
|
|
14
|
-
permissions: ["services:register", "commands:register"],
|
|
17
|
+
permissions: ["services:register", "commands:register", "kernel:access"],
|
|
15
18
|
async install(ctx) {
|
|
16
19
|
const { terminal, settings, legacyConfig } = ctx;
|
|
17
20
|
if (legacyConfig) {
|
|
@@ -59,6 +62,13 @@ var speechPlugin = {
|
|
|
59
62
|
});
|
|
60
63
|
let ttsVoice = "";
|
|
61
64
|
if (ttsProvider === "edge-tts") {
|
|
65
|
+
terminal.log.info("Installing Edge TTS plugin...");
|
|
66
|
+
try {
|
|
67
|
+
await installNpmPlugin(EDGE_TTS_PLUGIN);
|
|
68
|
+
terminal.log.success("Edge TTS plugin installed");
|
|
69
|
+
} catch (err) {
|
|
70
|
+
terminal.log.warning(`Failed to install Edge TTS plugin: ${err}. You can install it later with: openacp plugin install ${EDGE_TTS_PLUGIN}`);
|
|
71
|
+
}
|
|
62
72
|
ttsVoice = await terminal.text({
|
|
63
73
|
message: "TTS voice (leave blank for default):",
|
|
64
74
|
placeholder: "e.g. en-US-AriaNeural"
|
|
@@ -111,7 +121,6 @@ var speechPlugin = {
|
|
|
111
121
|
async setup(ctx) {
|
|
112
122
|
const config = ctx.pluginConfig;
|
|
113
123
|
const groqApiKey = config.groqApiKey;
|
|
114
|
-
const ttsVoice = config.ttsVoice;
|
|
115
124
|
const sttProvider = groqApiKey ? "groq" : null;
|
|
116
125
|
const speechConfig = {
|
|
117
126
|
stt: {
|
|
@@ -119,7 +128,7 @@ var speechPlugin = {
|
|
|
119
128
|
providers: groqApiKey ? { groq: { apiKey: groqApiKey } } : {}
|
|
120
129
|
},
|
|
121
130
|
tts: {
|
|
122
|
-
provider: "edge-tts",
|
|
131
|
+
provider: config.ttsProvider ?? "edge-tts",
|
|
123
132
|
providers: {}
|
|
124
133
|
}
|
|
125
134
|
};
|
|
@@ -127,7 +136,6 @@ var speechPlugin = {
|
|
|
127
136
|
if (groqApiKey) {
|
|
128
137
|
service.registerSTTProvider("groq", new GroqSTT(groqApiKey));
|
|
129
138
|
}
|
|
130
|
-
service.registerTTSProvider("edge-tts", new EdgeTTS(ttsVoice));
|
|
131
139
|
service.setProviderFactory((cfg) => {
|
|
132
140
|
const sttMap = /* @__PURE__ */ new Map();
|
|
133
141
|
const ttsMap = /* @__PURE__ */ new Map();
|
|
@@ -135,23 +143,77 @@ var speechPlugin = {
|
|
|
135
143
|
if (groqCfg?.apiKey) {
|
|
136
144
|
sttMap.set("groq", new GroqSTT(groqCfg.apiKey, groqCfg.model));
|
|
137
145
|
}
|
|
138
|
-
const edgeVoice = cfg.tts?.providers?.["edge-tts"]?.voice;
|
|
139
|
-
ttsMap.set("edge-tts", new EdgeTTS(edgeVoice));
|
|
140
146
|
return { stt: sttMap, tts: ttsMap };
|
|
141
147
|
});
|
|
142
148
|
ctx.registerService("speech", service);
|
|
149
|
+
const setSessionVoiceMode = (pluginCtx, sessionId, voiceMode) => {
|
|
150
|
+
if (!sessionId) return;
|
|
151
|
+
try {
|
|
152
|
+
const sessionManager = pluginCtx.sessions;
|
|
153
|
+
const session = sessionManager.getSession(sessionId);
|
|
154
|
+
if (session) {
|
|
155
|
+
session.setVoiceMode(voiceMode);
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
};
|
|
143
160
|
ctx.registerCommand({
|
|
144
161
|
name: "tts",
|
|
145
162
|
description: "Toggle text-to-speech",
|
|
146
|
-
usage: "on|off",
|
|
163
|
+
usage: "on|off|next|install",
|
|
147
164
|
category: "plugin",
|
|
148
165
|
handler: async (args) => {
|
|
149
166
|
const mode = args.raw.trim().toLowerCase();
|
|
150
|
-
if (mode === "on"
|
|
151
|
-
|
|
167
|
+
if ((mode === "on" || mode === "" || mode === "next") && !service.isTTSAvailable()) {
|
|
168
|
+
return {
|
|
169
|
+
type: "menu",
|
|
170
|
+
title: "TTS provider not installed. Install Edge TTS plugin?",
|
|
171
|
+
options: [
|
|
172
|
+
{ label: "Install Edge TTS", command: "/tts install" },
|
|
173
|
+
{ label: "Cancel", command: "/tts off" }
|
|
174
|
+
]
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (mode === "install") {
|
|
178
|
+
try {
|
|
179
|
+
const mod = await installNpmPlugin(EDGE_TTS_PLUGIN);
|
|
180
|
+
const plugin = mod.default;
|
|
181
|
+
if (plugin && ctx.core) {
|
|
182
|
+
const lm = ctx.core.lifecycleManager;
|
|
183
|
+
const registry = lm.registry;
|
|
184
|
+
if (registry) {
|
|
185
|
+
registry.register(plugin.name, {
|
|
186
|
+
version: plugin.version,
|
|
187
|
+
source: "npm",
|
|
188
|
+
enabled: true,
|
|
189
|
+
settingsPath: "",
|
|
190
|
+
description: plugin.description
|
|
191
|
+
});
|
|
192
|
+
await registry.save();
|
|
193
|
+
}
|
|
194
|
+
await lm.boot([plugin]);
|
|
195
|
+
}
|
|
196
|
+
return { type: "text", text: "Edge TTS plugin installed and ready! Use /tts on to enable." };
|
|
197
|
+
} catch (err) {
|
|
198
|
+
return { type: "error", message: `Failed to install Edge TTS plugin: ${err}. Try manually: openacp plugin install ${EDGE_TTS_PLUGIN}` };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (mode === "on") {
|
|
202
|
+
setSessionVoiceMode(ctx, args.sessionId, "on");
|
|
203
|
+
return { type: "text", text: "Text-to-speech enabled" };
|
|
204
|
+
}
|
|
205
|
+
if (mode === "off") {
|
|
206
|
+
setSessionVoiceMode(ctx, args.sessionId, "off");
|
|
207
|
+
return { type: "text", text: "Text-to-speech disabled" };
|
|
208
|
+
}
|
|
209
|
+
if (mode === "next") {
|
|
210
|
+
setSessionVoiceMode(ctx, args.sessionId, "next");
|
|
211
|
+
return { type: "text", text: "Text-to-speech enabled for next message" };
|
|
212
|
+
}
|
|
152
213
|
return { type: "menu", title: "Text to Speech", options: [
|
|
153
214
|
{ label: "Enable", command: "/tts on" },
|
|
154
|
-
{ label: "Disable", command: "/tts off" }
|
|
215
|
+
{ label: "Disable", command: "/tts off" },
|
|
216
|
+
{ label: "Next message only", command: "/tts next" }
|
|
155
217
|
] };
|
|
156
218
|
}
|
|
157
219
|
});
|
|
@@ -163,4 +225,4 @@ var speech_default = speechPlugin;
|
|
|
163
225
|
export {
|
|
164
226
|
speech_default
|
|
165
227
|
};
|
|
166
|
-
//# sourceMappingURL=chunk-
|
|
228
|
+
//# sourceMappingURL=chunk-TMVTSWVH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/speech/index.ts"],"sourcesContent":["import type { OpenACPPlugin, InstallContext, PluginContext } from '../../core/plugin/types.js'\nimport type { OpenACPCore } from '../../core/core.js'\nimport type { Session } from '../../core/sessions/session.js'\nimport { SpeechService, GroqSTT } from './exports.js'\nimport type { SpeechServiceConfig } from './exports.js'\nimport { installNpmPlugin } from '../../core/plugin/plugin-installer.js'\n\nconst EDGE_TTS_PLUGIN = '@openacp/msedge-tts-plugin'\n\nconst speechPlugin: OpenACPPlugin = {\n name: '@openacp/speech',\n version: '1.0.0',\n description: 'Text-to-speech and speech-to-text with pluggable providers',\n essential: false,\n optionalPluginDependencies: { '@openacp/file-service': '^1.0.0' },\n permissions: ['services:register', 'commands:register', 'kernel:access'],\n\n async install(ctx: InstallContext) {\n const { terminal, settings, legacyConfig } = ctx\n\n // Migrate from legacy config if present\n if (legacyConfig) {\n const speechCfg = legacyConfig.speech as Record<string, unknown> | undefined\n if (speechCfg) {\n const stt = speechCfg.stt as Record<string, unknown> | undefined\n const tts = speechCfg.tts as Record<string, unknown> | undefined\n const groqProviders = stt?.providers as Record<string, unknown> | undefined\n const groqConfig = groqProviders?.groq as Record<string, unknown> | undefined\n await settings.setAll({\n sttProvider: stt?.provider ?? null,\n groqApiKey: groqConfig?.apiKey ?? '',\n ttsProvider: tts?.provider ?? 'edge-tts',\n ttsVoice: '',\n })\n terminal.log.success('Speech settings migrated from legacy config')\n return\n }\n }\n\n // Interactive setup\n const enableStt = await terminal.confirm({\n message: 'Enable speech-to-text (STT)?',\n initialValue: false,\n })\n\n let sttProvider: string | null = null\n let groqApiKey = ''\n\n if (enableStt) {\n sttProvider = await terminal.select({\n message: 'STT provider:',\n options: [{ value: 'groq', label: 'Groq (Whisper)', hint: 'Fast and affordable' }],\n })\n\n if (sttProvider === 'groq') {\n groqApiKey = await terminal.text({\n message: 'Groq API key:',\n validate: (v) => (!v.trim() ? 'API key cannot be empty' : undefined),\n })\n groqApiKey = groqApiKey.trim()\n }\n }\n\n const ttsProvider = await terminal.select({\n message: 'TTS provider:',\n options: [\n { value: 'edge-tts', label: 'Edge TTS', hint: 'Free, good quality' },\n { value: 'none', label: 'None (disable TTS)' },\n ],\n })\n\n let ttsVoice = ''\n if (ttsProvider === 'edge-tts') {\n terminal.log.info('Installing Edge TTS plugin...')\n try {\n await installNpmPlugin(EDGE_TTS_PLUGIN)\n terminal.log.success('Edge TTS plugin installed')\n } catch (err) {\n terminal.log.warning(`Failed to install Edge TTS plugin: ${err}. You can install it later with: openacp plugin install ${EDGE_TTS_PLUGIN}`)\n }\n\n ttsVoice = await terminal.text({\n message: 'TTS voice (leave blank for default):',\n placeholder: 'e.g. en-US-AriaNeural',\n })\n ttsVoice = ttsVoice.trim()\n }\n\n await settings.setAll({\n sttProvider,\n groqApiKey,\n ttsProvider: ttsProvider === 'none' ? null : ttsProvider,\n ttsVoice,\n })\n terminal.log.success('Speech settings saved')\n },\n\n async configure(ctx: InstallContext) {\n const { terminal, settings } = ctx\n const current = await settings.getAll()\n\n const choice = await terminal.select({\n message: 'What to configure?',\n options: [\n { value: 'stt', label: 'Change STT provider/key' },\n { value: 'tts', label: 'Change TTS provider/voice' },\n { value: 'done', label: 'Done' },\n ],\n })\n\n if (choice === 'stt') {\n const key = await terminal.text({\n message: 'Groq API key (leave blank to disable STT):',\n defaultValue: (current.groqApiKey as string) ?? '',\n })\n const trimmed = key.trim()\n await settings.set('sttProvider', trimmed ? 'groq' : null)\n await settings.set('groqApiKey', trimmed)\n terminal.log.success('STT settings updated')\n } else if (choice === 'tts') {\n const voice = await terminal.text({\n message: 'TTS voice (leave blank for default):',\n defaultValue: (current.ttsVoice as string) ?? '',\n })\n await settings.set('ttsVoice', voice.trim())\n terminal.log.success('TTS settings updated')\n }\n },\n\n async uninstall(ctx: InstallContext, opts: { purge: boolean }) {\n if (opts.purge) {\n await ctx.settings.clear()\n ctx.terminal.log.success('Speech settings cleared')\n }\n },\n\n async setup(ctx) {\n const config = ctx.pluginConfig as Record<string, unknown>\n const groqApiKey = config.groqApiKey as string | undefined\n\n const sttProvider = groqApiKey ? 'groq' : null\n const speechConfig: SpeechServiceConfig = {\n stt: {\n provider: sttProvider,\n providers: groqApiKey ? { groq: { apiKey: groqApiKey } } : {},\n },\n tts: {\n provider: (config.ttsProvider as string) ?? 'edge-tts',\n providers: {},\n },\n }\n\n const service = new SpeechService(speechConfig)\n\n if (groqApiKey) {\n service.registerSTTProvider('groq', new GroqSTT(groqApiKey))\n }\n\n // TTS provider is now registered by @openacp/msedge-tts-plugin (no EdgeTTS here)\n\n // Register provider factory for hot-reload (STT only — TTS providers are managed by external plugins)\n service.setProviderFactory((cfg) => {\n const sttMap = new Map()\n const ttsMap = new Map()\n const groqCfg = cfg.stt?.providers?.groq\n if (groqCfg?.apiKey) {\n sttMap.set('groq', new GroqSTT(groqCfg.apiKey, groqCfg.model))\n }\n return { stt: sttMap, tts: ttsMap }\n })\n\n ctx.registerService('speech', service)\n\n // Helper to look up the session and set voiceMode\n const setSessionVoiceMode = (pluginCtx: PluginContext, sessionId: string | null, voiceMode: 'off' | 'next' | 'on'): void => {\n if (!sessionId) return\n try {\n const sessionManager = pluginCtx.sessions as { getSession(id: string): Session | undefined }\n const session = sessionManager.getSession(sessionId)\n if (session) {\n session.setVoiceMode(voiceMode)\n }\n } catch {\n // Session lookup may fail if kernel:access is unavailable; silently ignore\n }\n }\n\n ctx.registerCommand({\n name: 'tts',\n description: 'Toggle text-to-speech',\n usage: 'on|off|next|install',\n category: 'plugin',\n handler: async (args) => {\n const mode = args.raw.trim().toLowerCase()\n\n // Check if TTS provider is available\n if ((mode === 'on' || mode === '' || mode === 'next') && !service.isTTSAvailable()) {\n return {\n type: 'menu' as const,\n title: 'TTS provider not installed. Install Edge TTS plugin?',\n options: [\n { label: 'Install Edge TTS', command: '/tts install' },\n { label: 'Cancel', command: '/tts off' },\n ],\n }\n }\n\n if (mode === 'install') {\n try {\n const mod = await installNpmPlugin(EDGE_TTS_PLUGIN)\n const plugin = mod.default\n if (plugin && ctx.core) {\n const lm = (ctx.core as OpenACPCore).lifecycleManager\n const registry = lm.registry\n if (registry) {\n registry.register(plugin.name, {\n version: plugin.version,\n source: 'npm',\n enabled: true,\n settingsPath: '',\n description: plugin.description,\n })\n await registry.save()\n }\n await lm.boot([plugin])\n }\n return { type: 'text' as const, text: 'Edge TTS plugin installed and ready! Use /tts on to enable.' }\n } catch (err) {\n return { type: 'error' as const, message: `Failed to install Edge TTS plugin: ${err}. Try manually: openacp plugin install ${EDGE_TTS_PLUGIN}` }\n }\n }\n\n if (mode === 'on') {\n setSessionVoiceMode(ctx, args.sessionId, 'on')\n return { type: 'text' as const, text: 'Text-to-speech enabled' }\n }\n if (mode === 'off') {\n setSessionVoiceMode(ctx, args.sessionId, 'off')\n return { type: 'text' as const, text: 'Text-to-speech disabled' }\n }\n if (mode === 'next') {\n setSessionVoiceMode(ctx, args.sessionId, 'next')\n return { type: 'text' as const, text: 'Text-to-speech enabled for next message' }\n }\n return { type: 'menu' as const, title: 'Text to Speech', options: [\n { label: 'Enable', command: '/tts on' },\n { label: 'Disable', command: '/tts off' },\n { label: 'Next message only', command: '/tts next' },\n ]}\n },\n })\n\n ctx.log.info('Speech service ready')\n },\n}\n\nexport default speechPlugin\n"],"mappings":";;;;;;;;;AAOA,IAAM,kBAAkB;AAExB,IAAM,eAA8B;AAAA,EAClC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AAAA,EACb,WAAW;AAAA,EACX,4BAA4B,EAAE,yBAAyB,SAAS;AAAA,EAChE,aAAa,CAAC,qBAAqB,qBAAqB,eAAe;AAAA,EAEvE,MAAM,QAAQ,KAAqB;AACjC,UAAM,EAAE,UAAU,UAAU,aAAa,IAAI;AAG7C,QAAI,cAAc;AAChB,YAAM,YAAY,aAAa;AAC/B,UAAI,WAAW;AACb,cAAM,MAAM,UAAU;AACtB,cAAM,MAAM,UAAU;AACtB,cAAM,gBAAgB,KAAK;AAC3B,cAAM,aAAa,eAAe;AAClC,cAAM,SAAS,OAAO;AAAA,UACpB,aAAa,KAAK,YAAY;AAAA,UAC9B,YAAY,YAAY,UAAU;AAAA,UAClC,aAAa,KAAK,YAAY;AAAA,UAC9B,UAAU;AAAA,QACZ,CAAC;AACD,iBAAS,IAAI,QAAQ,6CAA6C;AAClE;AAAA,MACF;AAAA,IACF;AAGA,UAAM,YAAY,MAAM,SAAS,QAAQ;AAAA,MACvC,SAAS;AAAA,MACT,cAAc;AAAA,IAChB,CAAC;AAED,QAAI,cAA6B;AACjC,QAAI,aAAa;AAEjB,QAAI,WAAW;AACb,oBAAc,MAAM,SAAS,OAAO;AAAA,QAClC,SAAS;AAAA,QACT,SAAS,CAAC,EAAE,OAAO,QAAQ,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AAAA,MACnF,CAAC;AAED,UAAI,gBAAgB,QAAQ;AAC1B,qBAAa,MAAM,SAAS,KAAK;AAAA,UAC/B,SAAS;AAAA,UACT,UAAU,CAAC,MAAO,CAAC,EAAE,KAAK,IAAI,4BAA4B;AAAA,QAC5D,CAAC;AACD,qBAAa,WAAW,KAAK;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,SAAS,OAAO;AAAA,MACxC,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,OAAO,YAAY,OAAO,YAAY,MAAM,qBAAqB;AAAA,QACnE,EAAE,OAAO,QAAQ,OAAO,qBAAqB;AAAA,MAC/C;AAAA,IACF,CAAC;AAED,QAAI,WAAW;AACf,QAAI,gBAAgB,YAAY;AAC9B,eAAS,IAAI,KAAK,+BAA+B;AACjD,UAAI;AACF,cAAM,iBAAiB,eAAe;AACtC,iBAAS,IAAI,QAAQ,2BAA2B;AAAA,MAClD,SAAS,KAAK;AACZ,iBAAS,IAAI,QAAQ,sCAAsC,GAAG,2DAA2D,eAAe,EAAE;AAAA,MAC5I;AAEA,iBAAW,MAAM,SAAS,KAAK;AAAA,QAC7B,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AACD,iBAAW,SAAS,KAAK;AAAA,IAC3B;AAEA,UAAM,SAAS,OAAO;AAAA,MACpB;AAAA,MACA;AAAA,MACA,aAAa,gBAAgB,SAAS,OAAO;AAAA,MAC7C;AAAA,IACF,CAAC;AACD,aAAS,IAAI,QAAQ,uBAAuB;AAAA,EAC9C;AAAA,EAEA,MAAM,UAAU,KAAqB;AACnC,UAAM,EAAE,UAAU,SAAS,IAAI;AAC/B,UAAM,UAAU,MAAM,SAAS,OAAO;AAEtC,UAAM,SAAS,MAAM,SAAS,OAAO;AAAA,MACnC,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,OAAO,OAAO,OAAO,0BAA0B;AAAA,QACjD,EAAE,OAAO,OAAO,OAAO,4BAA4B;AAAA,QACnD,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,MACjC;AAAA,IACF,CAAC;AAED,QAAI,WAAW,OAAO;AACpB,YAAM,MAAM,MAAM,SAAS,KAAK;AAAA,QAC9B,SAAS;AAAA,QACT,cAAe,QAAQ,cAAyB;AAAA,MAClD,CAAC;AACD,YAAM,UAAU,IAAI,KAAK;AACzB,YAAM,SAAS,IAAI,eAAe,UAAU,SAAS,IAAI;AACzD,YAAM,SAAS,IAAI,cAAc,OAAO;AACxC,eAAS,IAAI,QAAQ,sBAAsB;AAAA,IAC7C,WAAW,WAAW,OAAO;AAC3B,YAAM,QAAQ,MAAM,SAAS,KAAK;AAAA,QAChC,SAAS;AAAA,QACT,cAAe,QAAQ,YAAuB;AAAA,MAChD,CAAC;AACD,YAAM,SAAS,IAAI,YAAY,MAAM,KAAK,CAAC;AAC3C,eAAS,IAAI,QAAQ,sBAAsB;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,KAAqB,MAA0B;AAC7D,QAAI,KAAK,OAAO;AACd,YAAM,IAAI,SAAS,MAAM;AACzB,UAAI,SAAS,IAAI,QAAQ,yBAAyB;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAK;AACf,UAAM,SAAS,IAAI;AACnB,UAAM,aAAa,OAAO;AAE1B,UAAM,cAAc,aAAa,SAAS;AAC1C,UAAM,eAAoC;AAAA,MACxC,KAAK;AAAA,QACH,UAAU;AAAA,QACV,WAAW,aAAa,EAAE,MAAM,EAAE,QAAQ,WAAW,EAAE,IAAI,CAAC;AAAA,MAC9D;AAAA,MACA,KAAK;AAAA,QACH,UAAW,OAAO,eAA0B;AAAA,QAC5C,WAAW,CAAC;AAAA,MACd;AAAA,IACF;AAEA,UAAM,UAAU,IAAI,cAAc,YAAY;AAE9C,QAAI,YAAY;AACd,cAAQ,oBAAoB,QAAQ,IAAI,QAAQ,UAAU,CAAC;AAAA,IAC7D;AAKA,YAAQ,mBAAmB,CAAC,QAAQ;AAClC,YAAM,SAAS,oBAAI,IAAI;AACvB,YAAM,SAAS,oBAAI,IAAI;AACvB,YAAM,UAAU,IAAI,KAAK,WAAW;AACpC,UAAI,SAAS,QAAQ;AACnB,eAAO,IAAI,QAAQ,IAAI,QAAQ,QAAQ,QAAQ,QAAQ,KAAK,CAAC;AAAA,MAC/D;AACA,aAAO,EAAE,KAAK,QAAQ,KAAK,OAAO;AAAA,IACpC,CAAC;AAED,QAAI,gBAAgB,UAAU,OAAO;AAGrC,UAAM,sBAAsB,CAAC,WAA0B,WAA0B,cAA2C;AAC1H,UAAI,CAAC,UAAW;AAChB,UAAI;AACF,cAAM,iBAAiB,UAAU;AACjC,cAAM,UAAU,eAAe,WAAW,SAAS;AACnD,YAAI,SAAS;AACX,kBAAQ,aAAa,SAAS;AAAA,QAChC;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI,gBAAgB;AAAA,MAClB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO;AAAA,MACP,UAAU;AAAA,MACV,SAAS,OAAO,SAAS;AACvB,cAAM,OAAO,KAAK,IAAI,KAAK,EAAE,YAAY;AAGzC,aAAK,SAAS,QAAQ,SAAS,MAAM,SAAS,WAAW,CAAC,QAAQ,eAAe,GAAG;AAClF,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO;AAAA,YACP,SAAS;AAAA,cACP,EAAE,OAAO,oBAAoB,SAAS,eAAe;AAAA,cACrD,EAAE,OAAO,UAAU,SAAS,WAAW;AAAA,YACzC;AAAA,UACF;AAAA,QACF;AAEA,YAAI,SAAS,WAAW;AACtB,cAAI;AACF,kBAAM,MAAM,MAAM,iBAAiB,eAAe;AAClD,kBAAM,SAAS,IAAI;AACnB,gBAAI,UAAU,IAAI,MAAM;AACtB,oBAAM,KAAM,IAAI,KAAqB;AACrC,oBAAM,WAAW,GAAG;AACpB,kBAAI,UAAU;AACZ,yBAAS,SAAS,OAAO,MAAM;AAAA,kBAC7B,SAAS,OAAO;AAAA,kBAChB,QAAQ;AAAA,kBACR,SAAS;AAAA,kBACT,cAAc;AAAA,kBACd,aAAa,OAAO;AAAA,gBACtB,CAAC;AACD,sBAAM,SAAS,KAAK;AAAA,cACtB;AACA,oBAAM,GAAG,KAAK,CAAC,MAAM,CAAC;AAAA,YACxB;AACA,mBAAO,EAAE,MAAM,QAAiB,MAAM,8DAA8D;AAAA,UACtG,SAAS,KAAK;AACZ,mBAAO,EAAE,MAAM,SAAkB,SAAS,sCAAsC,GAAG,0CAA0C,eAAe,GAAG;AAAA,UACjJ;AAAA,QACF;AAEA,YAAI,SAAS,MAAM;AACjB,8BAAoB,KAAK,KAAK,WAAW,IAAI;AAC7C,iBAAO,EAAE,MAAM,QAAiB,MAAM,yBAAyB;AAAA,QACjE;AACA,YAAI,SAAS,OAAO;AAClB,8BAAoB,KAAK,KAAK,WAAW,KAAK;AAC9C,iBAAO,EAAE,MAAM,QAAiB,MAAM,0BAA0B;AAAA,QAClE;AACA,YAAI,SAAS,QAAQ;AACnB,8BAAoB,KAAK,KAAK,WAAW,MAAM;AAC/C,iBAAO,EAAE,MAAM,QAAiB,MAAM,0CAA0C;AAAA,QAClF;AACA,eAAO,EAAE,MAAM,QAAiB,OAAO,kBAAkB,SAAS;AAAA,UAChE,EAAE,OAAO,UAAU,SAAS,UAAU;AAAA,UACtC,EAAE,OAAO,WAAW,SAAS,WAAW;AAAA,UACxC,EAAE,OAAO,qBAAqB,SAAS,YAAY;AAAA,QACrD,EAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,QAAI,IAAI,KAAK,sBAAsB;AAAA,EACrC;AACF;AAEA,IAAO,iBAAQ;","names":[]}
|