chainlesschain 0.37.8 → 0.37.10

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.
Files changed (59) hide show
  1. package/README.md +403 -8
  2. package/bin/chainlesschain.js +4 -0
  3. package/package.json +7 -2
  4. package/src/commands/agent.js +30 -0
  5. package/src/commands/ask.js +114 -0
  6. package/src/commands/audit.js +286 -0
  7. package/src/commands/auth.js +387 -0
  8. package/src/commands/browse.js +184 -0
  9. package/src/commands/chat.js +35 -0
  10. package/src/commands/db.js +152 -0
  11. package/src/commands/did.js +376 -0
  12. package/src/commands/encrypt.js +233 -0
  13. package/src/commands/export.js +125 -0
  14. package/src/commands/git.js +215 -0
  15. package/src/commands/import.js +259 -0
  16. package/src/commands/instinct.js +202 -0
  17. package/src/commands/llm.js +288 -0
  18. package/src/commands/mcp.js +302 -0
  19. package/src/commands/memory.js +282 -0
  20. package/src/commands/note.js +489 -0
  21. package/src/commands/org.js +505 -0
  22. package/src/commands/p2p.js +274 -0
  23. package/src/commands/plugin.js +398 -0
  24. package/src/commands/search.js +237 -0
  25. package/src/commands/session.js +238 -0
  26. package/src/commands/skill.js +479 -0
  27. package/src/commands/sync.js +249 -0
  28. package/src/commands/tokens.js +214 -0
  29. package/src/commands/wallet.js +416 -0
  30. package/src/index.js +65 -0
  31. package/src/lib/audit-logger.js +364 -0
  32. package/src/lib/bm25-search.js +322 -0
  33. package/src/lib/browser-automation.js +216 -0
  34. package/src/lib/crypto-manager.js +246 -0
  35. package/src/lib/did-manager.js +270 -0
  36. package/src/lib/ensure-utf8.js +59 -0
  37. package/src/lib/git-integration.js +220 -0
  38. package/src/lib/instinct-manager.js +190 -0
  39. package/src/lib/knowledge-exporter.js +302 -0
  40. package/src/lib/knowledge-importer.js +293 -0
  41. package/src/lib/llm-providers.js +325 -0
  42. package/src/lib/mcp-client.js +413 -0
  43. package/src/lib/memory-manager.js +211 -0
  44. package/src/lib/note-versioning.js +244 -0
  45. package/src/lib/org-manager.js +424 -0
  46. package/src/lib/p2p-manager.js +317 -0
  47. package/src/lib/pdf-parser.js +96 -0
  48. package/src/lib/permission-engine.js +374 -0
  49. package/src/lib/plan-mode.js +333 -0
  50. package/src/lib/platform.js +15 -0
  51. package/src/lib/plugin-manager.js +312 -0
  52. package/src/lib/response-cache.js +156 -0
  53. package/src/lib/session-manager.js +189 -0
  54. package/src/lib/sync-manager.js +347 -0
  55. package/src/lib/token-tracker.js +200 -0
  56. package/src/lib/wallet-manager.js +348 -0
  57. package/src/repl/agent-repl.js +912 -0
  58. package/src/repl/chat-repl.js +262 -0
  59. package/src/runtime/bootstrap.js +159 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Interactive AI chat REPL with streaming output
3
+ *
4
+ * Features:
5
+ * - Streaming token output
6
+ * - Slash commands: /exit, /model, /provider, /clear, /history
7
+ * - Conversation history
8
+ * - Session auto-save
9
+ */
10
+
11
+ import readline from "readline";
12
+ import chalk from "chalk";
13
+ import { logger } from "../lib/logger.js";
14
+
15
+ const SLASH_COMMANDS = {
16
+ "/exit": "Exit the chat",
17
+ "/quit": "Exit the chat",
18
+ "/model": "Show or change model (/model [name])",
19
+ "/provider": "Show or change provider (/provider [name])",
20
+ "/clear": "Clear conversation history",
21
+ "/history": "Show conversation history",
22
+ "/help": "Show available commands",
23
+ };
24
+
25
+ /**
26
+ * Stream a response from Ollama
27
+ */
28
+ async function streamOllama(messages, model, baseUrl, onToken) {
29
+ const response = await fetch(`${baseUrl}/api/chat`, {
30
+ method: "POST",
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify({
33
+ model,
34
+ messages,
35
+ stream: true,
36
+ }),
37
+ });
38
+
39
+ if (!response.ok) {
40
+ throw new Error(`Ollama error: ${response.status} ${response.statusText}`);
41
+ }
42
+
43
+ const reader = response.body.getReader();
44
+ const decoder = new TextDecoder();
45
+ let fullResponse = "";
46
+
47
+ while (true) {
48
+ const { done, value } = await reader.read();
49
+ if (done) break;
50
+
51
+ const text = decoder.decode(value, { stream: true });
52
+ const lines = text.split("\n").filter(Boolean);
53
+
54
+ for (const line of lines) {
55
+ try {
56
+ const json = JSON.parse(line);
57
+ if (json.message?.content) {
58
+ fullResponse += json.message.content;
59
+ onToken(json.message.content);
60
+ }
61
+ } catch {
62
+ // Partial JSON, skip
63
+ }
64
+ }
65
+ }
66
+
67
+ return fullResponse;
68
+ }
69
+
70
+ /**
71
+ * Stream a response from OpenAI-compatible API
72
+ */
73
+ async function streamOpenAI(messages, model, baseUrl, apiKey, onToken) {
74
+ const response = await fetch(`${baseUrl}/chat/completions`, {
75
+ method: "POST",
76
+ headers: {
77
+ "Content-Type": "application/json",
78
+ Authorization: `Bearer ${apiKey}`,
79
+ },
80
+ body: JSON.stringify({
81
+ model,
82
+ messages,
83
+ stream: true,
84
+ }),
85
+ });
86
+
87
+ if (!response.ok) {
88
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
89
+ }
90
+
91
+ const reader = response.body.getReader();
92
+ const decoder = new TextDecoder();
93
+ let fullResponse = "";
94
+
95
+ while (true) {
96
+ const { done, value } = await reader.read();
97
+ if (done) break;
98
+
99
+ const text = decoder.decode(value, { stream: true });
100
+ const lines = text.split("\n").filter(Boolean);
101
+
102
+ for (const line of lines) {
103
+ if (line.startsWith("data: ")) {
104
+ const data = line.slice(6);
105
+ if (data === "[DONE]") continue;
106
+ try {
107
+ const json = JSON.parse(data);
108
+ const content = json.choices?.[0]?.delta?.content;
109
+ if (content) {
110
+ fullResponse += content;
111
+ onToken(content);
112
+ }
113
+ } catch {
114
+ // Partial data
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ return fullResponse;
121
+ }
122
+
123
+ /**
124
+ * Start the interactive chat REPL
125
+ * @param {object} options
126
+ */
127
+ export async function startChatRepl(options = {}) {
128
+ let model = options.model || "qwen2:7b";
129
+ let provider = options.provider || "ollama";
130
+ const baseUrl = options.baseUrl || "http://localhost:11434";
131
+ const apiKey = options.apiKey || process.env.OPENAI_API_KEY;
132
+
133
+ const messages = [];
134
+
135
+ const rl = readline.createInterface({
136
+ input: process.stdin,
137
+ output: process.stdout,
138
+ prompt: chalk.green("you> "),
139
+ terminal: true,
140
+ });
141
+
142
+ logger.log(chalk.bold("\nChainlessChain AI Chat"));
143
+ logger.log(chalk.gray(`Model: ${model} Provider: ${provider}`));
144
+ logger.log(chalk.gray("Type /help for commands, /exit to quit\n"));
145
+
146
+ rl.prompt();
147
+
148
+ rl.on("line", async (input) => {
149
+ const trimmed = input.trim();
150
+
151
+ if (!trimmed) {
152
+ rl.prompt();
153
+ return;
154
+ }
155
+
156
+ // Handle slash commands
157
+ if (trimmed.startsWith("/")) {
158
+ const [cmd, ...args] = trimmed.split(" ");
159
+ const arg = args.join(" ").trim();
160
+
161
+ switch (cmd) {
162
+ case "/exit":
163
+ case "/quit":
164
+ logger.log(chalk.gray("\nGoodbye!"));
165
+ rl.close();
166
+ return;
167
+
168
+ case "/model":
169
+ if (arg) {
170
+ model = arg;
171
+ logger.info(`Model changed to: ${chalk.cyan(model)}`);
172
+ } else {
173
+ logger.info(`Current model: ${chalk.cyan(model)}`);
174
+ }
175
+ rl.prompt();
176
+ return;
177
+
178
+ case "/provider":
179
+ if (arg) {
180
+ provider = arg;
181
+ logger.info(`Provider changed to: ${chalk.cyan(provider)}`);
182
+ } else {
183
+ logger.info(`Current provider: ${chalk.cyan(provider)}`);
184
+ }
185
+ rl.prompt();
186
+ return;
187
+
188
+ case "/clear":
189
+ messages.length = 0;
190
+ logger.info("Conversation history cleared");
191
+ rl.prompt();
192
+ return;
193
+
194
+ case "/history":
195
+ if (messages.length === 0) {
196
+ logger.info("No conversation history");
197
+ } else {
198
+ for (const msg of messages) {
199
+ const prefix =
200
+ msg.role === "user"
201
+ ? chalk.green("you> ")
202
+ : chalk.blue("ai> ");
203
+ logger.log(
204
+ `${prefix}${msg.content.substring(0, 100)}${msg.content.length > 100 ? "..." : ""}`,
205
+ );
206
+ }
207
+ }
208
+ rl.prompt();
209
+ return;
210
+
211
+ case "/help":
212
+ logger.log(chalk.bold("\nAvailable commands:"));
213
+ for (const [cmd, desc] of Object.entries(SLASH_COMMANDS)) {
214
+ logger.log(` ${chalk.cyan(cmd.padEnd(12))} ${desc}`);
215
+ }
216
+ logger.log("");
217
+ rl.prompt();
218
+ return;
219
+
220
+ default:
221
+ logger.warn(`Unknown command: ${cmd}. Type /help for help.`);
222
+ rl.prompt();
223
+ return;
224
+ }
225
+ }
226
+
227
+ // Add user message
228
+ messages.push({ role: "user", content: trimmed });
229
+
230
+ // Stream the response
231
+ process.stdout.write(chalk.blue("ai> "));
232
+
233
+ try {
234
+ let response;
235
+ const onToken = (token) => process.stdout.write(token);
236
+
237
+ if (provider === "ollama") {
238
+ response = await streamOllama(messages, model, baseUrl, onToken);
239
+ } else if (provider === "openai") {
240
+ const url =
241
+ baseUrl !== "http://localhost:11434"
242
+ ? baseUrl
243
+ : "https://api.openai.com/v1";
244
+ response = await streamOpenAI(messages, model, url, apiKey, onToken);
245
+ } else {
246
+ throw new Error(`Unsupported provider: ${provider}`);
247
+ }
248
+
249
+ process.stdout.write("\n\n");
250
+ messages.push({ role: "assistant", content: response });
251
+ } catch (err) {
252
+ process.stdout.write("\n");
253
+ logger.error(`Error: ${err.message}`);
254
+ }
255
+
256
+ rl.prompt();
257
+ });
258
+
259
+ rl.on("close", () => {
260
+ process.exit(0);
261
+ });
262
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Headless bootstrap - initializes core packages without GUI modules.
3
+ *
4
+ * Stages:
5
+ * 1. Environment detection
6
+ * 2. Path resolution
7
+ * 3. Logger setup
8
+ * 4. Configuration loading
9
+ * 5. Database initialization
10
+ * 6. Service container setup
11
+ * 7. Event bus activation
12
+ * (Stages 8-10 reserved for LLM, AI, and social when extracted)
13
+ */
14
+
15
+ import { logger } from "../lib/logger.js";
16
+
17
+ let _context = null;
18
+
19
+ /**
20
+ * Initialize the headless runtime
21
+ * @param {object} [options]
22
+ * @param {boolean} [options.skipDb] - Skip database initialization
23
+ * @param {string} [options.dbPath] - Custom database path
24
+ * @param {boolean} [options.verbose] - Verbose output
25
+ * @returns {Promise<object>} Runtime context
26
+ */
27
+ export async function bootstrap(options = {}) {
28
+ if (_context) return _context;
29
+
30
+ const ctx = {
31
+ env: null,
32
+ config: null,
33
+ db: null,
34
+ container: null,
35
+ eventBus: null,
36
+ initialized: false,
37
+ };
38
+
39
+ try {
40
+ // Stage 1: Environment
41
+ if (options.verbose) logger.verbose("Stage 1: Detecting environment...");
42
+ const coreEnv = await import("@chainlesschain/core-env");
43
+ ctx.env = {
44
+ runtime: coreEnv.getRuntimeInfo(),
45
+ userDataPath: coreEnv.getUserDataPath(),
46
+ configDir: coreEnv.getConfigDir(),
47
+ dataDir: coreEnv.getDataDir(),
48
+ logsDir: coreEnv.getLogsDir(),
49
+ };
50
+
51
+ // Ensure directories exist
52
+ coreEnv.ensureDir(ctx.env.userDataPath);
53
+ coreEnv.ensureDir(ctx.env.configDir);
54
+ coreEnv.ensureDir(ctx.env.dataDir);
55
+ coreEnv.ensureDir(ctx.env.logsDir);
56
+
57
+ // Stage 2-3: Logger (shared-logger optional, CLI logger always works)
58
+ if (options.verbose) logger.verbose("Stage 2-3: Logger ready");
59
+
60
+ // Stage 4: Configuration
61
+ if (options.verbose) logger.verbose("Stage 4: Loading configuration...");
62
+ try {
63
+ const coreConfig = await import("@chainlesschain/core-config");
64
+ coreConfig.setPathResolvers({
65
+ getUserDataPath: () => ctx.env.userDataPath,
66
+ getDataDir: () => ctx.env.dataDir,
67
+ });
68
+ ctx.config = coreConfig.getAppConfig({
69
+ configPath: `${ctx.env.userDataPath}/app-config.json`,
70
+ });
71
+ } catch (err) {
72
+ if (options.verbose)
73
+ logger.verbose(`Config package not available: ${err.message}`);
74
+ ctx.config = null;
75
+ }
76
+
77
+ // Stage 5: Database
78
+ if (!options.skipDb) {
79
+ if (options.verbose) logger.verbose("Stage 5: Initializing database...");
80
+ try {
81
+ const coreDb = await import("@chainlesschain/core-db");
82
+ const dbPath =
83
+ options.dbPath ||
84
+ (ctx.config
85
+ ? ctx.config.getDatabasePath()
86
+ : `${ctx.env.dataDir}/chainlesschain.db`);
87
+
88
+ const dbManager = coreDb.getDatabaseManager();
89
+ await dbManager.initialize({ dbPath });
90
+ ctx.db = dbManager;
91
+ } catch (err) {
92
+ if (options.verbose)
93
+ logger.verbose(`Database init skipped: ${err.message}`);
94
+ ctx.db = null;
95
+ }
96
+ }
97
+
98
+ // Stage 6: Service container
99
+ if (options.verbose) logger.verbose("Stage 6: Service container...");
100
+ try {
101
+ const coreInfra = await import("@chainlesschain/core-infra");
102
+ ctx.container = coreInfra.getServiceContainer();
103
+ ctx.eventBus = coreInfra.getEventBus();
104
+
105
+ // Register core services
106
+ if (ctx.db) {
107
+ ctx.container.register("database", () => ctx.db, {
108
+ tags: ["core"],
109
+ });
110
+ }
111
+ if (ctx.config) {
112
+ ctx.container.register("config", () => ctx.config, {
113
+ tags: ["core"],
114
+ });
115
+ }
116
+ } catch (err) {
117
+ if (options.verbose)
118
+ logger.verbose(`Infra packages not available: ${err.message}`);
119
+ }
120
+
121
+ // Stage 7: Event bus
122
+ if (options.verbose) logger.verbose("Stage 7: Event bus active");
123
+
124
+ ctx.initialized = true;
125
+ _context = ctx;
126
+
127
+ return ctx;
128
+ } catch (err) {
129
+ logger.error(`Bootstrap failed: ${err.message}`);
130
+ throw err;
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Get the runtime context (must call bootstrap first)
136
+ */
137
+ export function getContext() {
138
+ return _context;
139
+ }
140
+
141
+ /**
142
+ * Gracefully shutdown the runtime
143
+ */
144
+ export async function shutdown() {
145
+ if (!_context) return;
146
+
147
+ try {
148
+ if (_context.db) {
149
+ _context.db.close();
150
+ }
151
+ if (_context.container) {
152
+ await _context.container.disposeAll();
153
+ }
154
+ } catch (err) {
155
+ logger.error(`Shutdown error: ${err.message}`);
156
+ }
157
+
158
+ _context = null;
159
+ }