@adversity/coding-tool-x 2.4.2 → 2.5.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/CHANGELOG.md +23 -0
- package/dist/web/assets/{icons-BkBtk3H1.js → icons-BALJo7bE.js} +1 -1
- package/dist/web/assets/{index-Cgq2DyzS.css → index-CvHZsWbE.css} +3 -3
- package/dist/web/assets/index-DZjidyED.js +14 -0
- package/dist/web/assets/{naive-ui-D-gb0WfN.js → naive-ui-sh0u_0bf.js} +1 -1
- package/dist/web/assets/{vendors-Bd5vxA1-.js → vendors-CzcvkTIS.js} +1 -1
- package/dist/web/assets/{vue-vendor-hRp8vsrL.js → vue-vendor-CEeI-Azr.js} +1 -1
- package/dist/web/index.html +6 -6
- package/package.json +2 -1
- package/src/commands/security.js +36 -0
- package/src/index.js +19 -3
- package/src/server/api/config-export.js +122 -32
- package/src/server/api/security.js +53 -0
- package/src/server/api/terminal.js +5 -1
- package/src/server/index.js +1 -0
- package/src/server/services/config-export-service.js +611 -38
- package/src/server/services/pty-manager.js +250 -28
- package/src/server/services/security-config.js +131 -0
- package/src/server/services/terminal-config.js +81 -1
- package/src/server/services/terminal-detector.js +76 -1
- package/src/server/services/ui-config.js +2 -0
- package/src/server/websocket-server.js +14 -3
- package/dist/web/assets/index-DOsR4qc6.js +0 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{S as t}from"./vue-vendor-
|
|
1
|
+
import{S as t}from"./vue-vendor-CEeI-Azr.js";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function e(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function n(t){if(t.__esModule)return t;var e=t.default;if("function"==typeof e){var n=function t(){return this instanceof t?Reflect.construct(e,arguments,this.constructor):e.apply(this,arguments)};n.prototype=e.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(t).forEach(function(e){var r=Object.getOwnPropertyDescriptor(t,e);Object.defineProperty(n,e,r.get?r:{enumerable:!0,get:function(){return t[e]}})}),n}var r={exports:{}};const o=n(t);
|
|
2
2
|
/**!
|
|
3
3
|
* Sortable 1.14.0
|
|
4
4
|
* @author RubaXa <trash@rubaxa.org>
|
|
@@ -41,4 +41,4 @@ let ic;const lc=e=>ic=e,cc=Symbol();function ac(e){return e&&"object"==typeof e&
|
|
|
41
41
|
* vue-router v4.6.4
|
|
42
42
|
* (c) 2025 Eduardo San Martin Morote
|
|
43
43
|
* @license MIT
|
|
44
|
-
*/(e,t);n=Na(o.reverse(),"beforeRouteLeave",e,t);for(const s of o)s.leaveGuards.forEach(o=>{n.push(Ra(o,e,t))});const c=y.bind(null,e,t);return n.push(c),M(n).then(()=>{n=[];for(const o of s.list())n.push(Ra(o,e,t));return n.push(c),M(n)}).then(()=>{n=Na(r,"beforeRouteUpdate",e,t);for(const o of r)o.updateGuards.forEach(o=>{n.push(Ra(o,e,t))});return n.push(c),M(n)}).then(()=>{n=[];for(const o of l)if(o.beforeEnter)if(Pc(o.beforeEnter))for(const r of o.beforeEnter)n.push(Ra(r,e,t));else n.push(Ra(o.beforeEnter,e,t));return n.push(c),M(n)}).then(()=>(e.matched.forEach(e=>e.enterCallbacks={}),n=Na(l,"beforeRouteEnter",e,t,b),n.push(c),M(n))).then(()=>{n=[];for(const o of i.list())n.push(Ra(o,e,t));return n.push(c),M(n)}).catch(e=>ba(e,va.NAVIGATION_CANCELLED)?e:Promise.reject(e))}function S(e,t,n){l.list().forEach(o=>b(()=>o(e,t,n)))}function C(e,t,n,o,s){const i=g(e,t);if(i)return i;const l=t===ia,a=Tc?history.state:{};n&&(o||l?r.replace(e.fullPath,Oc({scroll:l&&a&&a.scroll},s)):r.push(e.fullPath,s)),c.value=e,N(e,t,n,l),R()}let x;function w(){x||(x=r.listen((e,t,n)=>{if(!L.listening)return;const o=d(e),s=v(o,L.currentRoute.value);if(s)return void _(Oc(s,{replace:!0,force:!0}),o).catch(Nc);a=o;const i=c.value;var l,u;Tc&&(l=ha(i.fullPath,n.delta),u=pa(),ga.set(l,u)),E(o,i).catch(e=>ba(e,va.NAVIGATION_ABORTED|va.NAVIGATION_CANCELLED)?e:ba(e,va.NAVIGATION_GUARD_REDIRECT)?(_(Oc(h(e.to),{force:!0}),o).then(e=>{ba(e,va.NAVIGATION_ABORTED|va.NAVIGATION_DUPLICATED)&&!n.delta&&n.type===la.pop&&r.go(-1,!1)}).catch(Nc),Promise.reject()):(n.delta&&r.go(-n.delta,!1),O(e,o,i))).then(e=>{(e=e||C(o,i,!1))&&(n.delta&&!ba(e,va.NAVIGATION_CANCELLED)?r.go(-n.delta,!1):n.type===la.pop&&ba(e,va.NAVIGATION_ABORTED|va.NAVIGATION_DUPLICATED)&&r.go(-1,!1)),S(o,i,e)}).catch(Nc)}))}let A,T=Oa(),k=Oa();function O(e,t,n){R(e);const o=k.list();return o.length&&o.forEach(o=>o(e,t,n)),Promise.reject(e)}function R(e){return A||(A=!e,w(),T.list().forEach(([t,n])=>e?n(e):t()),T.reset()),e}function N(t,n,o,r){const{scrollBehavior:s}=e;if(!Tc||!s)return Promise.resolve();const i=!o&&function(e){const t=ga.get(e);return ga.delete(e),t}(ha(t.fullPath,0))||(r||!o)&&history.state&&history.state.scroll||null;return an().then(()=>s(t,n,i)).then(e=>e&&da(e)).catch(e=>O(e,t,n))}const P=e=>r.go(e);let I;const D=new Set,L={currentRoute:c,listening:!0,addRoute:function(e,n){let o,r;return ma(e)?(o=t.getRecordMatcher(e),r=n):r=e,t.addRoute(r,o)},removeRoute:function(e){const n=t.getRecordMatcher(e);n&&t.removeRoute(n)},clearRoutes:t.clearRoutes,hasRoute:function(e){return!!t.getRecordMatcher(e)},getRoutes:function(){return t.getRoutes().map(e=>e.record)},resolve:d,options:e,push:m,replace:function(e){return m(Oc(h(e),{replace:!0}))},go:P,back:()=>P(-1),forward:()=>P(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:k.add,isReady:function(){return A&&c.value!==ia?Promise.resolve():new Promise((e,t)=>{T.add([e,t])})},install(e){e.component("RouterLink",ou),e.component("RouterView",lu),e.config.globalProperties.$router=L,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>Dt(c)}),Tc&&!I&&c.value===ia&&(I=!0,m(r.location).catch(e=>{}));const t={};for(const o in ia)Object.defineProperty(t,o,{get:()=>c.value[o],enumerable:!0});e.provide(Aa,L),e.provide(Ta,vt(t)),e.provide(ka,c);const n=e.unmount;D.add(e),e.unmount=function(){D.delete(e),D.size<1&&(a=ia,x&&x(),x=null,c.value=ia,I=!1,A=!1),n()}}};function M(e){return e.reduce((e,t)=>e.then(()=>b(t)),Promise.resolve())}return L}function au(){return An(Aa)}function uu(e){return An(Ta)}export{
|
|
44
|
+
*/(e,t);n=Na(o.reverse(),"beforeRouteLeave",e,t);for(const s of o)s.leaveGuards.forEach(o=>{n.push(Ra(o,e,t))});const c=y.bind(null,e,t);return n.push(c),M(n).then(()=>{n=[];for(const o of s.list())n.push(Ra(o,e,t));return n.push(c),M(n)}).then(()=>{n=Na(r,"beforeRouteUpdate",e,t);for(const o of r)o.updateGuards.forEach(o=>{n.push(Ra(o,e,t))});return n.push(c),M(n)}).then(()=>{n=[];for(const o of l)if(o.beforeEnter)if(Pc(o.beforeEnter))for(const r of o.beforeEnter)n.push(Ra(r,e,t));else n.push(Ra(o.beforeEnter,e,t));return n.push(c),M(n)}).then(()=>(e.matched.forEach(e=>e.enterCallbacks={}),n=Na(l,"beforeRouteEnter",e,t,b),n.push(c),M(n))).then(()=>{n=[];for(const o of i.list())n.push(Ra(o,e,t));return n.push(c),M(n)}).catch(e=>ba(e,va.NAVIGATION_CANCELLED)?e:Promise.reject(e))}function S(e,t,n){l.list().forEach(o=>b(()=>o(e,t,n)))}function C(e,t,n,o,s){const i=g(e,t);if(i)return i;const l=t===ia,a=Tc?history.state:{};n&&(o||l?r.replace(e.fullPath,Oc({scroll:l&&a&&a.scroll},s)):r.push(e.fullPath,s)),c.value=e,N(e,t,n,l),R()}let x;function w(){x||(x=r.listen((e,t,n)=>{if(!L.listening)return;const o=d(e),s=v(o,L.currentRoute.value);if(s)return void _(Oc(s,{replace:!0,force:!0}),o).catch(Nc);a=o;const i=c.value;var l,u;Tc&&(l=ha(i.fullPath,n.delta),u=pa(),ga.set(l,u)),E(o,i).catch(e=>ba(e,va.NAVIGATION_ABORTED|va.NAVIGATION_CANCELLED)?e:ba(e,va.NAVIGATION_GUARD_REDIRECT)?(_(Oc(h(e.to),{force:!0}),o).then(e=>{ba(e,va.NAVIGATION_ABORTED|va.NAVIGATION_DUPLICATED)&&!n.delta&&n.type===la.pop&&r.go(-1,!1)}).catch(Nc),Promise.reject()):(n.delta&&r.go(-n.delta,!1),O(e,o,i))).then(e=>{(e=e||C(o,i,!1))&&(n.delta&&!ba(e,va.NAVIGATION_CANCELLED)?r.go(-n.delta,!1):n.type===la.pop&&ba(e,va.NAVIGATION_ABORTED|va.NAVIGATION_DUPLICATED)&&r.go(-1,!1)),S(o,i,e)}).catch(Nc)}))}let A,T=Oa(),k=Oa();function O(e,t,n){R(e);const o=k.list();return o.length&&o.forEach(o=>o(e,t,n)),Promise.reject(e)}function R(e){return A||(A=!e,w(),T.list().forEach(([t,n])=>e?n(e):t()),T.reset()),e}function N(t,n,o,r){const{scrollBehavior:s}=e;if(!Tc||!s)return Promise.resolve();const i=!o&&function(e){const t=ga.get(e);return ga.delete(e),t}(ha(t.fullPath,0))||(r||!o)&&history.state&&history.state.scroll||null;return an().then(()=>s(t,n,i)).then(e=>e&&da(e)).catch(e=>O(e,t,n))}const P=e=>r.go(e);let I;const D=new Set,L={currentRoute:c,listening:!0,addRoute:function(e,n){let o,r;return ma(e)?(o=t.getRecordMatcher(e),r=n):r=e,t.addRoute(r,o)},removeRoute:function(e){const n=t.getRecordMatcher(e);n&&t.removeRoute(n)},clearRoutes:t.clearRoutes,hasRoute:function(e){return!!t.getRecordMatcher(e)},getRoutes:function(){return t.getRoutes().map(e=>e.record)},resolve:d,options:e,push:m,replace:function(e){return m(Oc(h(e),{replace:!0}))},go:P,back:()=>P(-1),forward:()=>P(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:k.add,isReady:function(){return A&&c.value!==ia?Promise.resolve():new Promise((e,t)=>{T.add([e,t])})},install(e){e.component("RouterLink",ou),e.component("RouterView",lu),e.config.globalProperties.$router=L,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>Dt(c)}),Tc&&!I&&c.value===ia&&(I=!0,m(r.location).catch(e=>{}));const t={};for(const o in ia)Object.defineProperty(t,o,{get:()=>c.value[o],enumerable:!0});e.provide(Aa,L),e.provide(Ta,vt(t)),e.provide(ka,c);const n=e.unmount;D.add(e),e.unmount=function(){D.delete(e),D.size<1&&(a=ia,x&&x(),x=null,c.value=ia,I=!1,A=!1),n()}}};function M(e){return e.reduce((e,t)=>e.then(()=>b(t)),Promise.resolve())}return L}function au(){return An(Aa)}function uu(e){return An(Ta)}export{au as $,Ki as A,Ls as B,vs as C,ms as D,At as E,gs as F,$o as G,W as H,ec as I,Dt as J,xt as K,wt as L,jt as M,U as N,As as O,Ps as P,Es as Q,Fs as R,sc as S,Wn as T,wc as U,Ac as V,Is as W,Sn as X,Vs as Y,Ts as Z,X as _,_t as a,Qo as a0,Jo as a1,Wl as a2,uu as a3,ql as a4,cu as a5,La as a6,er as a7,zo as a8,To as a9,pc as aa,Bo as b,ui as c,Fo as d,mt as e,Oo as f,zs as g,Ro as h,An as i,Ms as j,co as k,Cn as l,fi as m,an as n,Vo as o,wn as p,tr as q,Rt as r,$s as s,$t as t,ks as u,Nt as v,Pn as w,Rn as x,Ri as y,Cl as z};
|
package/dist/web/index.html
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>CC-TOOL - ClaudeCode增强工作助手</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/vendors-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/icons-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/naive-ui-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DZjidyED.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-CEeI-Azr.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendors-CzcvkTIS.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/icons-BALJo7bE.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/naive-ui-sh0u_0bf.js">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CvHZsWbE.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adversity/coding-tool-x",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@iarna/toml": "^2.2.5",
|
|
49
49
|
"@lydell/node-pty": "^1.1.0",
|
|
50
|
+
"@xterm/headless": "^6.0.0",
|
|
50
51
|
"adm-zip": "^0.5.16",
|
|
51
52
|
"cc-tool-web": "file:src/web",
|
|
52
53
|
"chalk": "^4.1.2",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const SECURITY_FILE = path.join(os.homedir(), '.claude', 'cc-tool', 'security.json');
|
|
7
|
+
|
|
8
|
+
function showSecurityHelp() {
|
|
9
|
+
console.log(chalk.yellow('\n🔐 安全设置命令:'));
|
|
10
|
+
console.log(' ctx security reset 关闭访问密码(删除安全配置文件)');
|
|
11
|
+
console.log('');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function handleSecurityReset() {
|
|
15
|
+
console.log(chalk.cyan('\n🔐 安全设置 - 关闭访问密码\n'));
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(SECURITY_FILE)) {
|
|
18
|
+
console.log(chalk.yellow('⚠️ 未检测到安全配置文件'));
|
|
19
|
+
console.log(chalk.gray(`路径: ${SECURITY_FILE}\n`));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
fs.unlinkSync(SECURITY_FILE);
|
|
25
|
+
console.log(chalk.green('✅ 访问密码已关闭'));
|
|
26
|
+
console.log(chalk.gray(`已删除: ${SECURITY_FILE}\n`));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error(chalk.red('❌ 关闭密码失败:'), error.message);
|
|
29
|
+
console.log(chalk.gray(`路径: ${SECURITY_FILE}\n`));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
showSecurityHelp,
|
|
35
|
+
handleSecurityReset
|
|
36
|
+
};
|
package/src/index.js
CHANGED
|
@@ -10,8 +10,6 @@ const { showMainMenu } = require('./ui/menu');
|
|
|
10
10
|
const { handleList } = require('./commands/list');
|
|
11
11
|
const { handleSearch } = require('./commands/search');
|
|
12
12
|
const { switchProject } = require('./commands/switch');
|
|
13
|
-
const { handleUI } = require('./commands/ui');
|
|
14
|
-
const { handleProxyStart, handleProxyStop, handleProxyStatus } = require('./commands/proxy');
|
|
15
13
|
const { resetConfig } = require('./reset-config');
|
|
16
14
|
const { handleChannelManagement, handleAddChannel, handleChannelStatus } = require('./commands/channels');
|
|
17
15
|
const { handleToggleProxy } = require('./commands/toggle-proxy');
|
|
@@ -77,6 +75,7 @@ function showHelp() {
|
|
|
77
75
|
console.log(chalk.yellow('🛠️ 其他命令:'));
|
|
78
76
|
console.log(' ctx doctor 系统诊断');
|
|
79
77
|
console.log(' ctx reset 重置配置');
|
|
78
|
+
console.log(' ctx security reset 关闭访问密码');
|
|
80
79
|
console.log(' ctx --version, -v 显示版本');
|
|
81
80
|
console.log(' ctx --help, -h 显示帮助\n');
|
|
82
81
|
|
|
@@ -136,6 +135,19 @@ async function main() {
|
|
|
136
135
|
return;
|
|
137
136
|
}
|
|
138
137
|
|
|
138
|
+
// security 命令 - 安全设置
|
|
139
|
+
if (args[0] === 'security') {
|
|
140
|
+
const { showSecurityHelp, handleSecurityReset } = require('./commands/security');
|
|
141
|
+
const subCommand = args[1] || 'help';
|
|
142
|
+
|
|
143
|
+
if (subCommand === 'reset' || subCommand === 'disable' || subCommand === 'off') {
|
|
144
|
+
await handleSecurityReset();
|
|
145
|
+
} else {
|
|
146
|
+
showSecurityHelp();
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
139
151
|
// start 命令 - 启动服务(后台)
|
|
140
152
|
if (args[0] === 'start') {
|
|
141
153
|
await handleStart();
|
|
@@ -171,6 +183,7 @@ async function main() {
|
|
|
171
183
|
await handleRestart();
|
|
172
184
|
} else {
|
|
173
185
|
// 默认前台运行
|
|
186
|
+
const { handleUI } = require('./commands/ui');
|
|
174
187
|
await handleUI();
|
|
175
188
|
}
|
|
176
189
|
return;
|
|
@@ -252,6 +265,7 @@ async function main() {
|
|
|
252
265
|
|
|
253
266
|
// 代理命令
|
|
254
267
|
if (args[0] === 'proxy') {
|
|
268
|
+
const { handleProxyStart, handleProxyStop, handleProxyStatus } = require('./commands/proxy');
|
|
255
269
|
const subCommand = args[1] || 'start';
|
|
256
270
|
|
|
257
271
|
switch (subCommand) {
|
|
@@ -342,9 +356,11 @@ async function main() {
|
|
|
342
356
|
await handleAddChannel();
|
|
343
357
|
break;
|
|
344
358
|
|
|
345
|
-
case 'ui':
|
|
359
|
+
case 'ui': {
|
|
360
|
+
const { handleUI } = require('./commands/ui');
|
|
346
361
|
await handleUI();
|
|
347
362
|
break;
|
|
363
|
+
}
|
|
348
364
|
|
|
349
365
|
case 'port-config':
|
|
350
366
|
await handlePortConfig();
|
|
@@ -4,23 +4,74 @@
|
|
|
4
4
|
|
|
5
5
|
const express = require('express');
|
|
6
6
|
const configExportService = require('../services/config-export-service');
|
|
7
|
+
const AdmZip = require('adm-zip');
|
|
7
8
|
|
|
8
9
|
const router = express.Router();
|
|
9
10
|
|
|
11
|
+
function parseConfigZip(buffer) {
|
|
12
|
+
const zip = new AdmZip(buffer);
|
|
13
|
+
const entry = zip.getEntry('config.json');
|
|
14
|
+
if (!entry) {
|
|
15
|
+
throw new Error('配置包缺少 config.json');
|
|
16
|
+
}
|
|
17
|
+
const content = entry.getData().toString('utf8');
|
|
18
|
+
return JSON.parse(content);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildPreviewSummary(data) {
|
|
22
|
+
return {
|
|
23
|
+
version: data.version,
|
|
24
|
+
exportedAt: data.exportedAt,
|
|
25
|
+
counts: {
|
|
26
|
+
permissionTemplates: (data.data.permissionTemplates || []).length,
|
|
27
|
+
configTemplates: (data.data.configTemplates || []).length,
|
|
28
|
+
channels: (data.data.channels || []).length
|
|
29
|
+
},
|
|
30
|
+
items: {
|
|
31
|
+
permissionTemplates: (data.data.permissionTemplates || []).map(t => ({
|
|
32
|
+
id: t.id,
|
|
33
|
+
name: t.name,
|
|
34
|
+
description: t.description
|
|
35
|
+
})),
|
|
36
|
+
configTemplates: (data.data.configTemplates || []).map(t => ({
|
|
37
|
+
id: t.id,
|
|
38
|
+
name: t.name,
|
|
39
|
+
description: t.description
|
|
40
|
+
})),
|
|
41
|
+
channels: (data.data.channels || []).map(c => ({
|
|
42
|
+
id: c.id,
|
|
43
|
+
name: c.name,
|
|
44
|
+
type: c.type
|
|
45
|
+
}))
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
10
50
|
/**
|
|
11
51
|
* 导出所有配置
|
|
12
52
|
* GET /api/config-export
|
|
13
53
|
*/
|
|
14
54
|
router.get('/', (req, res) => {
|
|
15
55
|
try {
|
|
16
|
-
const
|
|
56
|
+
const format = (req.query.format || 'json').toLowerCase();
|
|
57
|
+
const result = format === 'zip'
|
|
58
|
+
? configExportService.exportAllConfigsZip()
|
|
59
|
+
: configExportService.exportAllConfigs();
|
|
17
60
|
|
|
18
61
|
if (result.success) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
62
|
+
if (format === 'zip') {
|
|
63
|
+
// 设置响应头,触发文件下载
|
|
64
|
+
const filename = result.filename || `ctx-config-${new Date().toISOString().split('T')[0]}.zip`;
|
|
65
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
66
|
+
res.setHeader('Content-Type', 'application/zip');
|
|
67
|
+
res.send(result.data);
|
|
68
|
+
} else {
|
|
69
|
+
// 设置响应头,触发文件下载
|
|
70
|
+
const filename = `ctx-config-${new Date().toISOString().split('T')[0]}.json`;
|
|
71
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
72
|
+
res.setHeader('Content-Type', 'application/json');
|
|
73
|
+
res.json(result.data);
|
|
74
|
+
}
|
|
24
75
|
} else {
|
|
25
76
|
res.status(500).json({
|
|
26
77
|
success: false,
|
|
@@ -64,6 +115,34 @@ router.post('/import', (req, res) => {
|
|
|
64
115
|
}
|
|
65
116
|
});
|
|
66
117
|
|
|
118
|
+
/**
|
|
119
|
+
* 导入 ZIP 配置
|
|
120
|
+
* POST /api/config-export/import-zip
|
|
121
|
+
*/
|
|
122
|
+
router.post('/import-zip', express.raw({ type: ['application/zip', 'application/octet-stream'], limit: '100mb' }), (req, res) => {
|
|
123
|
+
try {
|
|
124
|
+
const overwrite = req.query.overwrite === 'true';
|
|
125
|
+
const buffer = req.body;
|
|
126
|
+
|
|
127
|
+
if (!Buffer.isBuffer(buffer) || buffer.length === 0) {
|
|
128
|
+
return res.status(400).json({
|
|
129
|
+
success: false,
|
|
130
|
+
message: '缺少 ZIP 文件内容'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const data = parseConfigZip(buffer);
|
|
135
|
+
const result = configExportService.importConfigs(data, { overwrite });
|
|
136
|
+
res.json(result);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error('[ConfigExport API] 导入 ZIP 失败:', err);
|
|
139
|
+
res.status(500).json({
|
|
140
|
+
success: false,
|
|
141
|
+
message: err.message
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
67
146
|
/**
|
|
68
147
|
* 预览导入配置(不实际导入)
|
|
69
148
|
* POST /api/config-export/preview
|
|
@@ -79,32 +158,7 @@ router.post('/preview', (req, res) => {
|
|
|
79
158
|
});
|
|
80
159
|
}
|
|
81
160
|
|
|
82
|
-
const summary =
|
|
83
|
-
version: data.version,
|
|
84
|
-
exportedAt: data.exportedAt,
|
|
85
|
-
counts: {
|
|
86
|
-
permissionTemplates: (data.data.permissionTemplates || []).length,
|
|
87
|
-
configTemplates: (data.data.configTemplates || []).length,
|
|
88
|
-
channels: (data.data.channels || []).length
|
|
89
|
-
},
|
|
90
|
-
items: {
|
|
91
|
-
permissionTemplates: (data.data.permissionTemplates || []).map(t => ({
|
|
92
|
-
id: t.id,
|
|
93
|
-
name: t.name,
|
|
94
|
-
description: t.description
|
|
95
|
-
})),
|
|
96
|
-
configTemplates: (data.data.configTemplates || []).map(t => ({
|
|
97
|
-
id: t.id,
|
|
98
|
-
name: t.name,
|
|
99
|
-
description: t.description
|
|
100
|
-
})),
|
|
101
|
-
channels: (data.data.channels || []).map(c => ({
|
|
102
|
-
id: c.id,
|
|
103
|
-
name: c.name,
|
|
104
|
-
type: c.type
|
|
105
|
-
}))
|
|
106
|
-
}
|
|
107
|
-
};
|
|
161
|
+
const summary = buildPreviewSummary(data);
|
|
108
162
|
|
|
109
163
|
res.json({
|
|
110
164
|
success: true,
|
|
@@ -119,4 +173,40 @@ router.post('/preview', (req, res) => {
|
|
|
119
173
|
}
|
|
120
174
|
});
|
|
121
175
|
|
|
176
|
+
/**
|
|
177
|
+
* 预览 ZIP 导入配置(不实际导入)
|
|
178
|
+
* POST /api/config-export/preview-zip
|
|
179
|
+
*/
|
|
180
|
+
router.post('/preview-zip', express.raw({ type: ['application/zip', 'application/octet-stream'], limit: '100mb' }), (req, res) => {
|
|
181
|
+
try {
|
|
182
|
+
const buffer = req.body;
|
|
183
|
+
if (!Buffer.isBuffer(buffer) || buffer.length === 0) {
|
|
184
|
+
return res.status(400).json({
|
|
185
|
+
success: false,
|
|
186
|
+
message: '缺少 ZIP 文件内容'
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const data = parseConfigZip(buffer);
|
|
191
|
+
if (!data || !data.data) {
|
|
192
|
+
return res.status(400).json({
|
|
193
|
+
success: false,
|
|
194
|
+
message: '无效的导入数据格式'
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const summary = buildPreviewSummary(data);
|
|
199
|
+
res.json({
|
|
200
|
+
success: true,
|
|
201
|
+
data: summary
|
|
202
|
+
});
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.error('[ConfigExport API] 预览 ZIP 失败:', err);
|
|
205
|
+
res.status(500).json({
|
|
206
|
+
success: false,
|
|
207
|
+
message: err.message
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
122
212
|
module.exports = router;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const {
|
|
4
|
+
getSecurityStatus,
|
|
5
|
+
verifySecurityPassword,
|
|
6
|
+
setSecurityPassword
|
|
7
|
+
} = require('../services/security-config');
|
|
8
|
+
|
|
9
|
+
router.get('/', (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const status = getSecurityStatus();
|
|
12
|
+
res.json({ success: true, ...status });
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error('Error getting security status:', error);
|
|
15
|
+
res.status(500).json({ error: error.message });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
router.post('/verify', (req, res) => {
|
|
20
|
+
try {
|
|
21
|
+
const { password } = req.body;
|
|
22
|
+
if (typeof password !== 'string' || !password) {
|
|
23
|
+
return res.status(400).json({ error: '请输入密码' });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result = verifySecurityPassword(password);
|
|
27
|
+
if (result.reason === 'not_set') {
|
|
28
|
+
return res.status(400).json({ error: '尚未设置访问密码' });
|
|
29
|
+
}
|
|
30
|
+
if (!result.ok) {
|
|
31
|
+
return res.status(401).json({ error: '密码错误' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
res.json({ success: true });
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('Error verifying security password:', error);
|
|
37
|
+
res.status(500).json({ error: error.message });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
router.post('/password', (req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
const { currentPassword, newPassword } = req.body || {};
|
|
44
|
+
const result = setSecurityPassword({ currentPassword, newPassword });
|
|
45
|
+
res.json({ success: true, ...result });
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const status = error.code === 'INVALID_PASSWORD' ? 401 : 400;
|
|
48
|
+
console.error('Error setting security password:', error);
|
|
49
|
+
res.status(status).json({ error: error.message });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
module.exports = router;
|
|
@@ -9,6 +9,7 @@ const path = require('path');
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
|
|
11
11
|
const { ptyManager } = require('../services/pty-manager');
|
|
12
|
+
const { getWebTerminalShellConfig } = require('../services/terminal-config');
|
|
12
13
|
const {
|
|
13
14
|
loadTerminalCommands,
|
|
14
15
|
saveTerminalCommands,
|
|
@@ -171,13 +172,16 @@ router.post('/create', (req, res) => {
|
|
|
171
172
|
// 获取启动命令
|
|
172
173
|
const startCommand = getCommandForChannel(channel, sessionId, workDir);
|
|
173
174
|
|
|
175
|
+
const shellConfig = getWebTerminalShellConfig();
|
|
176
|
+
|
|
174
177
|
// 创建终端
|
|
175
178
|
const terminal = ptyManager.create({
|
|
176
179
|
cwd: workDir,
|
|
177
180
|
channel,
|
|
178
181
|
sessionId,
|
|
179
182
|
projectName,
|
|
180
|
-
startCommand
|
|
183
|
+
startCommand,
|
|
184
|
+
...shellConfig
|
|
181
185
|
});
|
|
182
186
|
|
|
183
187
|
res.json({
|
package/src/server/index.js
CHANGED
|
@@ -109,6 +109,7 @@ async function startServer(port) {
|
|
|
109
109
|
app.use('/api/aliases', require('./api/aliases')());
|
|
110
110
|
app.use('/api/favorites', require('./api/favorites'));
|
|
111
111
|
app.use('/api/ui-config', require('./api/ui-config'));
|
|
112
|
+
app.use('/api/security', require('./api/security'));
|
|
112
113
|
app.use('/api/channels', require('./api/channels'));
|
|
113
114
|
app.use('/api/proxy', require('./api/proxy'));
|
|
114
115
|
app.use('/api/codex/proxy', require('./api/codex-proxy'));
|