chainlesschain 0.44.0 → 0.45.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/README.md +67 -37
- package/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/commands/ui.js +169 -0
- package/src/index.js +6 -0
- package/src/lib/web-ui-server.js +1136 -0
package/README.md
CHANGED
|
@@ -226,7 +226,7 @@ chainlesschain skill sources # Show skill layer paths and counts
|
|
|
226
226
|
|
|
227
227
|
#### CLI Command Skill Packs
|
|
228
228
|
|
|
229
|
-
Automatically wraps
|
|
229
|
+
Automatically wraps 63 CLI commands into 9 Agent-callable domain skill packs:
|
|
230
230
|
|
|
231
231
|
```bash
|
|
232
232
|
chainlesschain skill sync-cli # Generate/update all 9 CLI skill packs
|
|
@@ -247,17 +247,17 @@ chainlesschain skill run cli-enterprise-pack "org list"
|
|
|
247
247
|
chainlesschain skill run cli-integration-pack "mcp servers"
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
-
| Pack
|
|
251
|
-
|
|
252
|
-
| `cli-knowledge-pack`
|
|
253
|
-
| `cli-identity-pack`
|
|
254
|
-
| `cli-infra-pack`
|
|
255
|
-
| `cli-ai-query-pack`
|
|
256
|
-
| `cli-agent-mode-pack`
|
|
257
|
-
| `cli-web3-pack`
|
|
258
|
-
| `cli-security-pack`
|
|
259
|
-
| `cli-enterprise-pack`
|
|
260
|
-
| `cli-integration-pack` | hybrid
|
|
250
|
+
| Pack | Mode | Commands |
|
|
251
|
+
| ---------------------- | --------- | -------------------------------------------------------- |
|
|
252
|
+
| `cli-knowledge-pack` | direct | note, search, memory, session, import, export |
|
|
253
|
+
| `cli-identity-pack` | direct | did, auth, audit |
|
|
254
|
+
| `cli-infra-pack` | direct | setup, start, stop, status, services, config, doctor, db |
|
|
255
|
+
| `cli-ai-query-pack` | llm-query | ask, llm, instinct, tokens |
|
|
256
|
+
| `cli-agent-mode-pack` | agent | agent, chat, cowork |
|
|
257
|
+
| `cli-web3-pack` | direct | wallet, p2p, sync, did |
|
|
258
|
+
| `cli-security-pack` | direct | encrypt, decrypt, audit, pqc |
|
|
259
|
+
| `cli-enterprise-pack` | direct | org, plugin, lowcode, compliance |
|
|
260
|
+
| `cli-integration-pack` | hybrid | mcp, browse, cli-anything, serve, ui |
|
|
261
261
|
|
|
262
262
|
---
|
|
263
263
|
|
|
@@ -569,11 +569,11 @@ chainlesschain skill run comfyui-video '{"prompt":"a cat walking","workflow":"wo
|
|
|
569
569
|
chainlesschain skill run audio-gen "你好,欢迎使用 ChainlessChain"
|
|
570
570
|
```
|
|
571
571
|
|
|
572
|
-
| Skill
|
|
573
|
-
|
|
572
|
+
| Skill | Description |
|
|
573
|
+
| --------------- | --------------------------------------------------------------------- |
|
|
574
574
|
| `comfyui-image` | ComfyUI REST API image generation (txt2img/img2img, custom workflows) |
|
|
575
|
-
| `comfyui-video` | ComfyUI + AnimateDiff video generation (requires workflow JSON)
|
|
576
|
-
| `audio-gen`
|
|
575
|
+
| `comfyui-video` | ComfyUI + AnimateDiff video generation (requires workflow JSON) |
|
|
576
|
+
| `audio-gen` | AI TTS: auto-selects edge-tts → piper-tts → ElevenLabs → OpenAI |
|
|
577
577
|
|
|
578
578
|
Also creates a `workflows/` directory with README for saving ComfyUI workflow JSON files.
|
|
579
579
|
|
|
@@ -590,11 +590,11 @@ chainlesschain skill run doc-edit '{"input_file":"report.md","instruction":"优
|
|
|
590
590
|
chainlesschain skill run doc-edit '{"input_file":"data.xlsx","instruction":"首字母大写"}'
|
|
591
591
|
```
|
|
592
592
|
|
|
593
|
-
| Skill
|
|
594
|
-
|
|
595
|
-
| `doc-generate`
|
|
596
|
-
| `libre-convert` | LibreOffice headless conversion: docx/pdf/html/odt/pptx/xlsx/png
|
|
597
|
-
| `doc-edit`
|
|
593
|
+
| Skill | Description |
|
|
594
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
595
|
+
| `doc-generate` | AI-generated structured documents: md/html/docx/pdf, 4 styles (report/proposal/manual/readme) |
|
|
596
|
+
| `libre-convert` | LibreOffice headless conversion: docx/pdf/html/odt/pptx/xlsx/png |
|
|
597
|
+
| `doc-edit` | AI edit existing docs: md/txt/html (direct LLM), docx (pandoc/soffice), xlsx (openpyxl, formulas preserved), pptx (python-pptx, charts preserved) |
|
|
598
598
|
|
|
599
599
|
Requirements: `winget install pandoc` (for docx), `winget install LibreOffice.LibreOffice` (for PDF/format conversion).
|
|
600
600
|
|
|
@@ -1007,6 +1007,36 @@ chainlesschain serve --project /path/to/project # Default project root f
|
|
|
1007
1007
|
|
|
1008
1008
|
---
|
|
1009
1009
|
|
|
1010
|
+
## Web Management Interface (v0.45.0)
|
|
1011
|
+
|
|
1012
|
+
### `chainlesschain ui`
|
|
1013
|
+
|
|
1014
|
+
Open a browser-based Web management interface — no extra software required.
|
|
1015
|
+
|
|
1016
|
+
```bash
|
|
1017
|
+
chainlesschain ui # Auto-detect mode, open browser
|
|
1018
|
+
chainlesschain ui --port 18810 # Custom HTTP port
|
|
1019
|
+
chainlesschain ui --ws-port 18800 # Custom WebSocket port
|
|
1020
|
+
chainlesschain ui --no-open # Start server without opening browser
|
|
1021
|
+
chainlesschain ui --token <secret> # Enable WebSocket auth token
|
|
1022
|
+
chainlesschain ui --host 0.0.0.0 # Bind to all interfaces (remote access)
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
**Two modes** (auto-detected based on current directory):
|
|
1026
|
+
|
|
1027
|
+
| Mode | Trigger | Description |
|
|
1028
|
+
| ---------------- | -------------------------------------------- | -------------------------------------------------------------- |
|
|
1029
|
+
| **Project mode** | Run from a directory with `.chainlesschain/` | AI automatically loads project context (rules, skills, config) |
|
|
1030
|
+
| **Global mode** | Run from any non-project directory | General-purpose AI management panel |
|
|
1031
|
+
|
|
1032
|
+
**Features**: streaming Markdown output, session management (new/switch/history), Agent/Chat mode toggle, slot-filling interactive dialogs, auto-reconnect (3s), Token auth.
|
|
1033
|
+
|
|
1034
|
+
**Ports**: HTTP 18810 (Web UI page), WebSocket 18800 (reuses `chainlesschain serve` infrastructure).
|
|
1035
|
+
|
|
1036
|
+
**Security**: JSON config embedded with XSS-safe Unicode escaping (`\u003c`/`\u003e`); Token auth via `--token`.
|
|
1037
|
+
|
|
1038
|
+
---
|
|
1039
|
+
|
|
1010
1040
|
## Global Options
|
|
1011
1041
|
|
|
1012
1042
|
```bash
|
|
@@ -1088,22 +1118,22 @@ npm run test:e2e # End-to-end tests
|
|
|
1088
1118
|
|
|
1089
1119
|
### Test Coverage
|
|
1090
1120
|
|
|
1091
|
-
| Category
|
|
1092
|
-
|
|
|
1093
|
-
| Unit — lib modules
|
|
1094
|
-
| Unit — commands
|
|
1095
|
-
| Unit — runtime
|
|
1096
|
-
| Unit — WS sessions
|
|
1097
|
-
| Unit — Skill Packs
|
|
1098
|
-
| Unit — AI Templates
|
|
1099
|
-
| Integration
|
|
1100
|
-
| Integration — WS session
|
|
1101
|
-
| Integration — AI Handlers
|
|
1102
|
-
| E2E
|
|
1103
|
-
| E2E — Skill Packs
|
|
1104
|
-
| E2E — AI Templates
|
|
1105
|
-
| Core packages (external)
|
|
1106
|
-
| **CLI Total**
|
|
1121
|
+
| Category | Files | Tests | Status |
|
|
1122
|
+
| ------------------------- | ------- | -------- | --------------- |
|
|
1123
|
+
| Unit — lib modules | 70 | 1700+ | All passing |
|
|
1124
|
+
| Unit — commands | 17 | 400+ | All passing |
|
|
1125
|
+
| Unit — runtime | 1 | 6 | All passing |
|
|
1126
|
+
| Unit — WS sessions | 9 | 156 | All passing |
|
|
1127
|
+
| Unit — Skill Packs | 2 | 57+ | All passing |
|
|
1128
|
+
| Unit — AI Templates | 2 | 130+ | All passing |
|
|
1129
|
+
| Integration | 13 | 230+ | All passing |
|
|
1130
|
+
| Integration — WS session | 1 | 12 | All passing |
|
|
1131
|
+
| Integration — AI Handlers | 2 | 100+ | All passing |
|
|
1132
|
+
| E2E | 15 | 260+ | All passing |
|
|
1133
|
+
| E2E — Skill Packs | 1 | 23+ | All passing |
|
|
1134
|
+
| E2E — AI Templates | 4 | 65+ | All passing |
|
|
1135
|
+
| Core packages (external) | — | 118 | All passing |
|
|
1136
|
+
| **CLI Total** | **132** | **3056** | **All passing** |
|
|
1107
1137
|
|
|
1108
1138
|
## License
|
|
1109
1139
|
|
package/bin/chainlesschain.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ui command — start a local web management UI
|
|
3
|
+
* chainlesschain ui [--port] [--ws-port] [--host] [--no-open] [--token]
|
|
4
|
+
*
|
|
5
|
+
* Project mode (run from a dir with .chainlesschain/): project-scoped chat UI
|
|
6
|
+
* Global mode (run from any other dir): global management panel
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { logger } from "../lib/logger.js";
|
|
13
|
+
import { ChainlessChainWSServer } from "../lib/ws-server.js";
|
|
14
|
+
import { WSSessionManager } from "../lib/ws-session-manager.js";
|
|
15
|
+
import { createWebUIServer } from "../lib/web-ui-server.js";
|
|
16
|
+
import { bootstrap } from "../runtime/bootstrap.js";
|
|
17
|
+
import { findProjectRoot, loadProjectConfig } from "../lib/project-detector.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Open a URL in the system default browser (cross-platform).
|
|
21
|
+
*/
|
|
22
|
+
function openBrowser(url) {
|
|
23
|
+
try {
|
|
24
|
+
const platform = process.platform;
|
|
25
|
+
if (platform === "win32") {
|
|
26
|
+
execSync(`start "" "${url}"`, { stdio: "ignore" });
|
|
27
|
+
} else if (platform === "darwin") {
|
|
28
|
+
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
29
|
+
} else {
|
|
30
|
+
execSync(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
31
|
+
}
|
|
32
|
+
} catch (_err) {
|
|
33
|
+
// Non-critical — user can open manually
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function registerUiCommand(program) {
|
|
38
|
+
program
|
|
39
|
+
.command("ui")
|
|
40
|
+
.description("Start a local web management UI (project or global mode)")
|
|
41
|
+
.option("-p, --port <port>", "HTTP server port", "18810")
|
|
42
|
+
.option("--ws-port <port>", "WebSocket server port", "18800")
|
|
43
|
+
.option("-H, --host <host>", "Bind host", "127.0.0.1")
|
|
44
|
+
.option("--no-open", "Do not open browser automatically")
|
|
45
|
+
.option(
|
|
46
|
+
"--token <token>",
|
|
47
|
+
"Authentication token for WebSocket (recommended for security)",
|
|
48
|
+
)
|
|
49
|
+
.action(async (opts) => {
|
|
50
|
+
const httpPort = parseInt(opts.port, 10);
|
|
51
|
+
const wsPort = parseInt(opts.wsPort, 10);
|
|
52
|
+
const host = opts.host;
|
|
53
|
+
|
|
54
|
+
if (isNaN(httpPort) || httpPort < 1 || httpPort > 65535) {
|
|
55
|
+
logger.error("Invalid --port. Must be between 1 and 65535.");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
if (isNaN(wsPort) || wsPort < 1 || wsPort > 65535) {
|
|
59
|
+
logger.error("Invalid --ws-port. Must be between 1 and 65535.");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Detect project context ────────────────────────────────────────────
|
|
64
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
65
|
+
const projectConfig = projectRoot ? loadProjectConfig(projectRoot) : null;
|
|
66
|
+
const projectName =
|
|
67
|
+
projectConfig?.name ||
|
|
68
|
+
(projectRoot ? path.basename(projectRoot) : null);
|
|
69
|
+
const mode = projectRoot ? "project" : "global";
|
|
70
|
+
|
|
71
|
+
// ── Bootstrap headless runtime ────────────────────────────────────────
|
|
72
|
+
let db = null;
|
|
73
|
+
try {
|
|
74
|
+
const ctx = await bootstrap({ skipDb: false });
|
|
75
|
+
db = ctx.db?.getDb?.() || null;
|
|
76
|
+
} catch (_err) {
|
|
77
|
+
logger.log(
|
|
78
|
+
chalk.yellow(
|
|
79
|
+
" Warning: Database not available, sessions will be in-memory only",
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Start WebSocket server ────────────────────────────────────────────
|
|
85
|
+
const sessionManager = new WSSessionManager({
|
|
86
|
+
db,
|
|
87
|
+
defaultProjectRoot: projectRoot || process.cwd(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const wsServer = new ChainlessChainWSServer({
|
|
91
|
+
port: wsPort,
|
|
92
|
+
host,
|
|
93
|
+
token: opts.token || null,
|
|
94
|
+
maxConnections: 20,
|
|
95
|
+
timeout: 60000,
|
|
96
|
+
sessionManager,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await wsServer.start();
|
|
101
|
+
} catch (err) {
|
|
102
|
+
logger.error(`Failed to start WebSocket server: ${err.message}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Start HTTP server ─────────────────────────────────────────────────
|
|
107
|
+
const httpServer = createWebUIServer({
|
|
108
|
+
wsPort,
|
|
109
|
+
wsToken: opts.token || null,
|
|
110
|
+
wsHost: host === "0.0.0.0" ? "127.0.0.1" : host,
|
|
111
|
+
projectRoot,
|
|
112
|
+
projectName,
|
|
113
|
+
mode,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await new Promise((resolve, reject) => {
|
|
118
|
+
httpServer.listen(httpPort, host, () => resolve());
|
|
119
|
+
httpServer.on("error", reject);
|
|
120
|
+
});
|
|
121
|
+
} catch (err) {
|
|
122
|
+
logger.error(`Failed to start HTTP server: ${err.message}`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── Print startup info ────────────────────────────────────────────────
|
|
127
|
+
const uiUrl = `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${httpPort}`;
|
|
128
|
+
|
|
129
|
+
logger.log("");
|
|
130
|
+
logger.log(chalk.bold(" ChainlessChain Web UI"));
|
|
131
|
+
logger.log("");
|
|
132
|
+
if (mode === "project") {
|
|
133
|
+
logger.log(
|
|
134
|
+
` Mode: ${chalk.cyan("project")} ${chalk.dim(projectRoot)}`,
|
|
135
|
+
);
|
|
136
|
+
if (projectName) {
|
|
137
|
+
logger.log(` Project: ${chalk.green(projectName)}`);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
logger.log(` Mode: ${chalk.cyan("global")}`);
|
|
141
|
+
}
|
|
142
|
+
logger.log(` UI: ${chalk.cyan(uiUrl)}`);
|
|
143
|
+
logger.log(` WS: ${chalk.dim(`ws://${host}:${wsPort}`)}`);
|
|
144
|
+
logger.log(
|
|
145
|
+
` Auth: ${opts.token ? chalk.green("enabled") : chalk.yellow("disabled")}`,
|
|
146
|
+
);
|
|
147
|
+
logger.log("");
|
|
148
|
+
logger.log(chalk.dim(" Press Ctrl+C to stop"));
|
|
149
|
+
logger.log("");
|
|
150
|
+
|
|
151
|
+
// ── Open browser ──────────────────────────────────────────────────────
|
|
152
|
+
if (opts.open !== false) {
|
|
153
|
+
openBrowser(uiUrl);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Graceful shutdown ─────────────────────────────────────────────────
|
|
157
|
+
const shutdown = async () => {
|
|
158
|
+
logger.log("\n" + chalk.yellow("Shutting down UI server..."));
|
|
159
|
+
await Promise.all([
|
|
160
|
+
new Promise((resolve) => httpServer.close(resolve)),
|
|
161
|
+
wsServer.stop(),
|
|
162
|
+
]);
|
|
163
|
+
process.exit(0);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
process.on("SIGINT", shutdown);
|
|
167
|
+
process.on("SIGTERM", shutdown);
|
|
168
|
+
});
|
|
169
|
+
}
|
package/src/index.js
CHANGED
|
@@ -86,6 +86,9 @@ import { registerCliAnythingCommand } from "./commands/cli-anything.js";
|
|
|
86
86
|
// WebSocket Server Interface
|
|
87
87
|
import { registerServeCommand } from "./commands/serve.js";
|
|
88
88
|
|
|
89
|
+
// Web UI
|
|
90
|
+
import { registerUiCommand } from "./commands/ui.js";
|
|
91
|
+
|
|
89
92
|
export function createProgram() {
|
|
90
93
|
const program = new Command();
|
|
91
94
|
|
|
@@ -201,5 +204,8 @@ export function createProgram() {
|
|
|
201
204
|
// WebSocket Server Interface
|
|
202
205
|
registerServeCommand(program);
|
|
203
206
|
|
|
207
|
+
// Web UI
|
|
208
|
+
registerUiCommand(program);
|
|
209
|
+
|
|
204
210
|
return program;
|
|
205
211
|
}
|