notoken-core 1.5.1 → 2.0.0

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 (99) hide show
  1. package/config/chat-responses.json +767 -0
  2. package/config/concept-clusters.json +31 -0
  3. package/config/entities.json +93 -0
  4. package/config/image-prompts.json +20 -0
  5. package/config/intent-vectors.json +1 -0
  6. package/config/intents.json +5023 -65
  7. package/config/ollama-models.json +193 -0
  8. package/config/rules.json +32 -1
  9. package/dist/automation/discordPatchright.d.ts +35 -0
  10. package/dist/automation/discordPatchright.js +424 -0
  11. package/dist/automation/discordSetup.d.ts +31 -0
  12. package/dist/automation/discordSetup.js +338 -0
  13. package/dist/conversation/coreference.js +44 -4
  14. package/dist/conversation/pendingActions.d.ts +55 -0
  15. package/dist/conversation/pendingActions.js +127 -0
  16. package/dist/conversation/store.d.ts +72 -0
  17. package/dist/conversation/store.js +140 -1
  18. package/dist/conversation/topicTracker.d.ts +36 -0
  19. package/dist/conversation/topicTracker.js +141 -0
  20. package/dist/execution/ssh.d.ts +42 -1
  21. package/dist/execution/ssh.js +532 -3
  22. package/dist/handlers/executor.js +3981 -16
  23. package/dist/index.d.ts +25 -3
  24. package/dist/index.js +36 -2
  25. package/dist/nlp/batchParser.d.ts +30 -0
  26. package/dist/nlp/batchParser.js +77 -0
  27. package/dist/nlp/conceptExpansion.d.ts +54 -0
  28. package/dist/nlp/conceptExpansion.js +136 -0
  29. package/dist/nlp/conceptRouter.d.ts +49 -0
  30. package/dist/nlp/conceptRouter.js +302 -0
  31. package/dist/nlp/confidenceCalibrator.d.ts +62 -0
  32. package/dist/nlp/confidenceCalibrator.js +116 -0
  33. package/dist/nlp/correctionLearner.d.ts +45 -0
  34. package/dist/nlp/correctionLearner.js +207 -0
  35. package/dist/nlp/entitySpellCorrect.d.ts +35 -0
  36. package/dist/nlp/entitySpellCorrect.js +141 -0
  37. package/dist/nlp/knowledgeGraph.d.ts +70 -0
  38. package/dist/nlp/knowledgeGraph.js +380 -0
  39. package/dist/nlp/llmFallback.js +28 -1
  40. package/dist/nlp/multiClassifier.js +91 -6
  41. package/dist/nlp/multiIntent.d.ts +43 -0
  42. package/dist/nlp/multiIntent.js +154 -0
  43. package/dist/nlp/parseIntent.d.ts +6 -1
  44. package/dist/nlp/parseIntent.js +180 -5
  45. package/dist/nlp/ruleParser.js +315 -0
  46. package/dist/nlp/semanticSimilarity.d.ts +30 -0
  47. package/dist/nlp/semanticSimilarity.js +174 -0
  48. package/dist/nlp/vocabularyBuilder.d.ts +43 -0
  49. package/dist/nlp/vocabularyBuilder.js +224 -0
  50. package/dist/nlp/wikidata.d.ts +49 -0
  51. package/dist/nlp/wikidata.js +228 -0
  52. package/dist/policy/confirm.d.ts +10 -0
  53. package/dist/policy/confirm.js +39 -0
  54. package/dist/policy/safety.js +6 -4
  55. package/dist/utils/aliases.d.ts +5 -0
  56. package/dist/utils/aliases.js +39 -0
  57. package/dist/utils/analysis.js +71 -15
  58. package/dist/utils/browser.d.ts +64 -0
  59. package/dist/utils/browser.js +364 -0
  60. package/dist/utils/commandHistory.d.ts +20 -0
  61. package/dist/utils/commandHistory.js +108 -0
  62. package/dist/utils/completer.d.ts +17 -0
  63. package/dist/utils/completer.js +79 -0
  64. package/dist/utils/config.js +32 -2
  65. package/dist/utils/dbQuery.d.ts +25 -0
  66. package/dist/utils/dbQuery.js +248 -0
  67. package/dist/utils/discordDiag.d.ts +35 -0
  68. package/dist/utils/discordDiag.js +826 -0
  69. package/dist/utils/diskCleanup.d.ts +36 -0
  70. package/dist/utils/diskCleanup.js +775 -0
  71. package/dist/utils/entityResolver.d.ts +107 -0
  72. package/dist/utils/entityResolver.js +468 -0
  73. package/dist/utils/imageGen.d.ts +92 -0
  74. package/dist/utils/imageGen.js +2031 -0
  75. package/dist/utils/installTracker.d.ts +57 -0
  76. package/dist/utils/installTracker.js +160 -0
  77. package/dist/utils/multiExec.d.ts +21 -0
  78. package/dist/utils/multiExec.js +141 -0
  79. package/dist/utils/openclawDiag.d.ts +29 -0
  80. package/dist/utils/openclawDiag.js +1035 -0
  81. package/dist/utils/output.js +4 -0
  82. package/dist/utils/platform.js +2 -1
  83. package/dist/utils/progressReporter.d.ts +50 -0
  84. package/dist/utils/progressReporter.js +58 -0
  85. package/dist/utils/projectDetect.d.ts +44 -0
  86. package/dist/utils/projectDetect.js +319 -0
  87. package/dist/utils/projectScanner.d.ts +44 -0
  88. package/dist/utils/projectScanner.js +312 -0
  89. package/dist/utils/shellCompat.d.ts +78 -0
  90. package/dist/utils/shellCompat.js +186 -0
  91. package/dist/utils/smartArchive.d.ts +16 -0
  92. package/dist/utils/smartArchive.js +172 -0
  93. package/dist/utils/smartRetry.d.ts +26 -0
  94. package/dist/utils/smartRetry.js +114 -0
  95. package/dist/utils/updater.d.ts +1 -0
  96. package/dist/utils/updater.js +1 -1
  97. package/dist/utils/version.d.ts +20 -0
  98. package/dist/utils/version.js +212 -0
  99. package/package.json +6 -3
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Command History — persistent history with search.
3
+ *
4
+ * Stores every command typed in interactive mode.
5
+ * Supports:
6
+ * - History file (~/.notoken/command-history.txt)
7
+ * - Search (Ctrl+R style fuzzy search)
8
+ * - Recent commands for suggestions
9
+ * - Dedup consecutive duplicates
10
+ */
11
+ import { existsSync, readFileSync, appendFileSync, mkdirSync } from "node:fs";
12
+ import { resolve } from "node:path";
13
+ import { homedir } from "node:os";
14
+ const HISTORY_DIR = resolve(homedir(), ".notoken");
15
+ const HISTORY_FILE = resolve(HISTORY_DIR, "command-history.txt");
16
+ const MAX_HISTORY = 2000;
17
+ let _history = null;
18
+ /** Load history from disk. */
19
+ export function loadHistory() {
20
+ if (_history)
21
+ return _history;
22
+ if (!existsSync(HISTORY_DIR))
23
+ mkdirSync(HISTORY_DIR, { recursive: true });
24
+ if (existsSync(HISTORY_FILE)) {
25
+ _history = readFileSync(HISTORY_FILE, "utf-8")
26
+ .split("\n")
27
+ .filter(l => l.trim())
28
+ .slice(-MAX_HISTORY);
29
+ }
30
+ else {
31
+ _history = [];
32
+ }
33
+ return _history;
34
+ }
35
+ /** Add a command to history (dedup consecutive). */
36
+ export function addToHistory(command) {
37
+ const history = loadHistory();
38
+ const trimmed = command.trim();
39
+ if (!trimmed)
40
+ return;
41
+ // Don't add if it's the same as the last command
42
+ if (history.length > 0 && history[history.length - 1] === trimmed)
43
+ return;
44
+ // Don't add meta commands
45
+ if (trimmed.startsWith(":") || trimmed.startsWith("/"))
46
+ return;
47
+ history.push(trimmed);
48
+ // Append to file
49
+ try {
50
+ appendFileSync(HISTORY_FILE, trimmed + "\n");
51
+ }
52
+ catch { }
53
+ // Trim in memory if too long
54
+ if (history.length > MAX_HISTORY) {
55
+ _history = history.slice(-MAX_HISTORY);
56
+ }
57
+ }
58
+ /** Search history with fuzzy matching. */
59
+ export function searchHistory(query, limit = 10) {
60
+ const history = loadHistory();
61
+ const lower = query.toLowerCase();
62
+ const matches = [];
63
+ for (let i = history.length - 1; i >= 0; i--) {
64
+ const cmd = history[i];
65
+ const cmdLower = cmd.toLowerCase();
66
+ if (cmdLower.includes(lower)) {
67
+ // Exact substring match — higher score for more recent
68
+ const recency = (i / history.length) * 0.3; // 0-0.3
69
+ const relevance = lower.length / cmdLower.length; // longer match = better
70
+ matches.push({ cmd, score: 0.5 + recency + relevance * 0.2 });
71
+ }
72
+ else {
73
+ // Check word overlap
74
+ const queryWords = lower.split(/\s+/);
75
+ const cmdWords = new Set(cmdLower.split(/\s+/));
76
+ const overlap = queryWords.filter(w => cmdWords.has(w)).length;
77
+ if (overlap > 0) {
78
+ const score = (overlap / queryWords.length) * 0.4 + (i / history.length) * 0.2;
79
+ matches.push({ cmd, score });
80
+ }
81
+ }
82
+ }
83
+ // Dedup and sort by score
84
+ const seen = new Set();
85
+ return matches
86
+ .sort((a, b) => b.score - a.score)
87
+ .filter(m => { if (seen.has(m.cmd))
88
+ return false; seen.add(m.cmd); return true; })
89
+ .slice(0, limit)
90
+ .map(m => m.cmd);
91
+ }
92
+ /** Get the N most recent unique commands. */
93
+ export function getRecentCommands(limit = 10) {
94
+ const history = loadHistory();
95
+ const seen = new Set();
96
+ const recent = [];
97
+ for (let i = history.length - 1; i >= 0 && recent.length < limit; i--) {
98
+ if (!seen.has(history[i])) {
99
+ seen.add(history[i]);
100
+ recent.push(history[i]);
101
+ }
102
+ }
103
+ return recent;
104
+ }
105
+ /** Get history for readline (returns copy of array for rl.history). */
106
+ export function getReadlineHistory() {
107
+ return [...loadHistory()].reverse();
108
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tab-completion for notoken interactive mode.
3
+ *
4
+ * Builds a flat list of completable phrases from intents, meta commands,
5
+ * service aliases, common verbs, and recent history. Cached and rebuilt
6
+ * every 60 seconds.
7
+ */
8
+ /**
9
+ * Build (or rebuild) the flat list of completable strings.
10
+ */
11
+ export declare function buildCompletions(): string[];
12
+ /**
13
+ * Readline-compatible completer function.
14
+ *
15
+ * Signature: (line: string) => [matches: string[], line: string]
16
+ */
17
+ export declare function completeInput(line: string): [string[], string];
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Tab-completion for notoken interactive mode.
3
+ *
4
+ * Builds a flat list of completable phrases from intents, meta commands,
5
+ * service aliases, common verbs, and recent history. Cached and rebuilt
6
+ * every 60 seconds.
7
+ */
8
+ import { loadIntents } from "./config.js";
9
+ import { loadRules } from "./config.js";
10
+ import { getRecentHistory } from "../context/history.js";
11
+ // ── Cache ───────────────────────────────────────────────────────────────────
12
+ let cached = [];
13
+ let lastBuilt = 0;
14
+ const CACHE_TTL = 60_000; // 60 s
15
+ const META_COMMANDS = [
16
+ "/jobs", "/help", "/quit", "/output", "/kill",
17
+ "/aliases", "/history",
18
+ ];
19
+ const COMMON_VERBS = [
20
+ "restart", "check", "show", "list", "install",
21
+ "diagnose", "monitor",
22
+ ];
23
+ /**
24
+ * Build (or rebuild) the flat list of completable strings.
25
+ */
26
+ export function buildCompletions() {
27
+ const set = new Set();
28
+ // Meta commands
29
+ for (const cmd of META_COMMANDS)
30
+ set.add(cmd);
31
+ // Common verbs
32
+ for (const v of COMMON_VERBS)
33
+ set.add(v);
34
+ // Intent synonyms
35
+ try {
36
+ const intents = loadIntents();
37
+ for (const intent of intents) {
38
+ for (const syn of intent.synonyms ?? []) {
39
+ set.add(syn.toLowerCase());
40
+ }
41
+ }
42
+ }
43
+ catch { /* config may not be loaded yet */ }
44
+ // Service alias names
45
+ try {
46
+ const rules = loadRules();
47
+ for (const [service, aliases] of Object.entries(rules.serviceAliases)) {
48
+ set.add(service);
49
+ for (const a of aliases)
50
+ set.add(a.toLowerCase());
51
+ }
52
+ }
53
+ catch { /* rules may not be loaded yet */ }
54
+ // Recent commands from history
55
+ try {
56
+ const recent = getRecentHistory(20);
57
+ for (const entry of recent) {
58
+ if (entry.rawText)
59
+ set.add(entry.rawText);
60
+ }
61
+ }
62
+ catch { /* history may be empty */ }
63
+ cached = [...set].sort();
64
+ lastBuilt = Date.now();
65
+ return cached;
66
+ }
67
+ /**
68
+ * Readline-compatible completer function.
69
+ *
70
+ * Signature: (line: string) => [matches: string[], line: string]
71
+ */
72
+ export function completeInput(line) {
73
+ if (Date.now() - lastBuilt > CACHE_TTL || cached.length === 0) {
74
+ buildCompletions();
75
+ }
76
+ const lower = line.toLowerCase();
77
+ const hits = cached.filter((c) => c.toLowerCase().startsWith(lower));
78
+ return [hits.length > 0 ? hits : cached, line];
79
+ }
@@ -1,8 +1,8 @@
1
- import { readFileSync } from "node:fs";
1
+ import { readFileSync, existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { RulesConfig } from "../types/rules.js";
4
4
  import { IntentsConfig } from "../types/intent.js";
5
- import { CONFIG_DIR } from "./paths.js";
5
+ import { CONFIG_DIR, USER_HOME } from "./paths.js";
6
6
  import { pluginRegistry } from "../plugins/registry.js";
7
7
  let cachedRules = null;
8
8
  let cachedIntents = null;
@@ -40,6 +40,36 @@ export function loadIntents(forceReload = false) {
40
40
  cachedIntents.intents.push(pi);
41
41
  }
42
42
  }
43
+ // Merge user custom intents from ~/.notoken/custom-intents.json
44
+ const customFile = resolve(USER_HOME, "custom-intents.json");
45
+ if (existsSync(customFile)) {
46
+ try {
47
+ const customRaw = readFileSync(customFile, "utf-8");
48
+ const customData = JSON.parse(customRaw);
49
+ const customIntents = customData.intents ?? [];
50
+ for (const ci of customIntents) {
51
+ const entry = ci;
52
+ // Build a full IntentDef with sensible defaults for user-defined intents
53
+ const def = {
54
+ name: entry.name,
55
+ description: entry.description ?? "",
56
+ synonyms: entry.synonyms ?? [],
57
+ fields: entry.fields ?? {},
58
+ command: entry.command ?? "",
59
+ execution: entry.execution ?? "local",
60
+ requiresConfirmation: entry.requiresConfirmation ?? true,
61
+ riskLevel: entry.riskLevel ?? "medium",
62
+ examples: entry.examples ?? entry.synonyms ?? [],
63
+ };
64
+ if (!cachedIntents.intents.find((i) => i.name === def.name)) {
65
+ cachedIntents.intents.push(def);
66
+ }
67
+ }
68
+ }
69
+ catch {
70
+ // Silently ignore malformed custom intents file
71
+ }
72
+ }
43
73
  return cachedIntents.intents;
44
74
  }
45
75
  export function getIntentDef(name) {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Natural language → SQL query builder.
3
+ *
4
+ * Translates phrases like:
5
+ * "look at items and find sally" → SELECT * FROM items WHERE ... LIKE '%sally%'
6
+ * "show me all users" → SELECT * FROM users
7
+ * "count orders" → SELECT COUNT(*) FROM orders
8
+ * "show tables" → \dt (postgres) or SHOW TABLES (mysql)
9
+ * "describe users table" → \d users or DESCRIBE users
10
+ * "find orders where total > 100" → SELECT * FROM orders WHERE total > 100
11
+ */
12
+ export type DbType = "postgres" | "mysql";
13
+ export interface DbQueryResult {
14
+ query: string;
15
+ command: string;
16
+ explanation: string;
17
+ }
18
+ /**
19
+ * Build a SQL query from natural language.
20
+ */
21
+ export declare function buildQuery(rawText: string, fields: Record<string, unknown>, dbType?: DbType): DbQueryResult;
22
+ /**
23
+ * Format query result for display — show the SQL, explain it, then run.
24
+ */
25
+ export declare function formatQueryPlan(result: DbQueryResult): string;
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Natural language → SQL query builder.
3
+ *
4
+ * Translates phrases like:
5
+ * "look at items and find sally" → SELECT * FROM items WHERE ... LIKE '%sally%'
6
+ * "show me all users" → SELECT * FROM users
7
+ * "count orders" → SELECT COUNT(*) FROM orders
8
+ * "show tables" → \dt (postgres) or SHOW TABLES (mysql)
9
+ * "describe users table" → \d users or DESCRIBE users
10
+ * "find orders where total > 100" → SELECT * FROM orders WHERE total > 100
11
+ */
12
+ const c = {
13
+ reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
14
+ green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m", red: "\x1b[31m",
15
+ };
16
+ /**
17
+ * Build a SQL query from natural language.
18
+ */
19
+ export function buildQuery(rawText, fields, dbType = "postgres") {
20
+ const text = rawText.toLowerCase();
21
+ const table = fields.table ?? extractTable(text);
22
+ const search = fields.search ?? extractSearchTerm(text);
23
+ // "show tables" / "list tables"
24
+ if (text.match(/show\s+tables|list\s+tables|what\s+tables|which\s+tables/)) {
25
+ return dbType === "postgres"
26
+ ? { query: "\\dt", command: buildPsqlCmd(fields, "\\dt"), explanation: "List all tables" }
27
+ : { query: "SHOW TABLES;", command: buildMysqlCmd(fields, "SHOW TABLES;"), explanation: "List all tables" };
28
+ }
29
+ // "describe <table>" / "schema of <table>" / "columns in <table>"
30
+ const describeMatch = text.match(/describe\s+(\w+)|schema\s+(?:of\s+)?(\w+)|columns\s+(?:in\s+|of\s+)?(\w+)|structure\s+(?:of\s+)?(\w+)/);
31
+ if (describeMatch) {
32
+ const t = describeMatch[1] || describeMatch[2] || describeMatch[3] || describeMatch[4];
33
+ return dbType === "postgres"
34
+ ? { query: `\\d ${t}`, command: buildPsqlCmd(fields, `\\d ${t}`), explanation: `Describe table ${t}` }
35
+ : { query: `DESCRIBE ${t};`, command: buildMysqlCmd(fields, `DESCRIBE ${t};`), explanation: `Describe table ${t}` };
36
+ }
37
+ // "count <table>"
38
+ if (text.match(/count|how\s+many/)) {
39
+ if (table) {
40
+ const where = extractWhere(text, table);
41
+ const query = where
42
+ ? `SELECT COUNT(*) FROM ${table} WHERE ${where};`
43
+ : `SELECT COUNT(*) FROM ${table};`;
44
+ return {
45
+ query,
46
+ command: dbType === "postgres" ? buildPsqlCmd(fields, query) : buildMysqlCmd(fields, query),
47
+ explanation: `Count rows in ${table}${where ? ` where ${where}` : ""}`,
48
+ };
49
+ }
50
+ }
51
+ // Search query: "find sally in items" / "look at items find sally"
52
+ if (table && search) {
53
+ const query = `SELECT * FROM ${table} WHERE ${buildSearchWhere(search, table, dbType)} LIMIT 50;`;
54
+ return {
55
+ query,
56
+ command: dbType === "postgres" ? buildPsqlCmd(fields, query) : buildMysqlCmd(fields, query),
57
+ explanation: `Search ${table} for "${search}"`,
58
+ };
59
+ }
60
+ // Search with no table: "find sally" — search across all tables
61
+ if (search && !table) {
62
+ if (dbType === "postgres") {
63
+ // Build a script that searches all tables for the term
64
+ const query = buildSearchAllTablesPg(search);
65
+ return {
66
+ query,
67
+ command: buildPsqlCmd(fields, query),
68
+ explanation: `Search ALL tables for "${search}"`,
69
+ };
70
+ }
71
+ else {
72
+ const query = buildSearchAllTablesMysql(search, fields.database ?? "");
73
+ return {
74
+ query,
75
+ command: buildMysqlCmd(fields, query),
76
+ explanation: `Search ALL tables for "${search}"`,
77
+ };
78
+ }
79
+ }
80
+ // Simple select: "show me <table>" / "list <table>" / "all <table>"
81
+ if (table) {
82
+ const where = extractWhere(text, table);
83
+ const limit = extractLimit(text);
84
+ const query = where
85
+ ? `SELECT * FROM ${table} WHERE ${where} LIMIT ${limit};`
86
+ : `SELECT * FROM ${table} LIMIT ${limit};`;
87
+ return {
88
+ query,
89
+ command: dbType === "postgres" ? buildPsqlCmd(fields, query) : buildMysqlCmd(fields, query),
90
+ explanation: `Show rows from ${table}${where ? ` where ${where}` : ""}`,
91
+ };
92
+ }
93
+ // Fallback: raw query if it looks like SQL
94
+ if (text.match(/^(select|insert|update|delete|create|alter|drop)\s/i)) {
95
+ const query = rawText.trim().endsWith(";") ? rawText.trim() : rawText.trim() + ";";
96
+ return {
97
+ query,
98
+ command: dbType === "postgres" ? buildPsqlCmd(fields, query) : buildMysqlCmd(fields, query),
99
+ explanation: "Raw SQL query",
100
+ };
101
+ }
102
+ return {
103
+ query: "",
104
+ command: "",
105
+ explanation: "Could not build a query. Try: show tables, describe <table>, or select * from <table>",
106
+ };
107
+ }
108
+ // ─── Extractors ──────────────────────────────────────────────────────────────
109
+ function extractTable(text) {
110
+ // "in <table>" / "from <table>" / "the <table> table" / "my <table>"
111
+ const patterns = [
112
+ /(?:from|in|at|into)\s+(?:the\s+)?(\w+)\s*(?:table)?/,
113
+ /(?:my|the|all)\s+(\w+)\s*(?:table)?/,
114
+ /(?:look\s+at|show|list|query|search|find\s+in)\s+(?:the\s+)?(?:my\s+)?(\w+)/,
115
+ /(\w+)\s+table/,
116
+ ];
117
+ for (const p of patterns) {
118
+ const match = text.match(p);
119
+ if (match) {
120
+ const t = match[1].toLowerCase();
121
+ // Filter out common non-table words
122
+ if (!["me", "all", "the", "my", "this", "that", "it", "and", "on", "to", "for", "with", "where", "tables", "database"].includes(t)) {
123
+ return t;
124
+ }
125
+ }
126
+ }
127
+ return null;
128
+ }
129
+ function extractSearchTerm(text) {
130
+ // "find <term>" / "has <term>" / "contains <term>" / "with <term>" / "named <term>"
131
+ const patterns = [
132
+ /(?:find|has|contains?|with|named|called|matching|like)\s+(?:the\s+)?['"]?(\w[\w\s]*?)['"]?\s*(?:in|$)/,
133
+ /(?:find|search\s+for|look\s+for)\s+['"]?(\w[\w\s]*?)['"]?\s*(?:in|from|$)/,
134
+ /(?:that\s+has|that\s+contains?)\s+['"]?(\w[\w\s]*?)['"]?/,
135
+ /(?:find\s+(?:the\s+)?(?:one\s+)?(?:that\s+)?(?:has\s+)?)?['"]?(\w+)['"]?\s*$/,
136
+ ];
137
+ for (const p of patterns) {
138
+ const match = text.match(p);
139
+ if (match) {
140
+ const term = match[1].trim();
141
+ if (term && !["it", "them", "the", "one", "that"].includes(term.toLowerCase())) {
142
+ return term;
143
+ }
144
+ }
145
+ }
146
+ return null;
147
+ }
148
+ function extractWhere(text, table) {
149
+ // "where <condition>" — pass through
150
+ const whereMatch = text.match(/where\s+(.+?)(?:\s+limit|\s*$)/);
151
+ if (whereMatch)
152
+ return whereMatch[1].trim();
153
+ return null;
154
+ }
155
+ function extractLimit(text) {
156
+ const match = text.match(/(?:limit|top|first|last)\s+(\d+)/);
157
+ if (match)
158
+ return parseInt(match[1]);
159
+ // "show me 20 users" / "10 orders"
160
+ const numMatch = text.match(/(?:show|list|get)\s+(?:me\s+)?(\d+)/);
161
+ if (numMatch)
162
+ return parseInt(numMatch[1]);
163
+ return 50;
164
+ }
165
+ function buildSearchWhere(search, table, dbType) {
166
+ const escaped = escapeSql(search);
167
+ if (dbType === "postgres") {
168
+ // Cast entire row to text and search — works across all columns
169
+ return `${table}::text ILIKE '%${escaped}%'`;
170
+ }
171
+ // MySQL: search common text column names
172
+ return `CONCAT_WS(' ', COALESCE(name,''), COALESCE(title,''), COALESCE(email,''), COALESCE(description,''), COALESCE(username,''), COALESCE(first_name,''), COALESCE(last_name,'')) LIKE '%${escaped}%'`;
173
+ }
174
+ /**
175
+ * Postgres: search all tables for a term.
176
+ * Casts each row to text and checks with ILIKE.
177
+ */
178
+ function buildSearchAllTablesPg(search) {
179
+ const escaped = escapeSql(search);
180
+ // Use a DO block that iterates through all user tables
181
+ return `DO $$
182
+ DECLARE
183
+ r RECORD;
184
+ tbl TEXT;
185
+ cnt INTEGER;
186
+ BEGIN
187
+ FOR r IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' LOOP
188
+ tbl := r.tablename;
189
+ EXECUTE format('SELECT COUNT(*) FROM %I WHERE %I::text ILIKE $1', tbl, tbl) INTO cnt USING '%${escaped}%';
190
+ IF cnt > 0 THEN
191
+ RAISE NOTICE 'Found % match(es) in table: %', cnt, tbl;
192
+ END IF;
193
+ END LOOP;
194
+ END $$;`;
195
+ }
196
+ /**
197
+ * MySQL: search all tables for a term.
198
+ */
199
+ function buildSearchAllTablesMysql(search, database) {
200
+ const escaped = escapeSql(search);
201
+ // MySQL approach: generate SELECT statements for each table
202
+ return `SELECT TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${database || "DATABASE()"}' AND DATA_TYPE IN ('varchar','text','char','longtext','mediumtext') ORDER BY TABLE_NAME;`;
203
+ }
204
+ function escapeSql(val) {
205
+ return val.replace(/'/g, "''").replace(/\\/g, "\\\\");
206
+ }
207
+ // ─── Command builders ────────────────────────────────────────────────────────
208
+ function buildPsqlCmd(fields, query) {
209
+ const db = fields.database ?? "";
210
+ const host = fields.host ?? "";
211
+ const user = fields.user ?? "";
212
+ const parts = ["psql"];
213
+ if (host)
214
+ parts.push(`-h ${host}`);
215
+ if (user)
216
+ parts.push(`-U ${user}`);
217
+ if (db)
218
+ parts.push(db);
219
+ parts.push(`-c ${JSON.stringify(query)}`);
220
+ return parts.join(" ");
221
+ }
222
+ function buildMysqlCmd(fields, query) {
223
+ const db = fields.database ?? "";
224
+ const host = fields.host ?? "";
225
+ const user = fields.user ?? "";
226
+ const parts = ["mysql"];
227
+ if (host)
228
+ parts.push(`-h ${host}`);
229
+ if (user)
230
+ parts.push(`-u ${user}`);
231
+ if (db)
232
+ parts.push(db);
233
+ parts.push(`-e ${JSON.stringify(query)}`);
234
+ return parts.join(" ");
235
+ }
236
+ /**
237
+ * Format query result for display — show the SQL, explain it, then run.
238
+ */
239
+ export function formatQueryPlan(result) {
240
+ if (!result.query)
241
+ return result.explanation;
242
+ const lines = [];
243
+ lines.push(`\n${c.bold}${c.cyan}── Query Plan ──${c.reset}\n`);
244
+ lines.push(` ${c.bold}SQL:${c.reset} ${c.green}${result.query}${c.reset}`);
245
+ lines.push(` ${c.dim}${result.explanation}${c.reset}`);
246
+ lines.push(` ${c.dim}Command: ${result.command}${c.reset}`);
247
+ return lines.join("\n");
248
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Discord bot diagnostics, setup, and auto-fix.
3
+ *
4
+ * `diagnoseDiscord()` — runs full checklist and auto-fixes everything:
5
+ * 1. Token valid?
6
+ * 2. Bot in guilds? → auto-invite via patchright
7
+ * 3. Intents enabled in OpenClaw config? → auto-fix
8
+ * 4. DM/group policy correct? → auto-fix
9
+ * 5. OpenClaw version check
10
+ * 6. Restart gateway if config changed
11
+ * 7. Poll gateway connection up to 60s
12
+ * 8. If 4014 error → enable intents via patchright → restart → re-poll
13
+ * 9. Check channels
14
+ * 10. Auto-approve pairing codes
15
+ * 11. Send test message + verify response
16
+ *
17
+ * The full chain runs end-to-end without stopping.
18
+ * User only needs to handle captcha/MFA when patchright prompts.
19
+ */
20
+ /**
21
+ * Live Discord connection monitor.
22
+ * Prints real-time status updates as the gateway connects to Discord.
23
+ * Auto-restarts if stuck, auto-waits through rate limits.
24
+ *
25
+ * Usage: "monitor discord" or "watch discord"
26
+ */
27
+ export declare function monitorDiscord(maxMinutes?: number): Promise<string>;
28
+ /**
29
+ * Full Discord diagnostic and auto-fix chain.
30
+ */
31
+ export declare function diagnoseDiscord(): Promise<string>;
32
+ /**
33
+ * Quick Discord status check.
34
+ */
35
+ export declare function quickDiscordCheck(): Promise<string>;