@caoruhua/open-claude-remote 0.1.7 → 0.2.3
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 +514 -11
- package/dist/backend/src/api/config-routes.d.ts +2 -2
- package/dist/backend/src/api/config-routes.d.ts.map +1 -1
- package/dist/backend/src/api/config-routes.js +21 -6
- package/dist/backend/src/api/config-routes.js.map +1 -1
- package/dist/backend/src/api/health-routes.d.ts +4 -1
- package/dist/backend/src/api/health-routes.d.ts.map +1 -1
- package/dist/backend/src/api/health-routes.js +20 -2
- package/dist/backend/src/api/health-routes.js.map +1 -1
- package/dist/backend/src/api/hook-routes.d.ts +2 -2
- package/dist/backend/src/api/hook-routes.d.ts.map +1 -1
- package/dist/backend/src/api/hook-routes.js +10 -3
- package/dist/backend/src/api/hook-routes.js.map +1 -1
- package/dist/backend/src/api/instance-routes.d.ts +5 -3
- package/dist/backend/src/api/instance-routes.d.ts.map +1 -1
- package/dist/backend/src/api/instance-routes.js +48 -45
- package/dist/backend/src/api/instance-routes.js.map +1 -1
- package/dist/backend/src/api/router.d.ts +4 -12
- package/dist/backend/src/api/router.d.ts.map +1 -1
- package/dist/backend/src/api/router.js +28 -20
- package/dist/backend/src/api/router.js.map +1 -1
- package/dist/backend/src/api/status-routes.d.ts +2 -2
- package/dist/backend/src/api/status-routes.d.ts.map +1 -1
- package/dist/backend/src/api/status-routes.js +11 -6
- package/dist/backend/src/api/status-routes.js.map +1 -1
- package/dist/backend/src/attach.d.ts +1 -1
- package/dist/backend/src/attach.d.ts.map +1 -1
- package/dist/backend/src/attach.js +25 -71
- package/dist/backend/src/attach.js.map +1 -1
- package/dist/backend/src/cli-utils.d.ts +24 -1
- package/dist/backend/src/cli-utils.d.ts.map +1 -1
- package/dist/backend/src/cli-utils.js +73 -37
- package/dist/backend/src/cli-utils.js.map +1 -1
- package/dist/backend/src/cli.d.ts +22 -2
- package/dist/backend/src/cli.d.ts.map +1 -1
- package/dist/backend/src/cli.js +328 -23
- package/dist/backend/src/cli.js.map +1 -1
- package/dist/backend/src/config.d.ts +48 -15
- package/dist/backend/src/config.d.ts.map +1 -1
- package/dist/backend/src/config.js +124 -24
- package/dist/backend/src/config.js.map +1 -1
- package/dist/backend/src/daemon/daemon-client.d.ts +69 -0
- package/dist/backend/src/daemon/daemon-client.d.ts.map +1 -0
- package/dist/backend/src/daemon/daemon-client.js +209 -0
- package/dist/backend/src/daemon/daemon-client.js.map +1 -0
- package/dist/backend/src/daemon/daemon-entry.d.ts +8 -0
- package/dist/backend/src/daemon/daemon-entry.d.ts.map +1 -0
- package/dist/backend/src/daemon/daemon-entry.js +37 -0
- package/dist/backend/src/daemon/daemon-entry.js.map +1 -0
- package/dist/backend/src/daemon/daemon-launcher.d.ts +13 -0
- package/dist/backend/src/daemon/daemon-launcher.d.ts.map +1 -0
- package/dist/backend/src/daemon/daemon-launcher.js +62 -0
- package/dist/backend/src/daemon/daemon-launcher.js.map +1 -0
- package/dist/backend/src/index.d.ts.map +1 -1
- package/dist/backend/src/index.js +115 -251
- package/dist/backend/src/index.js.map +1 -1
- package/dist/backend/src/instance/index.d.ts +4 -0
- package/dist/backend/src/instance/index.d.ts.map +1 -0
- package/dist/backend/src/instance/index.js +3 -0
- package/dist/backend/src/instance/index.js.map +1 -0
- package/dist/backend/src/instance/instance-manager.d.ts +62 -0
- package/dist/backend/src/instance/instance-manager.d.ts.map +1 -0
- package/dist/backend/src/instance/instance-manager.js +194 -0
- package/dist/backend/src/instance/instance-manager.js.map +1 -0
- package/dist/backend/src/instance/instance-session.d.ts +87 -0
- package/dist/backend/src/instance/instance-session.d.ts.map +1 -0
- package/dist/backend/src/instance/instance-session.js +359 -0
- package/dist/backend/src/instance/instance-session.js.map +1 -0
- package/dist/backend/src/instance/types.d.ts +39 -0
- package/dist/backend/src/instance/types.d.ts.map +1 -0
- package/dist/backend/src/instance/types.js +2 -0
- package/dist/backend/src/instance/types.js.map +1 -0
- package/dist/backend/src/notification/notification-manager.js +1 -1
- package/dist/backend/src/notification/notification-manager.js.map +1 -1
- package/dist/backend/src/notification/notification-service-factory.js +1 -1
- package/dist/backend/src/notification/notification-service-factory.js.map +1 -1
- package/dist/backend/src/pty/virtual-pty.d.ts.map +1 -1
- package/dist/backend/src/pty/virtual-pty.js +6 -0
- package/dist/backend/src/pty/virtual-pty.js.map +1 -1
- package/dist/backend/src/registry/shared-token.d.ts +2 -2
- package/dist/backend/src/registry/shared-token.js +11 -11
- package/dist/backend/src/registry/shared-token.js.map +1 -1
- package/dist/backend/src/registry/stop-instances.d.ts +0 -25
- package/dist/backend/src/registry/stop-instances.d.ts.map +1 -1
- package/dist/backend/src/registry/stop-instances.js +6 -206
- package/dist/backend/src/registry/stop-instances.js.map +1 -1
- package/dist/backend/src/skills/index.d.ts +4 -0
- package/dist/backend/src/skills/index.d.ts.map +1 -0
- package/dist/backend/src/skills/index.js +4 -0
- package/dist/backend/src/skills/index.js.map +1 -0
- package/dist/backend/src/skills/skill-command-merger.d.ts +32 -0
- package/dist/backend/src/skills/skill-command-merger.d.ts.map +1 -0
- package/dist/backend/src/skills/skill-command-merger.js +78 -0
- package/dist/backend/src/skills/skill-command-merger.js.map +1 -0
- package/dist/backend/src/skills/skill-commands.d.ts +25 -0
- package/dist/backend/src/skills/skill-commands.d.ts.map +1 -0
- package/dist/backend/src/skills/skill-commands.js +56 -0
- package/dist/backend/src/skills/skill-commands.js.map +1 -0
- package/dist/backend/src/skills/skill-scanner.d.ts +36 -0
- package/dist/backend/src/skills/skill-scanner.d.ts.map +1 -0
- package/dist/backend/src/skills/skill-scanner.js +143 -0
- package/dist/backend/src/skills/skill-scanner.js.map +1 -0
- package/dist/backend/src/terminal/terminal-relay.d.ts +1 -0
- package/dist/backend/src/terminal/terminal-relay.d.ts.map +1 -1
- package/dist/backend/src/terminal/terminal-relay.js +6 -0
- package/dist/backend/src/terminal/terminal-relay.js.map +1 -1
- package/dist/backend/src/update.d.ts.map +1 -1
- package/dist/backend/src/update.js +46 -1
- package/dist/backend/src/update.js.map +1 -1
- package/dist/backend/src/utils/banner.d.ts +15 -0
- package/dist/backend/src/utils/banner.d.ts.map +1 -0
- package/dist/backend/src/utils/banner.js +95 -0
- package/dist/backend/src/utils/banner.js.map +1 -0
- package/dist/backend/src/ws/ws-server.d.ts +13 -54
- package/dist/backend/src/ws/ws-server.d.ts.map +1 -1
- package/dist/backend/src/ws/ws-server.js +57 -155
- package/dist/backend/src/ws/ws-server.js.map +1 -1
- package/dist/shared/constants.d.ts +2 -1
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +2 -1
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/defaults.d.ts.map +1 -1
- package/dist/shared/defaults.js +1 -5
- package/dist/shared/defaults.js.map +1 -1
- package/dist/shared/instance.d.ts +4 -9
- package/dist/shared/instance.d.ts.map +1 -1
- package/dist/shared/instance.js +0 -2
- package/dist/shared/instance.js.map +1 -1
- package/frontend-dist/assets/index-CM2xfmS8.js +152 -0
- package/frontend-dist/index.html +1 -1
- package/package.json +2 -2
- package/frontend-dist/assets/index-Cfhr3h3e.js +0 -152
|
@@ -1,27 +1,2 @@
|
|
|
1
|
-
import type { InstanceInfo } from '../../../shared/index.js';
|
|
2
|
-
import { InstanceRegistryManager } from './instance-registry.js';
|
|
3
|
-
export interface StopFailure {
|
|
4
|
-
instanceId: string;
|
|
5
|
-
name: string;
|
|
6
|
-
port: number;
|
|
7
|
-
pid: number;
|
|
8
|
-
reason: string;
|
|
9
|
-
}
|
|
10
|
-
export interface StopSummary {
|
|
11
|
-
total: number;
|
|
12
|
-
stopped: number;
|
|
13
|
-
failed: number;
|
|
14
|
-
failures: StopFailure[];
|
|
15
|
-
}
|
|
16
|
-
export interface StopInstancesOptions {
|
|
17
|
-
gracePeriodMs?: number;
|
|
18
|
-
pollIntervalMs?: number;
|
|
19
|
-
killSignal?: NodeJS.Signals;
|
|
20
|
-
signalSender?: (pid: number, signal: NodeJS.Signals | 0) => void;
|
|
21
|
-
sleep?: (ms: number) => Promise<void>;
|
|
22
|
-
processVerifier?: (instance: InstanceInfo) => Promise<boolean> | boolean;
|
|
23
|
-
}
|
|
24
|
-
export declare function stopInstances(registry: InstanceRegistryManager, options?: StopInstancesOptions): Promise<StopSummary>;
|
|
25
|
-
export declare function stopAllRegisteredInstances(options?: StopInstancesOptions): Promise<StopSummary>;
|
|
26
1
|
export declare function main(): Promise<void>;
|
|
27
2
|
//# sourceMappingURL=stop-instances.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stop-instances.d.ts","sourceRoot":"","sources":["../../../../backend/src/registry/stop-instances.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stop-instances.d.ts","sourceRoot":"","sources":["../../../../backend/src/registry/stop-instances.ts"],"names":[],"mappings":"AAMA,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C"}
|
|
@@ -1,210 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import { InstanceRegistryManager } from './instance-registry.js';
|
|
7
|
-
const DEFAULT_GRACE_PERIOD_MS = 5_000;
|
|
8
|
-
const DEFAULT_POLL_INTERVAL_MS = 200;
|
|
9
|
-
function createProcessVerifier(signalSender) {
|
|
10
|
-
return (instance) => {
|
|
11
|
-
// 先确认 PID 仍存在
|
|
12
|
-
try {
|
|
13
|
-
signalSender(instance.pid, 0);
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
// 再确认该 PID 仍在监听注册端口,降低 PID 复用误杀风险
|
|
19
|
-
try {
|
|
20
|
-
const output = execFileSync('lsof', ['-nP', `-iTCP:${instance.port}`, '-sTCP:LISTEN', '-Fp'], {
|
|
21
|
-
encoding: 'utf-8',
|
|
22
|
-
});
|
|
23
|
-
const pids = output
|
|
24
|
-
.split('\n')
|
|
25
|
-
.filter((line) => line.startsWith('p'))
|
|
26
|
-
.map((line) => Number(line.slice(1)))
|
|
27
|
-
.filter((pid) => Number.isInteger(pid));
|
|
28
|
-
return pids.includes(instance.pid);
|
|
29
|
-
}
|
|
30
|
-
catch (err) {
|
|
31
|
-
logger.warn({ err, pid: instance.pid, port: instance.port }, 'Process verification via lsof failed, fallback to PID-only check');
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function createSleep() {
|
|
37
|
-
return (ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
|
38
|
-
}
|
|
39
|
-
function createSignalSender() {
|
|
40
|
-
return (pid, signal) => {
|
|
41
|
-
process.kill(pid, signal);
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function createIsAlive(signalSender) {
|
|
45
|
-
return (pid) => {
|
|
46
|
-
try {
|
|
47
|
-
signalSender(pid, 0);
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
async function waitForExit(pid, timeoutMs, pollIntervalMs, isAlive, sleep) {
|
|
56
|
-
const deadline = Date.now() + timeoutMs;
|
|
57
|
-
while (Date.now() < deadline) {
|
|
58
|
-
if (!isAlive(pid)) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
await sleep(pollIntervalMs);
|
|
62
|
-
}
|
|
63
|
-
return !isAlive(pid);
|
|
64
|
-
}
|
|
65
|
-
export async function stopInstances(registry, options = {}) {
|
|
66
|
-
const gracePeriodMs = options.gracePeriodMs ?? DEFAULT_GRACE_PERIOD_MS;
|
|
67
|
-
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
68
|
-
const killSignal = options.killSignal ?? 'SIGKILL';
|
|
69
|
-
const signalSender = options.signalSender ?? createSignalSender();
|
|
70
|
-
const sleep = options.sleep ?? createSleep();
|
|
71
|
-
const isAlive = createIsAlive(signalSender);
|
|
72
|
-
const processVerifier = options.processVerifier ?? createProcessVerifier(signalSender);
|
|
73
|
-
const instances = await registry.list();
|
|
74
|
-
const summary = {
|
|
75
|
-
total: instances.length,
|
|
76
|
-
stopped: 0,
|
|
77
|
-
failed: 0,
|
|
78
|
-
failures: [],
|
|
79
|
-
};
|
|
80
|
-
logger.info({ total: instances.length }, 'Starting multi-instance stop');
|
|
81
|
-
if (instances.length === 0) {
|
|
82
|
-
logger.info('No active instances found, stop is idempotent');
|
|
83
|
-
return summary;
|
|
84
|
-
}
|
|
85
|
-
for (const instance of instances) {
|
|
86
|
-
const verified = await processVerifier(instance);
|
|
87
|
-
if (!verified) {
|
|
88
|
-
summary.failed += 1;
|
|
89
|
-
summary.failures.push({
|
|
90
|
-
instanceId: instance.instanceId,
|
|
91
|
-
name: instance.name,
|
|
92
|
-
port: instance.port,
|
|
93
|
-
pid: instance.pid,
|
|
94
|
-
reason: 'Process verification failed: pid/port mismatch',
|
|
95
|
-
});
|
|
96
|
-
logger.error({
|
|
97
|
-
instanceId: instance.instanceId,
|
|
98
|
-
name: instance.name,
|
|
99
|
-
pid: instance.pid,
|
|
100
|
-
port: instance.port,
|
|
101
|
-
}, 'Skip stopping instance due to process verification failure');
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
const result = await stopSingleInstance(instance, {
|
|
105
|
-
gracePeriodMs,
|
|
106
|
-
pollIntervalMs,
|
|
107
|
-
killSignal,
|
|
108
|
-
signalSender,
|
|
109
|
-
sleep,
|
|
110
|
-
isAlive,
|
|
111
|
-
});
|
|
112
|
-
if (result.ok) {
|
|
113
|
-
summary.stopped += 1;
|
|
114
|
-
registry.unregister(instance.instanceId);
|
|
115
|
-
logger.info({
|
|
116
|
-
instanceId: instance.instanceId,
|
|
117
|
-
name: instance.name,
|
|
118
|
-
pid: instance.pid,
|
|
119
|
-
port: instance.port,
|
|
120
|
-
}, 'Instance stopped');
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
summary.failed += 1;
|
|
124
|
-
summary.failures.push({
|
|
125
|
-
instanceId: instance.instanceId,
|
|
126
|
-
name: instance.name,
|
|
127
|
-
port: instance.port,
|
|
128
|
-
pid: instance.pid,
|
|
129
|
-
reason: result.reason,
|
|
130
|
-
});
|
|
131
|
-
logger.error({
|
|
132
|
-
instanceId: instance.instanceId,
|
|
133
|
-
name: instance.name,
|
|
134
|
-
pid: instance.pid,
|
|
135
|
-
port: instance.port,
|
|
136
|
-
reason: result.reason,
|
|
137
|
-
}, 'Failed to stop instance');
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
logger.info({
|
|
141
|
-
total: summary.total,
|
|
142
|
-
stopped: summary.stopped,
|
|
143
|
-
failed: summary.failed,
|
|
144
|
-
failures: summary.failures,
|
|
145
|
-
}, 'Multi-instance stop summary');
|
|
146
|
-
return summary;
|
|
147
|
-
}
|
|
148
|
-
async function stopSingleInstance(instance, ctx) {
|
|
149
|
-
const { gracePeriodMs, pollIntervalMs, killSignal, signalSender, sleep, isAlive } = ctx;
|
|
150
|
-
try {
|
|
151
|
-
signalSender(instance.pid, 'SIGTERM');
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
const code = err.code;
|
|
155
|
-
if (code === 'ESRCH') {
|
|
156
|
-
return { ok: true };
|
|
157
|
-
}
|
|
158
|
-
return { ok: false, reason: `SIGTERM failed: ${String(code ?? err)}` };
|
|
159
|
-
}
|
|
160
|
-
const exitedGracefully = await waitForExit(instance.pid, gracePeriodMs, pollIntervalMs, isAlive, sleep);
|
|
161
|
-
if (exitedGracefully) {
|
|
162
|
-
return { ok: true };
|
|
163
|
-
}
|
|
164
|
-
logger.warn({
|
|
165
|
-
instanceId: instance.instanceId,
|
|
166
|
-
pid: instance.pid,
|
|
167
|
-
killSignal,
|
|
168
|
-
}, 'Graceful stop timed out, sending fallback signal');
|
|
169
|
-
try {
|
|
170
|
-
signalSender(instance.pid, killSignal);
|
|
171
|
-
}
|
|
172
|
-
catch (err) {
|
|
173
|
-
const code = err.code;
|
|
174
|
-
if (code === 'ESRCH') {
|
|
175
|
-
return { ok: true };
|
|
176
|
-
}
|
|
177
|
-
return { ok: false, reason: `${killSignal} failed: ${String(code ?? err)}` };
|
|
178
|
-
}
|
|
179
|
-
const exitedAfterKill = await waitForExit(instance.pid, Math.max(pollIntervalMs * 2, 300), pollIntervalMs, isAlive, sleep);
|
|
180
|
-
if (!exitedAfterKill) {
|
|
181
|
-
return { ok: false, reason: `Process still alive after ${killSignal}` };
|
|
182
|
-
}
|
|
183
|
-
return { ok: true };
|
|
184
|
-
}
|
|
185
|
-
export async function stopAllRegisteredInstances(options = {}) {
|
|
186
|
-
const sharedConfigDir = resolve(homedir(), CLAUDE_REMOTE_DIR);
|
|
187
|
-
const registry = new InstanceRegistryManager(sharedConfigDir);
|
|
188
|
-
return stopInstances(registry, options);
|
|
189
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Stop all instances by sending a shutdown request to the daemon.
|
|
3
|
+
* Used by `pnpm stop` script.
|
|
4
|
+
*/
|
|
5
|
+
import { stopDaemon } from '../daemon/daemon-client.js';
|
|
190
6
|
export async function main() {
|
|
191
|
-
|
|
192
|
-
const summary = await stopAllRegisteredInstances();
|
|
193
|
-
if (summary.failed > 0) {
|
|
194
|
-
process.stderr.write(`Failed to stop ${summary.failed}/${summary.total} instance(s).\n`);
|
|
195
|
-
for (const failure of summary.failures) {
|
|
196
|
-
process.stderr.write(`- ${failure.name} (${failure.instanceId}, pid=${failure.pid}, port=${failure.port}): ${failure.reason}\n`);
|
|
197
|
-
}
|
|
198
|
-
process.exit(1);
|
|
199
|
-
}
|
|
200
|
-
process.stderr.write(`Stopped ${summary.stopped} instance(s).\n`);
|
|
201
|
-
process.exit(0);
|
|
202
|
-
}
|
|
203
|
-
catch (err) {
|
|
204
|
-
logger.error({ err }, 'Unexpected error while stopping instances');
|
|
205
|
-
process.stderr.write(`Stop failed: ${String(err)}\n`);
|
|
206
|
-
process.exit(1);
|
|
207
|
-
}
|
|
7
|
+
await stopDaemon();
|
|
208
8
|
}
|
|
209
9
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
210
10
|
void main();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stop-instances.js","sourceRoot":"","sources":["../../../../backend/src/registry/stop-instances.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"stop-instances.js","sourceRoot":"","sources":["../../../../backend/src/registry/stop-instances.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,UAAU,EAAE,CAAC;AACrB,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACpD,KAAK,IAAI,EAAE,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { scanSkills, scanSkillDirectory, parseSkillFrontmatter, type SkillInfo, } from './skill-scanner.js';
|
|
2
|
+
export { skillToCommand, convertSkillsToCommands, isSkillCommand, } from './skill-commands.js';
|
|
3
|
+
export { mergeSkillCommands, type MergeResult, } from './skill-command-merger.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../backend/src/skills/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,SAAS,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,kBAAkB,EAClB,KAAK,WAAW,GACjB,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { scanSkills, scanSkillDirectory, parseSkillFrontmatter, } from './skill-scanner.js';
|
|
2
|
+
export { skillToCommand, convertSkillsToCommands, isSkillCommand, } from './skill-commands.js';
|
|
3
|
+
export { mergeSkillCommands, } from './skill-command-merger.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../backend/src/skills/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,qBAAqB,GAEtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,kBAAkB,GAEnB,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ConfigurableCommand } from '../../../shared/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* 合并结果统计
|
|
4
|
+
*/
|
|
5
|
+
export interface MergeResult {
|
|
6
|
+
/** 最终合并后的命令列表 */
|
|
7
|
+
commands: ConfigurableCommand[];
|
|
8
|
+
/** 新增的 Skill 数量 */
|
|
9
|
+
added: number;
|
|
10
|
+
/** 移除的 Skill 数量 */
|
|
11
|
+
removed: number;
|
|
12
|
+
/** 保留并合并用户修改的 Skill 数量 */
|
|
13
|
+
preserved: number;
|
|
14
|
+
/** 总 Skill Command 数量 */
|
|
15
|
+
total: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 智能合并 Skill Commands
|
|
19
|
+
*
|
|
20
|
+
* 合并策略:
|
|
21
|
+
* 1. 保留所有非 Skill commands(系统默认 + 用户自定义)
|
|
22
|
+
* 2. 对于 Skill commands:
|
|
23
|
+
* - 新增 Skill → 添加到列表
|
|
24
|
+
* - 删除 Skill → 从列表移除
|
|
25
|
+
* - 保留的 Skill → 保留用户的 enabled、autoSend、desc 修改
|
|
26
|
+
*
|
|
27
|
+
* @param existingCommands 现有命令列表(可能包含用户的修改)
|
|
28
|
+
* @param newSkillCommands 新扫描出的 Skill Commands
|
|
29
|
+
* @returns 合并结果
|
|
30
|
+
*/
|
|
31
|
+
export declare function mergeSkillCommands(existingCommands: ConfigurableCommand[], newSkillCommands: ConfigurableCommand[]): MergeResult;
|
|
32
|
+
//# sourceMappingURL=skill-command-merger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-command-merger.d.ts","sourceRoot":"","sources":["../../../../backend/src/skills/skill-command-merger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAGnD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,iBAAiB;IACjB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,gBAAgB,EAAE,mBAAmB,EAAE,EACvC,gBAAgB,EAAE,mBAAmB,EAAE,GACtC,WAAW,CAoEb"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { isSkillCommand } from './skill-commands.js';
|
|
2
|
+
/**
|
|
3
|
+
* 智能合并 Skill Commands
|
|
4
|
+
*
|
|
5
|
+
* 合并策略:
|
|
6
|
+
* 1. 保留所有非 Skill commands(系统默认 + 用户自定义)
|
|
7
|
+
* 2. 对于 Skill commands:
|
|
8
|
+
* - 新增 Skill → 添加到列表
|
|
9
|
+
* - 删除 Skill → 从列表移除
|
|
10
|
+
* - 保留的 Skill → 保留用户的 enabled、autoSend、desc 修改
|
|
11
|
+
*
|
|
12
|
+
* @param existingCommands 现有命令列表(可能包含用户的修改)
|
|
13
|
+
* @param newSkillCommands 新扫描出的 Skill Commands
|
|
14
|
+
* @returns 合并结果
|
|
15
|
+
*/
|
|
16
|
+
export function mergeSkillCommands(existingCommands, newSkillCommands) {
|
|
17
|
+
// 1. 分离现有命令:非 Skill commands 和 Skill commands
|
|
18
|
+
const nonSkillCommands = [];
|
|
19
|
+
const existingSkillCommands = new Map();
|
|
20
|
+
for (const cmd of existingCommands) {
|
|
21
|
+
if (isSkillCommand(cmd)) {
|
|
22
|
+
existingSkillCommands.set(cmd.label, cmd);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
nonSkillCommands.push(cmd);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// 2. 构建新 Skill 命令映射
|
|
29
|
+
const newSkillMap = new Map();
|
|
30
|
+
for (const cmd of newSkillCommands) {
|
|
31
|
+
newSkillMap.set(cmd.label, cmd);
|
|
32
|
+
}
|
|
33
|
+
// 3. 合并 Skill commands
|
|
34
|
+
const mergedSkillCommands = [];
|
|
35
|
+
let added = 0;
|
|
36
|
+
let removed = 0;
|
|
37
|
+
let preserved = 0;
|
|
38
|
+
// 处理新 Skill
|
|
39
|
+
for (const [label, newCmd] of newSkillMap) {
|
|
40
|
+
const existingCmd = existingSkillCommands.get(label);
|
|
41
|
+
if (existingCmd) {
|
|
42
|
+
// Skill 存在,保留用户的修改
|
|
43
|
+
mergedSkillCommands.push({
|
|
44
|
+
...newCmd,
|
|
45
|
+
// 保留用户的 enabled 设置
|
|
46
|
+
enabled: existingCmd.enabled,
|
|
47
|
+
// 保留用户的 autoSend 设置
|
|
48
|
+
autoSend: existingCmd.autoSend ?? newCmd.autoSend,
|
|
49
|
+
// 保留用户的 desc 修改(如果用户有自定义)
|
|
50
|
+
desc: existingCmd.desc ?? newCmd.desc,
|
|
51
|
+
});
|
|
52
|
+
preserved++;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// 新 Skill,直接添加
|
|
56
|
+
mergedSkillCommands.push(newCmd);
|
|
57
|
+
added++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// 统计移除的 Skill
|
|
61
|
+
for (const [label] of existingSkillCommands) {
|
|
62
|
+
if (!newSkillMap.has(label)) {
|
|
63
|
+
removed++;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// 4. 组合最终结果:非 Skill commands + 合并后的 Skill commands
|
|
67
|
+
// 按 label 排序 Skill commands
|
|
68
|
+
mergedSkillCommands.sort((a, b) => a.label.localeCompare(b.label));
|
|
69
|
+
const finalCommands = [...nonSkillCommands, ...mergedSkillCommands];
|
|
70
|
+
return {
|
|
71
|
+
commands: finalCommands,
|
|
72
|
+
added,
|
|
73
|
+
removed,
|
|
74
|
+
preserved,
|
|
75
|
+
total: mergedSkillCommands.length,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=skill-command-merger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-command-merger.js","sourceRoot":"","sources":["../../../../backend/src/skills/skill-command-merger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAkBrD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAChC,gBAAuC,EACvC,gBAAuC;IAEvC,8CAA8C;IAC9C,MAAM,gBAAgB,GAA0B,EAAE,CAAC;IACnD,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAA+B,CAAC;IAErE,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,MAAM,WAAW,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC3D,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,uBAAuB;IACvB,MAAM,mBAAmB,GAA0B,EAAE,CAAC;IACtD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,YAAY;IACZ,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC1C,MAAM,WAAW,GAAG,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAErD,IAAI,WAAW,EAAE,CAAC;YAChB,mBAAmB;YACnB,mBAAmB,CAAC,IAAI,CAAC;gBACvB,GAAG,MAAM;gBACT,mBAAmB;gBACnB,OAAO,EAAE,WAAW,CAAC,OAAO;gBAC5B,oBAAoB;gBACpB,QAAQ,EAAE,WAAW,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;gBACjD,0BAA0B;gBAC1B,IAAI,EAAE,WAAW,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI;aACtC,CAAC,CAAC;YACH,SAAS,EAAE,CAAC;QACd,CAAC;aAAM,CAAC;YACN,eAAe;YACf,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjC,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC;IAED,cAAc;IACd,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,qBAAqB,EAAE,CAAC;QAC5C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,4BAA4B;IAC5B,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAEnE,MAAM,aAAa,GAAG,CAAC,GAAG,gBAAgB,EAAE,GAAG,mBAAmB,CAAC,CAAC;IAEpE,OAAO;QACL,QAAQ,EAAE,aAAa;QACvB,KAAK;QACL,OAAO;QACP,SAAS;QACT,KAAK,EAAE,mBAAmB,CAAC,MAAM;KAClC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ConfigurableCommand } from '../../../shared/index.js';
|
|
2
|
+
import type { SkillInfo } from './skill-scanner.js';
|
|
3
|
+
/**
|
|
4
|
+
* 将 Skill 信息转换为 ConfigurableCommand
|
|
5
|
+
* @param skill Skill 信息
|
|
6
|
+
* @returns ConfigurableCommand 对象
|
|
7
|
+
*/
|
|
8
|
+
export declare function skillToCommand(skill: SkillInfo): ConfigurableCommand;
|
|
9
|
+
/**
|
|
10
|
+
* 批量将 Skills 转换为 Commands
|
|
11
|
+
* @param skills Skill 信息数组
|
|
12
|
+
* @returns ConfigurableCommand 数组
|
|
13
|
+
*/
|
|
14
|
+
export declare function convertSkillsToCommands(skills: SkillInfo[]): ConfigurableCommand[];
|
|
15
|
+
/**
|
|
16
|
+
* 判断一个 Command 是否为 Skill Command
|
|
17
|
+
* Skill Command 的特征:
|
|
18
|
+
* - label 以 '/' 开头
|
|
19
|
+
* - 不是系统默认命令
|
|
20
|
+
*
|
|
21
|
+
* @param command 要判断的命令
|
|
22
|
+
* @returns 是否为 Skill Command
|
|
23
|
+
*/
|
|
24
|
+
export declare function isSkillCommand(command: ConfigurableCommand): boolean;
|
|
25
|
+
//# sourceMappingURL=skill-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-commands.d.ts","sourceRoot":"","sources":["../../../../backend/src/skills/skill-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAcpD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,mBAAmB,CAUpE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,mBAAmB,EAAE,CAElF;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAYpE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 系统默认命令列表(不可被 Skill 覆盖)
|
|
3
|
+
*/
|
|
4
|
+
const SYSTEM_COMMANDS = new Set([
|
|
5
|
+
'/clear',
|
|
6
|
+
'/compact',
|
|
7
|
+
'/resume',
|
|
8
|
+
'/stats',
|
|
9
|
+
'/exit',
|
|
10
|
+
'/rename',
|
|
11
|
+
]);
|
|
12
|
+
/**
|
|
13
|
+
* 将 Skill 信息转换为 ConfigurableCommand
|
|
14
|
+
* @param skill Skill 信息
|
|
15
|
+
* @returns ConfigurableCommand 对象
|
|
16
|
+
*/
|
|
17
|
+
export function skillToCommand(skill) {
|
|
18
|
+
return {
|
|
19
|
+
label: `/${skill.name}`,
|
|
20
|
+
command: `/${skill.name}`,
|
|
21
|
+
enabled: true,
|
|
22
|
+
// 描述截断到 50 字符
|
|
23
|
+
desc: skill.description?.slice(0, 50),
|
|
24
|
+
// 默认直接发送
|
|
25
|
+
autoSend: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 批量将 Skills 转换为 Commands
|
|
30
|
+
* @param skills Skill 信息数组
|
|
31
|
+
* @returns ConfigurableCommand 数组
|
|
32
|
+
*/
|
|
33
|
+
export function convertSkillsToCommands(skills) {
|
|
34
|
+
return skills.map(skillToCommand);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 判断一个 Command 是否为 Skill Command
|
|
38
|
+
* Skill Command 的特征:
|
|
39
|
+
* - label 以 '/' 开头
|
|
40
|
+
* - 不是系统默认命令
|
|
41
|
+
*
|
|
42
|
+
* @param command 要判断的命令
|
|
43
|
+
* @returns 是否为 Skill Command
|
|
44
|
+
*/
|
|
45
|
+
export function isSkillCommand(command) {
|
|
46
|
+
// label 不以 '/' 开头,不是 Skill Command
|
|
47
|
+
if (!command.label.startsWith('/')) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
// 系统默认命令不是 Skill Command
|
|
51
|
+
if (SYSTEM_COMMANDS.has(command.label)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=skill-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-commands.js","sourceRoot":"","sources":["../../../../backend/src/skills/skill-commands.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,QAAQ;IACR,UAAU;IACV,SAAS;IACT,QAAQ;IACR,OAAO;IACP,SAAS;CACV,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,OAAO;QACL,KAAK,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE;QACvB,OAAO,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE;QACzB,OAAO,EAAE,IAAI;QACb,cAAc;QACd,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACrC,SAAS;QACT,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAmB;IACzD,OAAO,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,OAA4B;IACzD,mCAAmC;IACnC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,yBAAyB;IACzB,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill 信息结构
|
|
3
|
+
*/
|
|
4
|
+
export interface SkillInfo {
|
|
5
|
+
/** Skill 名称(从 YAML frontmatter 提取) */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Skill 描述(可选) */
|
|
8
|
+
description?: string;
|
|
9
|
+
/** SKILL.md 文件完整路径 */
|
|
10
|
+
path: string;
|
|
11
|
+
/** Skill 来源:全局或项目级 */
|
|
12
|
+
source: 'global' | 'project';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 解析 Skill 文件的 YAML frontmatter
|
|
16
|
+
* @param content SKILL.md 文件内容
|
|
17
|
+
* @returns 解析出的 name 和 description,解析失败返回空对象
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseSkillFrontmatter(content: string): {
|
|
20
|
+
name?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 扫描单个 Skill 目录
|
|
25
|
+
* @param dir Skill 目录路径
|
|
26
|
+
* @param source Skill 来源
|
|
27
|
+
* @returns Skill 信息数组
|
|
28
|
+
*/
|
|
29
|
+
export declare function scanSkillDirectory(dir: string, source: 'global' | 'project'): SkillInfo[];
|
|
30
|
+
/**
|
|
31
|
+
* 扫描全局和项目级 Skill 目录
|
|
32
|
+
* @param projectDir 项目工作目录
|
|
33
|
+
* @returns 合并后的 Skill 信息数组(项目级优先覆盖全局同名 Skill)
|
|
34
|
+
*/
|
|
35
|
+
export declare function scanSkills(projectDir: string): SkillInfo[];
|
|
36
|
+
//# sourceMappingURL=skill-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-scanner.d.ts","sourceRoot":"","sources":["../../../../backend/src/skills/skill-scanner.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAuD9F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,EAAE,CAgDzF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,CAmC1D"}
|