chainlesschain 0.37.9 → 0.37.11
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 +309 -19
- package/bin/chainlesschain.js +4 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/did.js +376 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/import.js +259 -0
- package/src/commands/init.js +184 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +155 -4
- package/src/commands/lowcode.js +320 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +187 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +451 -0
- package/src/commands/sandbox.js +366 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +93 -1
- package/src/lib/a2a-protocol.js +371 -0
- package/src/lib/agent-coordinator.js +273 -0
- package/src/lib/agent-economy.js +369 -0
- package/src/lib/app-builder.js +377 -0
- package/src/lib/audit-logger.js +364 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -0
- package/src/lib/cowork/ab-comparator-cli.js +180 -0
- package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
- package/src/lib/cowork/debate-review-cli.js +144 -0
- package/src/lib/cowork/decision-kb-cli.js +153 -0
- package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
- package/src/lib/cowork-adapter.js +106 -0
- package/src/lib/crypto-manager.js +246 -0
- package/src/lib/did-manager.js +270 -0
- package/src/lib/ensure-utf8.js +59 -0
- package/src/lib/evolution-system.js +508 -0
- package/src/lib/git-integration.js +220 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/instinct-manager.js +190 -0
- package/src/lib/knowledge-exporter.js +302 -0
- package/src/lib/knowledge-importer.js +293 -0
- package/src/lib/llm-providers.js +325 -0
- package/src/lib/mcp-client.js +413 -0
- package/src/lib/memory-manager.js +211 -0
- package/src/lib/note-versioning.js +244 -0
- package/src/lib/org-manager.js +424 -0
- package/src/lib/p2p-manager.js +317 -0
- package/src/lib/pdf-parser.js +96 -0
- package/src/lib/permission-engine.js +374 -0
- package/src/lib/plan-mode.js +333 -0
- package/src/lib/plugin-manager.js +430 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/session-manager.js +189 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/sync-manager.js +347 -0
- package/src/lib/token-tracker.js +200 -0
- package/src/lib/wallet-manager.js +348 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +259 -124
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instinct learning commands
|
|
3
|
+
* chainlesschain instinct show|reset|categories
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { logger } from "../lib/logger.js";
|
|
8
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
getInstincts,
|
|
11
|
+
getStrongInstincts,
|
|
12
|
+
resetInstincts,
|
|
13
|
+
deleteInstinct,
|
|
14
|
+
decayInstincts,
|
|
15
|
+
generateInstinctPrompt,
|
|
16
|
+
INSTINCT_CATEGORIES,
|
|
17
|
+
} from "../lib/instinct-manager.js";
|
|
18
|
+
|
|
19
|
+
export function registerInstinctCommand(program) {
|
|
20
|
+
const instinct = program
|
|
21
|
+
.command("instinct")
|
|
22
|
+
.description("Instinct learning — learned user preferences");
|
|
23
|
+
|
|
24
|
+
// instinct show
|
|
25
|
+
instinct
|
|
26
|
+
.command("show", { isDefault: true })
|
|
27
|
+
.description("Show learned instincts")
|
|
28
|
+
.option("--category <cat>", "Filter by category")
|
|
29
|
+
.option("-n, --limit <n>", "Max entries", "30")
|
|
30
|
+
.option("--strong", "Show only high-confidence instincts (>= 70%)")
|
|
31
|
+
.option("--json", "Output as JSON")
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
try {
|
|
34
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
35
|
+
if (!ctx.db) {
|
|
36
|
+
logger.error("Database not available");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const db = ctx.db.getDatabase();
|
|
40
|
+
|
|
41
|
+
const instincts = options.strong
|
|
42
|
+
? getStrongInstincts(db)
|
|
43
|
+
: getInstincts(db, {
|
|
44
|
+
category: options.category,
|
|
45
|
+
limit: parseInt(options.limit) || 30,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (options.json) {
|
|
49
|
+
console.log(JSON.stringify(instincts, null, 2));
|
|
50
|
+
} else if (instincts.length === 0) {
|
|
51
|
+
logger.info(
|
|
52
|
+
"No instincts learned yet. Use the agent to build up preferences.",
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
logger.log(chalk.bold(`Learned Instincts (${instincts.length}):\n`));
|
|
56
|
+
for (const inst of instincts) {
|
|
57
|
+
const pct = (inst.confidence * 100).toFixed(0);
|
|
58
|
+
const bar =
|
|
59
|
+
"█".repeat(Math.round(inst.confidence * 10)) +
|
|
60
|
+
"░".repeat(10 - Math.round(inst.confidence * 10));
|
|
61
|
+
logger.log(
|
|
62
|
+
` ${chalk.gray(inst.id.slice(0, 8))} ${chalk.cyan(inst.category.padEnd(18))} ${bar} ${pct}%`,
|
|
63
|
+
);
|
|
64
|
+
logger.log(` ${chalk.white(inst.pattern)}`);
|
|
65
|
+
logger.log(
|
|
66
|
+
` ${chalk.gray(`seen ${inst.occurrences}x | last: ${inst.last_seen || "unknown"}`)}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await shutdown();
|
|
72
|
+
} catch (err) {
|
|
73
|
+
logger.error(`Failed: ${err.message}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// instinct categories
|
|
79
|
+
instinct
|
|
80
|
+
.command("categories")
|
|
81
|
+
.description("List instinct categories")
|
|
82
|
+
.action(async () => {
|
|
83
|
+
logger.log(chalk.bold("Instinct Categories:\n"));
|
|
84
|
+
for (const [key, value] of Object.entries(INSTINCT_CATEGORIES)) {
|
|
85
|
+
logger.log(` ${chalk.cyan(value.padEnd(20))} ${chalk.gray(key)}`);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// instinct prompt
|
|
90
|
+
instinct
|
|
91
|
+
.command("prompt")
|
|
92
|
+
.description("Generate a system prompt from learned instincts")
|
|
93
|
+
.option("--json", "Output as JSON")
|
|
94
|
+
.action(async (options) => {
|
|
95
|
+
try {
|
|
96
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
97
|
+
if (!ctx.db) {
|
|
98
|
+
logger.error("Database not available");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
const db = ctx.db.getDatabase();
|
|
102
|
+
const prompt = generateInstinctPrompt(db);
|
|
103
|
+
|
|
104
|
+
if (options.json) {
|
|
105
|
+
console.log(JSON.stringify({ prompt }, null, 2));
|
|
106
|
+
} else if (!prompt) {
|
|
107
|
+
logger.info("No strong instincts yet. Keep using the agent!");
|
|
108
|
+
} else {
|
|
109
|
+
logger.log(prompt);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await shutdown();
|
|
113
|
+
} catch (err) {
|
|
114
|
+
logger.error(`Failed: ${err.message}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// instinct delete
|
|
120
|
+
instinct
|
|
121
|
+
.command("delete")
|
|
122
|
+
.description("Delete an instinct by ID")
|
|
123
|
+
.argument("<id>", "Instinct ID (or prefix)")
|
|
124
|
+
.action(async (id) => {
|
|
125
|
+
try {
|
|
126
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
127
|
+
if (!ctx.db) {
|
|
128
|
+
logger.error("Database not available");
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const db = ctx.db.getDatabase();
|
|
132
|
+
const ok = deleteInstinct(db, id);
|
|
133
|
+
if (ok) {
|
|
134
|
+
logger.success("Instinct deleted");
|
|
135
|
+
} else {
|
|
136
|
+
logger.error(`Instinct not found: ${id}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await shutdown();
|
|
140
|
+
} catch (err) {
|
|
141
|
+
logger.error(`Failed: ${err.message}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// instinct reset
|
|
147
|
+
instinct
|
|
148
|
+
.command("reset")
|
|
149
|
+
.description("Reset all learned instincts")
|
|
150
|
+
.option("--force", "Skip confirmation")
|
|
151
|
+
.action(async (options) => {
|
|
152
|
+
try {
|
|
153
|
+
if (!options.force) {
|
|
154
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
155
|
+
const ok = await confirm({
|
|
156
|
+
message: "Reset all learned instincts? This cannot be undone.",
|
|
157
|
+
});
|
|
158
|
+
if (!ok) {
|
|
159
|
+
logger.info("Cancelled");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
165
|
+
if (!ctx.db) {
|
|
166
|
+
logger.error("Database not available");
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
const db = ctx.db.getDatabase();
|
|
170
|
+
const count = resetInstincts(db);
|
|
171
|
+
logger.success(`Reset ${count} instincts`);
|
|
172
|
+
|
|
173
|
+
await shutdown();
|
|
174
|
+
} catch (err) {
|
|
175
|
+
logger.error(`Failed: ${err.message}`);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// instinct decay
|
|
181
|
+
instinct
|
|
182
|
+
.command("decay")
|
|
183
|
+
.description("Decay old instincts (reduce confidence of unused patterns)")
|
|
184
|
+
.option("--days <n>", "Days threshold", "30")
|
|
185
|
+
.action(async (options) => {
|
|
186
|
+
try {
|
|
187
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
188
|
+
if (!ctx.db) {
|
|
189
|
+
logger.error("Database not available");
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
const db = ctx.db.getDatabase();
|
|
193
|
+
const decayed = decayInstincts(db, parseInt(options.days) || 30);
|
|
194
|
+
logger.success(`Decayed ${decayed} old instincts`);
|
|
195
|
+
|
|
196
|
+
await shutdown();
|
|
197
|
+
} catch (err) {
|
|
198
|
+
logger.error(`Failed: ${err.message}`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
package/src/commands/llm.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LLM management commands
|
|
3
|
-
* chainlesschain llm
|
|
3
|
+
* chainlesschain llm models|test|providers|add-provider|switch
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import { logger } from "../lib/logger.js";
|
|
9
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
10
|
+
import {
|
|
11
|
+
BUILT_IN_PROVIDERS,
|
|
12
|
+
LLMProviderRegistry,
|
|
13
|
+
} from "../lib/llm-providers.js";
|
|
9
14
|
|
|
10
15
|
export function registerLlmCommand(program) {
|
|
11
16
|
const llm = program.command("llm").description("LLM provider management");
|
|
@@ -44,9 +49,21 @@ export function registerLlmCommand(program) {
|
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
} else {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
// Show known models for non-Ollama providers
|
|
53
|
+
spinner.stop();
|
|
54
|
+
const provider = BUILT_IN_PROVIDERS[options.provider];
|
|
55
|
+
if (provider) {
|
|
56
|
+
if (options.json) {
|
|
57
|
+
console.log(JSON.stringify(provider.models, null, 2));
|
|
58
|
+
} else {
|
|
59
|
+
logger.log(chalk.bold(`${provider.displayName} Models:\n`));
|
|
60
|
+
for (const m of provider.models) {
|
|
61
|
+
logger.log(` ${chalk.cyan(m)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
logger.error(`Unknown provider: ${options.provider}`);
|
|
66
|
+
}
|
|
50
67
|
}
|
|
51
68
|
} catch (err) {
|
|
52
69
|
spinner.fail(`Failed to list models: ${err.message}`);
|
|
@@ -134,4 +151,138 @@ export function registerLlmCommand(program) {
|
|
|
134
151
|
process.exit(1);
|
|
135
152
|
}
|
|
136
153
|
});
|
|
154
|
+
|
|
155
|
+
// llm providers - list all available providers
|
|
156
|
+
llm
|
|
157
|
+
.command("providers")
|
|
158
|
+
.description("List all available LLM providers")
|
|
159
|
+
.option("--json", "Output as JSON")
|
|
160
|
+
.action(async (options) => {
|
|
161
|
+
try {
|
|
162
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
163
|
+
if (!ctx.db) {
|
|
164
|
+
// Fall back to built-in list without DB
|
|
165
|
+
const providers = Object.values(BUILT_IN_PROVIDERS);
|
|
166
|
+
if (options.json) {
|
|
167
|
+
console.log(JSON.stringify(providers, null, 2));
|
|
168
|
+
} else {
|
|
169
|
+
logger.log(chalk.bold(`LLM Providers (${providers.length}):\n`));
|
|
170
|
+
for (const p of providers) {
|
|
171
|
+
const hasKey = p.apiKeyEnv
|
|
172
|
+
? process.env[p.apiKeyEnv]
|
|
173
|
+
? chalk.green("✓")
|
|
174
|
+
: chalk.red("✗")
|
|
175
|
+
: chalk.green("✓");
|
|
176
|
+
logger.log(
|
|
177
|
+
` ${hasKey} ${chalk.cyan(p.name.padEnd(15))} ${p.displayName}`,
|
|
178
|
+
);
|
|
179
|
+
logger.log(` ${chalk.gray("Models:")} ${p.models.join(", ")}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
await shutdown();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const db = ctx.db.getDatabase();
|
|
187
|
+
const registry = new LLMProviderRegistry(db);
|
|
188
|
+
const providers = registry.list();
|
|
189
|
+
const active = registry.getActive();
|
|
190
|
+
|
|
191
|
+
if (options.json) {
|
|
192
|
+
console.log(JSON.stringify({ active, providers }, null, 2));
|
|
193
|
+
} else {
|
|
194
|
+
logger.log(chalk.bold(`LLM Providers (${providers.length}):\n`));
|
|
195
|
+
for (const p of providers) {
|
|
196
|
+
const isActive = p.name === active ? chalk.green(" [active]") : "";
|
|
197
|
+
const keyStatus = p.hasApiKey
|
|
198
|
+
? chalk.green("✓")
|
|
199
|
+
: chalk.red("✗ key missing");
|
|
200
|
+
const custom = p.custom ? chalk.yellow(" [custom]") : "";
|
|
201
|
+
logger.log(
|
|
202
|
+
` ${keyStatus} ${chalk.cyan(p.name.padEnd(15))} ${p.displayName}${isActive}${custom}`,
|
|
203
|
+
);
|
|
204
|
+
logger.log(` ${chalk.gray("Models:")} ${p.models.join(", ")}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await shutdown();
|
|
209
|
+
} catch (err) {
|
|
210
|
+
logger.error(`Failed: ${err.message}`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// llm add-provider - add a custom provider
|
|
216
|
+
llm
|
|
217
|
+
.command("add-provider")
|
|
218
|
+
.description("Add a custom LLM provider")
|
|
219
|
+
.argument("<name>", "Provider name")
|
|
220
|
+
.requiredOption("-u, --base-url <url>", "API base URL")
|
|
221
|
+
.option("-d, --display-name <name>", "Display name")
|
|
222
|
+
.option("-k, --api-key-env <var>", "Environment variable for API key")
|
|
223
|
+
.option("-m, --models <models>", "Comma-separated model names")
|
|
224
|
+
.option("--json", "Output as JSON")
|
|
225
|
+
.action(async (name, options) => {
|
|
226
|
+
try {
|
|
227
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
228
|
+
if (!ctx.db) {
|
|
229
|
+
logger.error("Database not available");
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const db = ctx.db.getDatabase();
|
|
234
|
+
const registry = new LLMProviderRegistry(db);
|
|
235
|
+
const provider = registry.addProvider(name, {
|
|
236
|
+
displayName: options.displayName || name,
|
|
237
|
+
baseUrl: options.baseUrl,
|
|
238
|
+
apiKeyEnv: options.apiKeyEnv,
|
|
239
|
+
models: options.models
|
|
240
|
+
? options.models.split(",").map((m) => m.trim())
|
|
241
|
+
: [],
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (options.json) {
|
|
245
|
+
console.log(JSON.stringify(provider, null, 2));
|
|
246
|
+
} else {
|
|
247
|
+
logger.success(`Added provider ${chalk.cyan(name)}`);
|
|
248
|
+
logger.log(` ${chalk.gray("URL:")} ${provider.baseUrl}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
await shutdown();
|
|
252
|
+
} catch (err) {
|
|
253
|
+
logger.error(`Failed: ${err.message}`);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// llm switch - switch active provider
|
|
259
|
+
llm
|
|
260
|
+
.command("switch")
|
|
261
|
+
.description("Switch the active LLM provider")
|
|
262
|
+
.argument("<name>", "Provider name")
|
|
263
|
+
.option("--json", "Output as JSON")
|
|
264
|
+
.action(async (name, options) => {
|
|
265
|
+
try {
|
|
266
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
267
|
+
if (!ctx.db) {
|
|
268
|
+
logger.error("Database not available");
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const db = ctx.db.getDatabase();
|
|
273
|
+
const registry = new LLMProviderRegistry(db);
|
|
274
|
+
const provider = registry.setActive(name);
|
|
275
|
+
|
|
276
|
+
if (options.json) {
|
|
277
|
+
console.log(JSON.stringify({ active: name, provider }, null, 2));
|
|
278
|
+
} else {
|
|
279
|
+
logger.success(`Switched to ${chalk.cyan(provider.displayName)}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
await shutdown();
|
|
283
|
+
} catch (err) {
|
|
284
|
+
logger.error(`Failed: ${err.message}`);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
137
288
|
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-Code Platform commands
|
|
3
|
+
* chainlesschain lowcode create|list|preview|publish|components|datasource|versions|rollback|export|deploy
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { logger } from "../lib/logger.js";
|
|
9
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
10
|
+
import {
|
|
11
|
+
ensureLowcodeTables,
|
|
12
|
+
createApp,
|
|
13
|
+
saveDesign,
|
|
14
|
+
previewApp,
|
|
15
|
+
publishApp,
|
|
16
|
+
listComponents,
|
|
17
|
+
addDataSource,
|
|
18
|
+
getVersions,
|
|
19
|
+
rollbackApp,
|
|
20
|
+
exportApp,
|
|
21
|
+
listApps,
|
|
22
|
+
} from "../lib/app-builder.js";
|
|
23
|
+
|
|
24
|
+
export function registerLowcodeCommand(program) {
|
|
25
|
+
const lowcode = program
|
|
26
|
+
.command("lowcode")
|
|
27
|
+
.description("Low-code application platform");
|
|
28
|
+
|
|
29
|
+
// lowcode create <name>
|
|
30
|
+
lowcode
|
|
31
|
+
.command("create")
|
|
32
|
+
.description("Create a new low-code application")
|
|
33
|
+
.argument("<name>", "Application name")
|
|
34
|
+
.option("--description <desc>", "Application description", "")
|
|
35
|
+
.option(
|
|
36
|
+
"--platform <platform>",
|
|
37
|
+
"Target platform (web|desktop|mobile|all)",
|
|
38
|
+
"web",
|
|
39
|
+
)
|
|
40
|
+
.option("--json", "Output as JSON")
|
|
41
|
+
.action(async (name, options) => {
|
|
42
|
+
const spinner = ora("Creating application...").start();
|
|
43
|
+
try {
|
|
44
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
45
|
+
if (!ctx.db) {
|
|
46
|
+
spinner.fail("Database not available");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const db = ctx.db.getDatabase();
|
|
50
|
+
ensureLowcodeTables(db);
|
|
51
|
+
|
|
52
|
+
const result = createApp(db, {
|
|
53
|
+
name,
|
|
54
|
+
description: options.description,
|
|
55
|
+
platform: options.platform,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
spinner.stop();
|
|
59
|
+
|
|
60
|
+
if (options.json) {
|
|
61
|
+
console.log(JSON.stringify(result, null, 2));
|
|
62
|
+
} else {
|
|
63
|
+
logger.log(
|
|
64
|
+
chalk.green(`Application created: ${chalk.bold(result.name)}`),
|
|
65
|
+
);
|
|
66
|
+
logger.log(` ID: ${chalk.cyan(result.id)}`);
|
|
67
|
+
logger.log(` Status: ${result.status}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await shutdown();
|
|
71
|
+
} catch (err) {
|
|
72
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// lowcode list
|
|
78
|
+
lowcode
|
|
79
|
+
.command("list")
|
|
80
|
+
.description("List all low-code applications")
|
|
81
|
+
.option("--json", "Output as JSON")
|
|
82
|
+
.action(async (options) => {
|
|
83
|
+
try {
|
|
84
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
85
|
+
if (!ctx.db) {
|
|
86
|
+
logger.error("Database not available");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const db = ctx.db.getDatabase();
|
|
90
|
+
ensureLowcodeTables(db);
|
|
91
|
+
|
|
92
|
+
const apps = listApps(db);
|
|
93
|
+
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(apps, null, 2));
|
|
96
|
+
} else if (apps.length === 0) {
|
|
97
|
+
logger.info(
|
|
98
|
+
'No applications found. Create one with "chainlesschain lowcode create <name>"',
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
101
|
+
logger.log(chalk.bold(`Applications (${apps.length}):\n`));
|
|
102
|
+
for (const app of apps) {
|
|
103
|
+
const status =
|
|
104
|
+
app.status === "published"
|
|
105
|
+
? chalk.green(app.status)
|
|
106
|
+
: chalk.yellow(app.status);
|
|
107
|
+
logger.log(
|
|
108
|
+
` ${chalk.cyan(app.name)} [${status}] v${app.version} (${app.platform})`,
|
|
109
|
+
);
|
|
110
|
+
if (app.description)
|
|
111
|
+
logger.log(` ${chalk.gray(app.description)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
await shutdown();
|
|
116
|
+
} catch (err) {
|
|
117
|
+
logger.error(`Failed: ${err.message}`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// lowcode preview <app-id>
|
|
123
|
+
lowcode
|
|
124
|
+
.command("preview")
|
|
125
|
+
.description("Preview application info")
|
|
126
|
+
.argument("<app-id>", "Application ID")
|
|
127
|
+
.action(async (appId) => {
|
|
128
|
+
try {
|
|
129
|
+
const preview = previewApp(appId);
|
|
130
|
+
|
|
131
|
+
logger.log(chalk.bold("Application Preview:"));
|
|
132
|
+
logger.log(` App ID: ${chalk.cyan(preview.appId)}`);
|
|
133
|
+
logger.log(` Platform: ${preview.platform}`);
|
|
134
|
+
logger.log(` Preview URL: ${chalk.underline(preview.previewUrl)}`);
|
|
135
|
+
logger.log(
|
|
136
|
+
` Components: ${(preview.design.components || []).length}`,
|
|
137
|
+
);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
logger.error(`Failed: ${err.message}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// lowcode publish <app-id>
|
|
145
|
+
lowcode
|
|
146
|
+
.command("publish")
|
|
147
|
+
.description("Publish an application")
|
|
148
|
+
.argument("<app-id>", "Application ID")
|
|
149
|
+
.action(async (appId) => {
|
|
150
|
+
const spinner = ora("Publishing application...").start();
|
|
151
|
+
try {
|
|
152
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
153
|
+
if (!ctx.db) {
|
|
154
|
+
spinner.fail("Database not available");
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
const db = ctx.db.getDatabase();
|
|
158
|
+
ensureLowcodeTables(db);
|
|
159
|
+
|
|
160
|
+
const result = publishApp(db, appId);
|
|
161
|
+
spinner.succeed(`Application ${chalk.cyan(appId)} published`);
|
|
162
|
+
|
|
163
|
+
await shutdown();
|
|
164
|
+
} catch (err) {
|
|
165
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// lowcode components
|
|
171
|
+
lowcode
|
|
172
|
+
.command("components")
|
|
173
|
+
.description("List available built-in components")
|
|
174
|
+
.option("--json", "Output as JSON")
|
|
175
|
+
.action((options) => {
|
|
176
|
+
const components = listComponents();
|
|
177
|
+
|
|
178
|
+
if (options.json) {
|
|
179
|
+
console.log(JSON.stringify(components, null, 2));
|
|
180
|
+
} else {
|
|
181
|
+
logger.log(chalk.bold(`Built-in Components (${components.length}):\n`));
|
|
182
|
+
const categories = [...new Set(components.map((c) => c.category))];
|
|
183
|
+
for (const cat of categories) {
|
|
184
|
+
logger.log(chalk.yellow(` [${cat}]`));
|
|
185
|
+
for (const comp of components.filter((c) => c.category === cat)) {
|
|
186
|
+
logger.log(
|
|
187
|
+
` ${chalk.cyan(comp.name)} — props: ${comp.props.join(", ")}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// lowcode datasource <app-id> <name> <type>
|
|
195
|
+
lowcode
|
|
196
|
+
.command("datasource")
|
|
197
|
+
.description("Add a data source to an application")
|
|
198
|
+
.argument("<app-id>", "Application ID")
|
|
199
|
+
.argument("<name>", "Data source name")
|
|
200
|
+
.argument("<type>", "Data source type (rest|graphql|database|csv)")
|
|
201
|
+
.option("--config <json>", "Configuration as JSON string", "{}")
|
|
202
|
+
.action(async (appId, name, type, options) => {
|
|
203
|
+
const spinner = ora("Adding data source...").start();
|
|
204
|
+
try {
|
|
205
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
206
|
+
if (!ctx.db) {
|
|
207
|
+
spinner.fail("Database not available");
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
const db = ctx.db.getDatabase();
|
|
211
|
+
ensureLowcodeTables(db);
|
|
212
|
+
|
|
213
|
+
let config;
|
|
214
|
+
try {
|
|
215
|
+
config = JSON.parse(options.config);
|
|
216
|
+
} catch (_err) {
|
|
217
|
+
// Intentionally ignore parse error — use empty config
|
|
218
|
+
config = {};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const result = addDataSource(db, appId, name, type, config);
|
|
222
|
+
spinner.succeed(`Data source "${chalk.cyan(name)}" added (${type})`);
|
|
223
|
+
logger.log(` ID: ${chalk.gray(result.id)}`);
|
|
224
|
+
|
|
225
|
+
await shutdown();
|
|
226
|
+
} catch (err) {
|
|
227
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// lowcode versions <app-id>
|
|
233
|
+
lowcode
|
|
234
|
+
.command("versions")
|
|
235
|
+
.description("Show version history for an application")
|
|
236
|
+
.argument("<app-id>", "Application ID")
|
|
237
|
+
.option("--json", "Output as JSON")
|
|
238
|
+
.action((appId, options) => {
|
|
239
|
+
const versions = getVersions(appId);
|
|
240
|
+
|
|
241
|
+
if (options.json) {
|
|
242
|
+
console.log(JSON.stringify(versions, null, 2));
|
|
243
|
+
} else if (versions.length === 0) {
|
|
244
|
+
logger.info("No versions found for this application");
|
|
245
|
+
} else {
|
|
246
|
+
logger.log(chalk.bold(`Version History (${versions.length}):\n`));
|
|
247
|
+
for (const v of versions) {
|
|
248
|
+
logger.log(` v${v.version} — ${v.created_at || "unknown date"}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// lowcode rollback <app-id> <version>
|
|
254
|
+
lowcode
|
|
255
|
+
.command("rollback")
|
|
256
|
+
.description("Rollback application to a previous version")
|
|
257
|
+
.argument("<app-id>", "Application ID")
|
|
258
|
+
.argument("<version>", "Version number to restore")
|
|
259
|
+
.action(async (appId, version) => {
|
|
260
|
+
const spinner = ora(`Rolling back to version ${version}...`).start();
|
|
261
|
+
try {
|
|
262
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
263
|
+
if (!ctx.db) {
|
|
264
|
+
spinner.fail("Database not available");
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
const db = ctx.db.getDatabase();
|
|
268
|
+
ensureLowcodeTables(db);
|
|
269
|
+
|
|
270
|
+
const result = rollbackApp(db, appId, parseInt(version, 10));
|
|
271
|
+
|
|
272
|
+
if (result.restored) {
|
|
273
|
+
spinner.succeed(`Rolled back to version ${version}`);
|
|
274
|
+
} else {
|
|
275
|
+
spinner.fail(`Version ${version} not found`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await shutdown();
|
|
279
|
+
} catch (err) {
|
|
280
|
+
spinner.fail(`Failed: ${err.message}`);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// lowcode export <app-id>
|
|
286
|
+
lowcode
|
|
287
|
+
.command("export")
|
|
288
|
+
.description("Export application definition")
|
|
289
|
+
.argument("<app-id>", "Application ID")
|
|
290
|
+
.option("--json", "Output as JSON")
|
|
291
|
+
.action((appId, options) => {
|
|
292
|
+
const result = exportApp(appId);
|
|
293
|
+
|
|
294
|
+
if (options.json) {
|
|
295
|
+
console.log(JSON.stringify(result, null, 2));
|
|
296
|
+
} else {
|
|
297
|
+
logger.log(chalk.bold("Application Export:"));
|
|
298
|
+
logger.log(` App ID: ${chalk.cyan(result.appId)}`);
|
|
299
|
+
logger.log(
|
|
300
|
+
` App: ${result.app ? result.app.name : "not found"}`,
|
|
301
|
+
);
|
|
302
|
+
logger.log(` Data Sources: ${result.dataSources.length}`);
|
|
303
|
+
logger.log(` Versions: ${result.versions.length}`);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// lowcode deploy <app-id>
|
|
308
|
+
lowcode
|
|
309
|
+
.command("deploy")
|
|
310
|
+
.description("Deploy application (placeholder)")
|
|
311
|
+
.argument("<app-id>", "Application ID")
|
|
312
|
+
.action(async (appId) => {
|
|
313
|
+
logger.log(
|
|
314
|
+
chalk.yellow(
|
|
315
|
+
`Deploy for app ${chalk.cyan(appId)} is a placeholder. ` +
|
|
316
|
+
"Full deployment support coming in a future release.",
|
|
317
|
+
),
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
}
|