notoken-core 1.6.0 → 1.8.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 (123) hide show
  1. package/config/ascii-art.json +12 -0
  2. package/config/chat-responses.json +1019 -0
  3. package/config/cheat-sheets.json +94 -0
  4. package/config/concept-clusters.json +31 -0
  5. package/config/daily-tips.json +105 -0
  6. package/config/entities.json +93 -0
  7. package/config/history-today.json +9762 -0
  8. package/config/image-prompts.json +20 -0
  9. package/config/intent-vectors.json +1 -0
  10. package/config/intents.json +5354 -85
  11. package/config/ollama-models.json +193 -0
  12. package/config/rules.json +32 -1
  13. package/config/startup-quotes.json +45 -0
  14. package/dist/automation/discordPatchright.d.ts +35 -0
  15. package/dist/automation/discordPatchright.js +424 -0
  16. package/dist/automation/discordSetup.d.ts +31 -0
  17. package/dist/automation/discordSetup.js +338 -0
  18. package/dist/automation/smAutomation.d.ts +82 -0
  19. package/dist/automation/smAutomation.js +448 -0
  20. package/dist/conversation/coreference.js +44 -4
  21. package/dist/conversation/pendingActions.d.ts +55 -0
  22. package/dist/conversation/pendingActions.js +127 -0
  23. package/dist/conversation/store.d.ts +72 -0
  24. package/dist/conversation/store.js +140 -1
  25. package/dist/conversation/topicTracker.d.ts +36 -0
  26. package/dist/conversation/topicTracker.js +141 -0
  27. package/dist/execution/ssh.d.ts +42 -1
  28. package/dist/execution/ssh.js +538 -3
  29. package/dist/handlers/executor.d.ts +2 -0
  30. package/dist/handlers/executor.js +4234 -31
  31. package/dist/index.d.ts +35 -4
  32. package/dist/index.js +51 -3
  33. package/dist/nlp/batchParser.d.ts +30 -0
  34. package/dist/nlp/batchParser.js +77 -0
  35. package/dist/nlp/conceptExpansion.d.ts +54 -0
  36. package/dist/nlp/conceptExpansion.js +136 -0
  37. package/dist/nlp/conceptRouter.d.ts +49 -0
  38. package/dist/nlp/conceptRouter.js +302 -0
  39. package/dist/nlp/confidenceCalibrator.d.ts +62 -0
  40. package/dist/nlp/confidenceCalibrator.js +116 -0
  41. package/dist/nlp/correctionLearner.d.ts +45 -0
  42. package/dist/nlp/correctionLearner.js +207 -0
  43. package/dist/nlp/entitySpellCorrect.d.ts +35 -0
  44. package/dist/nlp/entitySpellCorrect.js +141 -0
  45. package/dist/nlp/knowledgeGraph.d.ts +70 -0
  46. package/dist/nlp/knowledgeGraph.js +380 -0
  47. package/dist/nlp/llmFallback.js +28 -1
  48. package/dist/nlp/multiClassifier.js +91 -6
  49. package/dist/nlp/multiIntent.d.ts +43 -0
  50. package/dist/nlp/multiIntent.js +154 -0
  51. package/dist/nlp/parseIntent.d.ts +6 -1
  52. package/dist/nlp/parseIntent.js +180 -5
  53. package/dist/nlp/ruleParser.js +317 -0
  54. package/dist/nlp/semanticSimilarity.d.ts +30 -0
  55. package/dist/nlp/semanticSimilarity.js +174 -0
  56. package/dist/nlp/vocabularyBuilder.d.ts +43 -0
  57. package/dist/nlp/vocabularyBuilder.js +224 -0
  58. package/dist/nlp/wikidata.d.ts +49 -0
  59. package/dist/nlp/wikidata.js +228 -0
  60. package/dist/policy/confirm.d.ts +10 -0
  61. package/dist/policy/confirm.js +39 -0
  62. package/dist/policy/safety.js +6 -4
  63. package/dist/types/intent.d.ts +8 -0
  64. package/dist/types/intent.js +1 -0
  65. package/dist/utils/achievements.d.ts +38 -0
  66. package/dist/utils/achievements.js +126 -0
  67. package/dist/utils/aliases.d.ts +5 -0
  68. package/dist/utils/aliases.js +39 -0
  69. package/dist/utils/analysis.js +71 -15
  70. package/dist/utils/bookmarks.d.ts +13 -0
  71. package/dist/utils/bookmarks.js +51 -0
  72. package/dist/utils/browser.d.ts +64 -0
  73. package/dist/utils/browser.js +364 -0
  74. package/dist/utils/commandHistory.d.ts +20 -0
  75. package/dist/utils/commandHistory.js +108 -0
  76. package/dist/utils/completer.d.ts +17 -0
  77. package/dist/utils/completer.js +79 -0
  78. package/dist/utils/config.js +32 -2
  79. package/dist/utils/dbQuery.d.ts +25 -0
  80. package/dist/utils/dbQuery.js +248 -0
  81. package/dist/utils/devTools.d.ts +35 -0
  82. package/dist/utils/devTools.js +95 -0
  83. package/dist/utils/discordDiag.d.ts +35 -0
  84. package/dist/utils/discordDiag.js +826 -0
  85. package/dist/utils/diskCleanup.d.ts +36 -0
  86. package/dist/utils/diskCleanup.js +775 -0
  87. package/dist/utils/entityResolver.d.ts +107 -0
  88. package/dist/utils/entityResolver.js +468 -0
  89. package/dist/utils/imageGen.d.ts +92 -0
  90. package/dist/utils/imageGen.js +2031 -0
  91. package/dist/utils/installTracker.d.ts +57 -0
  92. package/dist/utils/installTracker.js +160 -0
  93. package/dist/utils/multiExec.d.ts +21 -0
  94. package/dist/utils/multiExec.js +141 -0
  95. package/dist/utils/openclawDiag.d.ts +29 -0
  96. package/dist/utils/openclawDiag.js +1035 -0
  97. package/dist/utils/output.js +4 -0
  98. package/dist/utils/platform.js +2 -1
  99. package/dist/utils/progressReporter.d.ts +50 -0
  100. package/dist/utils/progressReporter.js +58 -0
  101. package/dist/utils/projectDetect.d.ts +44 -0
  102. package/dist/utils/projectDetect.js +319 -0
  103. package/dist/utils/projectScanner.d.ts +44 -0
  104. package/dist/utils/projectScanner.js +312 -0
  105. package/dist/utils/shellCompat.d.ts +78 -0
  106. package/dist/utils/shellCompat.js +186 -0
  107. package/dist/utils/smartArchive.d.ts +16 -0
  108. package/dist/utils/smartArchive.js +172 -0
  109. package/dist/utils/smartRetry.d.ts +26 -0
  110. package/dist/utils/smartRetry.js +114 -0
  111. package/dist/utils/snippets.d.ts +13 -0
  112. package/dist/utils/snippets.js +53 -0
  113. package/dist/utils/stabilityMatrixManager.d.ts +80 -0
  114. package/dist/utils/stabilityMatrixManager.js +268 -0
  115. package/dist/utils/teachMode.d.ts +41 -0
  116. package/dist/utils/teachMode.js +100 -0
  117. package/dist/utils/timer.d.ts +22 -0
  118. package/dist/utils/timer.js +52 -0
  119. package/dist/utils/updater.d.ts +1 -0
  120. package/dist/utils/updater.js +1 -1
  121. package/dist/utils/version.d.ts +20 -0
  122. package/dist/utils/version.js +212 -0
  123. package/package.json +6 -3
@@ -0,0 +1,448 @@
1
+ /**
2
+ * Stability Matrix UI Automation via PowerShell.
3
+ *
4
+ * Uses Windows UIAutomation COM API through PowerShell to:
5
+ * - Launch SM and wait for window
6
+ * - Click "Add Package" button
7
+ * - Select the right package (Forge, ComfyUI, etc.)
8
+ * - Click Install and monitor progress
9
+ * - Click Launch when done
10
+ * - Configure settings
11
+ *
12
+ * All automation runs on Windows side via PowerShell.
13
+ * From WSL, commands are sent through /mnt/c/.../powershell.exe
14
+ */
15
+ import { execSync } from "node:child_process";
16
+ import { findStabilityMatrix } from "../utils/stabilityMatrixManager.js";
17
+ const c = {
18
+ reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
19
+ green: "\x1b[32m", yellow: "\x1b[33m", red: "\x1b[31m", cyan: "\x1b[36m",
20
+ };
21
+ const PS = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe";
22
+ const PS_WIN = "powershell.exe";
23
+ function tryExec(cmd, timeout = 10000) {
24
+ try {
25
+ return execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout }).trim() || null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ function isWSL() {
32
+ try {
33
+ return !!execSync("grep -qi microsoft /proc/version && echo wsl", {
34
+ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 2000,
35
+ }).trim();
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ function ps(script, timeout = 30000) {
42
+ const psExe = isWSL() ? PS : PS_WIN;
43
+ // Escape for bash → powershell
44
+ const escaped = script.replace(/'/g, "''");
45
+ try {
46
+ return execSync(`${psExe} -NoProfile -Command "${escaped}"`, {
47
+ encoding: "utf-8",
48
+ stdio: ["pipe", "pipe", "pipe"],
49
+ timeout,
50
+ }).trim() || null;
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ function psRun(script, timeout = 30000) {
57
+ const psExe = isWSL() ? PS : PS_WIN;
58
+ execSync(`${psExe} -NoProfile -Command "${script.replace(/'/g, "''")}"`, {
59
+ stdio: "inherit",
60
+ timeout,
61
+ });
62
+ }
63
+ // ─── Window Management ─────────────────────────────────────────────────────
64
+ export function isSMRunning() {
65
+ return !!ps("Get-Process StabilityMatrix -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Id");
66
+ }
67
+ export function launchSM(sm) {
68
+ if (isSMRunning()) {
69
+ console.log(`${c.dim} SM already running${c.reset}`);
70
+ return true;
71
+ }
72
+ const smExe = sm.platform === "wsl"
73
+ ? tryExec(`wslpath -w "${sm.path}/StabilityMatrix.exe"`)
74
+ : `${sm.path}\\StabilityMatrix.exe`;
75
+ if (!smExe)
76
+ return false;
77
+ console.log(`${c.dim} Launching Stability Matrix...${c.reset}`);
78
+ ps(`Start-Process '${smExe}'`);
79
+ // Wait for window
80
+ for (let i = 0; i < 20; i++) {
81
+ if (isSMRunning())
82
+ return true;
83
+ tryExec("sleep 1");
84
+ }
85
+ return isSMRunning();
86
+ }
87
+ export function focusSMWindow() {
88
+ return !!ps(`
89
+ Add-Type -AssemblyName UIAutomationClient
90
+ $root = [System.Windows.Automation.AutomationElement]::RootElement
91
+ $cond = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::NameProperty, 'Stability Matrix')
92
+ $win = $root.FindFirst([System.Windows.Automation.TreeScope]::Children, $cond)
93
+ if ($win) {
94
+ $pattern = $win.GetCurrentPattern([System.Windows.Automation.WindowPattern]::Pattern)
95
+ $pattern.SetWindowVisualState([System.Windows.Automation.WindowVisualState]::Normal)
96
+ Write-Output 'focused'
97
+ }
98
+ `);
99
+ }
100
+ // ─── UI Automation Helpers ─────────────────────────────────────────────────
101
+ /**
102
+ * Find a UI element by name/automationId and click it.
103
+ */
104
+ export function clickButton(name, timeout = 10000) {
105
+ const script = `
106
+ Add-Type -AssemblyName UIAutomationClient
107
+ Add-Type -AssemblyName UIAutomationTypes
108
+
109
+ $root = [System.Windows.Automation.AutomationElement]::RootElement
110
+
111
+ # Find SM window
112
+ $smCond = New-Object System.Windows.Automation.PropertyCondition(
113
+ [System.Windows.Automation.AutomationElement]::NameProperty, 'Stability Matrix')
114
+ $smWin = $root.FindFirst([System.Windows.Automation.TreeScope]::Children, $smCond)
115
+
116
+ if (-not $smWin) {
117
+ # Try partial match
118
+ $procs = Get-Process StabilityMatrix -ErrorAction SilentlyContinue
119
+ if ($procs) {
120
+ $smCond = New-Object System.Windows.Automation.PropertyCondition(
121
+ [System.Windows.Automation.AutomationElement]::ProcessIdProperty, $procs[0].Id)
122
+ $smWin = $root.FindFirst([System.Windows.Automation.TreeScope]::Children, $smCond)
123
+ }
124
+ }
125
+
126
+ if (-not $smWin) { Write-Output 'WINDOW_NOT_FOUND'; exit 1 }
127
+
128
+ # Find button by name
129
+ $btnCond = New-Object System.Windows.Automation.PropertyCondition(
130
+ [System.Windows.Automation.AutomationElement]::NameProperty, '${name}')
131
+ $btn = $smWin.FindFirst([System.Windows.Automation.TreeScope]::Descendants, $btnCond)
132
+
133
+ if (-not $btn) {
134
+ # Try AutomationId
135
+ $btnCond = New-Object System.Windows.Automation.PropertyCondition(
136
+ [System.Windows.Automation.AutomationElement]::AutomationIdProperty, '${name}')
137
+ $btn = $smWin.FindFirst([System.Windows.Automation.TreeScope]::Descendants, $btnCond)
138
+ }
139
+
140
+ if (-not $btn) { Write-Output 'BUTTON_NOT_FOUND'; exit 1 }
141
+
142
+ # Click it
143
+ $invokePattern = $btn.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)
144
+ $invokePattern.Invoke()
145
+ Write-Output 'CLICKED'
146
+ `;
147
+ const result = ps(script, timeout);
148
+ return result === "CLICKED";
149
+ }
150
+ /**
151
+ * Find a UI element by partial text match and click it.
152
+ */
153
+ export function clickByText(text) {
154
+ const script = `
155
+ Add-Type -AssemblyName UIAutomationClient
156
+ Add-Type -AssemblyName UIAutomationTypes
157
+
158
+ $root = [System.Windows.Automation.AutomationElement]::RootElement
159
+ $procs = Get-Process StabilityMatrix -ErrorAction SilentlyContinue
160
+ if (-not $procs) { Write-Output 'NOT_RUNNING'; exit 1 }
161
+
162
+ $smCond = New-Object System.Windows.Automation.PropertyCondition(
163
+ [System.Windows.Automation.AutomationElement]::ProcessIdProperty, $procs[0].Id)
164
+ $smWin = $root.FindFirst([System.Windows.Automation.TreeScope]::Children, $smCond)
165
+ if (-not $smWin) { Write-Output 'WINDOW_NOT_FOUND'; exit 1 }
166
+
167
+ # Search all descendants for text match
168
+ $all = $smWin.FindAll([System.Windows.Automation.TreeScope]::Descendants,
169
+ [System.Windows.Automation.Condition]::TrueCondition)
170
+
171
+ foreach ($el in $all) {
172
+ $elName = $el.Current.Name
173
+ if ($elName -like '*${text}*') {
174
+ try {
175
+ $invokePattern = $el.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)
176
+ $invokePattern.Invoke()
177
+ Write-Output "CLICKED: $elName"
178
+ exit 0
179
+ } catch {
180
+ # Try SelectionItemPattern for list items
181
+ try {
182
+ $selectPattern = $el.GetCurrentPattern([System.Windows.Automation.SelectionItemPattern]::Pattern)
183
+ $selectPattern.Select()
184
+ Write-Output "SELECTED: $elName"
185
+ exit 0
186
+ } catch {}
187
+ }
188
+ }
189
+ }
190
+ Write-Output 'TEXT_NOT_FOUND'
191
+ `;
192
+ const result = ps(script, 15000);
193
+ return !!result && (result.startsWith("CLICKED") || result.startsWith("SELECTED"));
194
+ }
195
+ /**
196
+ * List all clickable elements in SM window (for debugging).
197
+ */
198
+ export function listUIElements() {
199
+ const script = `
200
+ Add-Type -AssemblyName UIAutomationClient
201
+ $root = [System.Windows.Automation.AutomationElement]::RootElement
202
+ $procs = Get-Process StabilityMatrix -ErrorAction SilentlyContinue
203
+ if (-not $procs) { exit 1 }
204
+
205
+ $smCond = New-Object System.Windows.Automation.PropertyCondition(
206
+ [System.Windows.Automation.AutomationElement]::ProcessIdProperty, $procs[0].Id)
207
+ $smWin = $root.FindFirst([System.Windows.Automation.TreeScope]::Children, $smCond)
208
+ if (-not $smWin) { exit 1 }
209
+
210
+ $all = $smWin.FindAll([System.Windows.Automation.TreeScope]::Descendants,
211
+ [System.Windows.Automation.Condition]::TrueCondition)
212
+
213
+ foreach ($el in $all) {
214
+ $name = $el.Current.Name
215
+ $type = $el.Current.ControlType.ProgrammaticName
216
+ $id = $el.Current.AutomationId
217
+ if ($name -or $id) {
218
+ Write-Output "$type | $name | $id"
219
+ }
220
+ }
221
+ `;
222
+ const result = ps(script, 20000);
223
+ return result ? result.split("\n").filter(l => l.trim()) : [];
224
+ }
225
+ // ─── High-Level Actions ────────────────────────────────────────────────────
226
+ /**
227
+ * Full automated install flow:
228
+ * 1. Launch SM
229
+ * 2. Click "Add Package"
230
+ * 3. Select package (Forge, ComfyUI, etc.)
231
+ * 4. Click Install
232
+ * 5. Wait for completion
233
+ * 6. Click Launch
234
+ */
235
+ export async function automateInstallPackage(packageName = "Stable Diffusion WebUI Forge") {
236
+ const sm = findStabilityMatrix();
237
+ if (!sm)
238
+ return { success: false, message: "Stability Matrix not found" };
239
+ console.log(`${c.cyan}Step 1/${c.reset} Launching Stability Matrix...`);
240
+ if (!launchSM(sm)) {
241
+ return { success: false, message: "Could not launch Stability Matrix" };
242
+ }
243
+ // Wait for UI to be ready
244
+ await sleep(3000);
245
+ console.log(`${c.cyan}Step 2/${c.reset} Looking for Add Package button...`);
246
+ // SM uses Avalonia UI — try common button names
247
+ let clicked = clickButton("Add Package") || clickButton("AddPackageButton") || clickByText("Add");
248
+ if (!clicked) {
249
+ // List elements to help debug
250
+ console.log(`${c.dim} Could not find Add Package button. Available elements:${c.reset}`);
251
+ const elements = listUIElements();
252
+ const relevant = elements.filter(e => e.toLowerCase().includes("add") || e.toLowerCase().includes("install") ||
253
+ e.toLowerCase().includes("package") || e.toLowerCase().includes("button")).slice(0, 10);
254
+ for (const el of relevant) {
255
+ console.log(`${c.dim} ${el}${c.reset}`);
256
+ }
257
+ return { success: false, message: "Could not find Add Package button — SM UI may have changed" };
258
+ }
259
+ await sleep(2000);
260
+ console.log(`${c.cyan}Step 3/${c.reset} Selecting ${packageName}...`);
261
+ clicked = clickByText(packageName);
262
+ if (!clicked) {
263
+ console.log(`${c.yellow}⚠${c.reset} Could not find "${packageName}" in package list`);
264
+ return { success: false, message: `Package "${packageName}" not found in SM` };
265
+ }
266
+ await sleep(1000);
267
+ console.log(`${c.cyan}Step 4/${c.reset} Clicking Install...`);
268
+ clicked = clickButton("Install") || clickByText("Install");
269
+ if (!clicked) {
270
+ return { success: false, message: "Could not find Install button" };
271
+ }
272
+ console.log(`${c.cyan}Step 5/${c.reset} Installation started — monitoring progress...`);
273
+ console.log(`${c.dim} SM is downloading Python, models, and all dependencies.${c.reset}`);
274
+ console.log(`${c.dim} This may take 10-30 minutes.${c.reset}`);
275
+ // Monitor by checking if the install is still running
276
+ // SM shows a progress bar — we can check if the Launch button appears
277
+ for (let i = 0; i < 120; i++) { // Up to 60 minutes
278
+ await sleep(30000);
279
+ const elapsed = (i + 1) * 30;
280
+ console.log(`${c.dim} ${Math.floor(elapsed / 60)}m ${elapsed % 60}s elapsed...${c.reset}`);
281
+ // Check if Launch button appeared (install done)
282
+ if (clickButton("Launch") || clickByText("Launch")) {
283
+ console.log(`${c.green}✓${c.reset} Installation complete — package launched!`);
284
+ return { success: true, message: `${c.green}✓${c.reset} ${packageName} installed and launched via Stability Matrix` };
285
+ }
286
+ }
287
+ return { success: false, message: "Installation timed out after 60 minutes" };
288
+ }
289
+ /**
290
+ * Launch the active package.
291
+ */
292
+ export async function automateLaunch() {
293
+ const sm = findStabilityMatrix();
294
+ if (!sm)
295
+ return { success: false, message: "Stability Matrix not found" };
296
+ if (!launchSM(sm)) {
297
+ return { success: false, message: "Could not launch SM" };
298
+ }
299
+ await sleep(3000);
300
+ console.log(`${c.dim} Clicking Launch...${c.reset}`);
301
+ const clicked = clickButton("Launch") || clickByText("Launch");
302
+ if (!clicked) {
303
+ return { success: false, message: "Could not find Launch button — package may not be installed" };
304
+ }
305
+ return { success: true, message: `${c.green}✓${c.reset} Package launching...` };
306
+ }
307
+ // ─── Status & Diagnostics ──────────────────────────────────────────────────
308
+ /**
309
+ * Check SD API from the Windows side (bypasses WSL networking issues).
310
+ */
311
+ export function checkAPIFromWindows(port = 7860) {
312
+ const result = ps(`
313
+ try {
314
+ $r = Invoke-WebRequest -Uri "http://127.0.0.1:${port}/sdapi/v1/sd-models" -TimeoutSec 5
315
+ Write-Output "OK:$($r.StatusCode):$($r.Content.Substring(0, [Math]::Min(500, $r.Content.Length)))"
316
+ } catch {
317
+ $code = 0
318
+ if ($_.Exception.Response) { $code = [int]$_.Exception.Response.StatusCode }
319
+ Write-Output "FAIL:$code:$($_.Exception.Message)"
320
+ }
321
+ `, 10000);
322
+ if (!result)
323
+ return { running: false, statusCode: 0 };
324
+ const [status, code, body] = result.split(":", 3);
325
+ if (status === "OK") {
326
+ try {
327
+ const models = JSON.parse(body).map((m) => m.model_name);
328
+ return { running: true, statusCode: parseInt(code), models };
329
+ }
330
+ catch {
331
+ return { running: true, statusCode: parseInt(code) };
332
+ }
333
+ }
334
+ return { running: false, statusCode: parseInt(code) || 0 };
335
+ }
336
+ /**
337
+ * Check what port SD is listening on from Windows.
338
+ */
339
+ export function findSDPort() {
340
+ const result = ps(`
341
+ $ports = @(7860, 7861, 8188, 9090)
342
+ foreach ($p in $ports) {
343
+ $tcp = Get-NetTCPConnection -LocalPort $p -ErrorAction SilentlyContinue
344
+ if ($tcp) { Write-Output $p; exit }
345
+ }
346
+ Write-Output "NONE"
347
+ `, 10000);
348
+ if (!result || result === "NONE")
349
+ return null;
350
+ return parseInt(result);
351
+ }
352
+ /**
353
+ * Get Python process info from Windows.
354
+ */
355
+ export function getPythonProcesses() {
356
+ const result = ps(`
357
+ Get-Process python* -ErrorAction SilentlyContinue | ForEach-Object {
358
+ Write-Output "$($_.Id)|$($_.WorkingSet64)|$($_.Path)"
359
+ }
360
+ `, 10000);
361
+ if (!result)
362
+ return [];
363
+ return result.split("\n").filter(l => l.includes("|")).map(line => {
364
+ const [pid, ram, path] = line.split("|");
365
+ return { pid: parseInt(pid), ram: parseInt(ram), path: path?.trim() ?? "" };
366
+ });
367
+ }
368
+ /**
369
+ * Full diagnostic — check everything and report.
370
+ */
371
+ export async function diagnoseSD() {
372
+ const lines = [];
373
+ lines.push(`${c.bold}${c.cyan}SD Diagnostic Report${c.reset}\n`);
374
+ // 1. SM process
375
+ const smRunning = isSMRunning();
376
+ lines.push(`${smRunning ? c.green + "✓" : c.red + "✗"}${c.reset} Stability Matrix: ${smRunning ? "running" : "not running"}`);
377
+ // 2. Python processes
378
+ const pythons = getPythonProcesses();
379
+ const sdPython = pythons.filter(p => p.path.includes("reforge") || p.path.includes("StabilityMatrix") || p.ram > 1_000_000_000);
380
+ lines.push(`${sdPython.length > 0 ? c.green + "✓" : c.red + "✗"}${c.reset} Python processes: ${sdPython.length} SD-related`);
381
+ for (const p of sdPython) {
382
+ lines.push(` ${c.dim}PID ${p.pid} — ${(p.ram / 1073741824).toFixed(1)}GB RAM — ${p.path}${c.reset}`);
383
+ }
384
+ // 3. Port
385
+ const port = findSDPort();
386
+ lines.push(`${port ? c.green + "✓" : c.yellow + "⚠"}${c.reset} Port: ${port ? `${port} listening` : "no SD port detected"}`);
387
+ // 4. API
388
+ if (port) {
389
+ const api = checkAPIFromWindows(port);
390
+ lines.push(`${api.running ? c.green + "✓" : c.yellow + "⚠"}${c.reset} API: ${api.running ? `responding (HTTP ${api.statusCode})` : `not ready (HTTP ${api.statusCode}) — still loading`}`);
391
+ if (api.models) {
392
+ lines.push(` ${c.dim}Models: ${api.models.join(", ")}${c.reset}`);
393
+ }
394
+ }
395
+ else {
396
+ lines.push(`${c.red}✗${c.reset} API: no port listening — SD may have failed to start`);
397
+ }
398
+ // 5. WSL↔Windows connectivity
399
+ if (port && isWSL()) {
400
+ const winIP = tryExec("cat /etc/resolv.conf | grep nameserver | awk '{print $2}'");
401
+ const wslReach = tryExec(`curl -sf --max-time 2 http://${winIP}:${port}/ 2>/dev/null`);
402
+ const localReach = tryExec(`curl -sf --max-time 2 http://localhost:${port}/ 2>/dev/null`);
403
+ lines.push(`${localReach ? c.green + "✓" : c.yellow + "⚠"}${c.reset} WSL localhost:${port}: ${localReach ? "reachable" : "not reachable"}`);
404
+ lines.push(`${wslReach ? c.green + "✓" : c.yellow + "⚠"}${c.reset} WSL→Windows (${winIP}:${port}): ${wslReach ? "reachable" : "not reachable — need --listen flag"}`);
405
+ }
406
+ // 6. SM settings check
407
+ const sm = findStabilityMatrix();
408
+ if (sm) {
409
+ const { getActivePackage: getActive } = await import("../utils/stabilityMatrixManager.js");
410
+ const active = getActive(sm);
411
+ if (active) {
412
+ const hasApi = active.LaunchArgs.some(a => a.Name === "--api" && a.OptionValue === true);
413
+ const hasListen = active.LaunchArgs.some(a => a.Name === "--listen" && a.OptionValue === true);
414
+ lines.push(`${hasApi ? c.green + "✓" : c.red + "✗"}${c.reset} --api flag: ${hasApi ? "enabled" : "DISABLED — enable with: notoken sd config --api"}`);
415
+ lines.push(`${hasListen ? c.green + "✓" : c.yellow + "⚠"}${c.reset} --listen flag: ${hasListen ? "enabled" : "disabled — needed for WSL access"}`);
416
+ }
417
+ }
418
+ return lines.join("\n");
419
+ }
420
+ /**
421
+ * Stop SD — kill Python processes running the package.
422
+ */
423
+ export function stopSD() {
424
+ ps(`
425
+ Get-Process python* -ErrorAction SilentlyContinue | Where-Object {
426
+ $_.Path -like '*reforge*' -or $_.Path -like '*StabilityMatrix*'
427
+ } | Stop-Process -Force
428
+ `, 10000);
429
+ return `${c.green}✓${c.reset} SD processes stopped`;
430
+ }
431
+ /**
432
+ * Restart SD — stop then launch.
433
+ */
434
+ export async function restartSD() {
435
+ stopSD();
436
+ await sleep(2000);
437
+ const sm = findStabilityMatrix();
438
+ if (!sm)
439
+ return `${c.red}✗${c.reset} SM not found`;
440
+ if (!isSMRunning())
441
+ launchSM(sm);
442
+ await sleep(3000);
443
+ clickButton("LaunchButton");
444
+ return `${c.green}✓${c.reset} SD restarting — check status in a minute`;
445
+ }
446
+ function sleep(ms) {
447
+ return new Promise(resolve => setTimeout(resolve, ms));
448
+ }
@@ -13,7 +13,7 @@ import { getLastEntity, getRecentTurns } from "./store.js";
13
13
  */
14
14
  // Patterns that signal a reference to a previous turn
15
15
  const REPEAT_PATTERNS = [
16
- /^(do it|do that|run it|run that|same thing|again|repeat|redo|re-?run)\b/i,
16
+ /^(do it|do that|run it|run that|same thing|again|repeat|redo|re-?run|try again|retry|one more time|run again|do it again|try that again|go again|try it now|try now|try it|let.?s try|give it another|another try)\b/i,
17
17
  /^same\b/i,
18
18
  ];
19
19
  const PRONOUN_PATTERNS = [
@@ -24,6 +24,18 @@ const PRONOUN_PATTERNS = [
24
24
  { pattern: /\bthat (file|path|directory)\b/i, refType: "path" },
25
25
  { pattern: /\bthere\b/i, refType: "environment" },
26
26
  { pattern: /\bthat (env|environment|box|machine)\b/i, refType: "environment" },
27
+ // "the other" — refers to the second-most-recent entity (not the one we just acted on)
28
+ { pattern: /\bthe other (one|service|server|thing)\b/i, refType: "service", offset: 1 },
29
+ { pattern: /\bthe other (env|environment|box|machine|server)\b/i, refType: "environment", offset: 1 },
30
+ { pattern: /\bthe other\b/i, refType: "any", offset: 1 },
31
+ { pattern: /\bnot that one\b/i, refType: "any", offset: 1 },
32
+ { pattern: /\bnot this one\b/i, refType: "any", offset: 1 },
33
+ { pattern: /\bno not this one\b/i, refType: "any", offset: 1 },
34
+ { pattern: /\bno not that one\b/i, refType: "any", offset: 1 },
35
+ { pattern: /\bnot that\b/i, refType: "any", offset: 1 },
36
+ { pattern: /\bthe previous one\b/i, refType: "any", offset: 1 },
37
+ { pattern: /\bthe one before\b/i, refType: "any", offset: 1 },
38
+ { pattern: /\bthe first one\b/i, refType: "any", offset: 1 },
27
39
  ];
28
40
  const OVERRIDE_PATTERNS = [
29
41
  { pattern: /\bbut (?:on|in) (\w+)\b/i, field: "environment" },
@@ -42,6 +54,27 @@ export function resolveCoreferences(rawText, conv) {
42
54
  let resolvedIntent;
43
55
  const recentTurns = getRecentTurns(conv, 5);
44
56
  const lastUserTurn = recentTurns[recentTurns.length - 1];
57
+ // 0. Check for "the other thing" / "try the other thing" — second-to-last command
58
+ const otherThingPattern = /^(?:try |do |run )?the other (?:thing|one|command)\b/i;
59
+ if (otherThingPattern.test(rawText.trim())) {
60
+ isReference = true;
61
+ const prevTurn = recentTurns[recentTurns.length - 2];
62
+ if (prevTurn?.intent && prevTurn.fields) {
63
+ resolvedIntent = {
64
+ intent: prevTurn.intent,
65
+ confidence: 0.8,
66
+ rawText,
67
+ fields: { ...prevTurn.fields },
68
+ };
69
+ resolvedText = prevTurn.rawText;
70
+ resolutions.push({
71
+ original: rawText,
72
+ resolved: prevTurn.rawText,
73
+ source: "last_turn",
74
+ });
75
+ }
76
+ return { resolvedText, isReference, resolvedIntent, resolutions };
77
+ }
45
78
  // 1. Check for full repeat patterns ("do it again", "same thing")
46
79
  for (const pattern of REPEAT_PATTERNS) {
47
80
  if (pattern.test(rawText.trim())) {
@@ -73,13 +106,20 @@ export function resolveCoreferences(rawText, conv) {
73
106
  return { resolvedText, isReference, resolvedIntent, resolutions };
74
107
  }
75
108
  }
76
- // 2. Resolve pronouns ("restart it", "check that service")
77
- for (const { pattern, refType } of PRONOUN_PATTERNS) {
109
+ // 2. Resolve pronouns ("restart it", "check that service", "the other one")
110
+ for (const { pattern, refType, offset } of PRONOUN_PATTERNS) {
78
111
  const match = rawText.match(pattern);
79
112
  if (!match)
80
113
  continue;
81
114
  let resolved;
82
- if (refType === "any") {
115
+ if (offset && offset > 0) {
116
+ // "the other" — get the Nth entity (skip the most recent)
117
+ const candidates = refType === "any"
118
+ ? [...conv.knowledgeTree].sort((a, b) => b.lastMentioned - a.lastMentioned)
119
+ : conv.knowledgeTree.filter(n => n.type === refType).sort((a, b) => b.lastMentioned - a.lastMentioned);
120
+ resolved = candidates[offset]; // offset=1 means second-most-recent
121
+ }
122
+ else if (refType === "any") {
83
123
  // "it" → most recent service, then most recent entity
84
124
  resolved = getLastEntity(conv, "service")
85
125
  ?? getLastEntity(conv, "path")
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Pending Actions.
3
+ *
4
+ * Tracks suggestions NoToken makes so when the user says
5
+ * "ok", "try that", "do it", "run it", "yes" — it knows what to execute.
6
+ *
7
+ * Examples:
8
+ * NoToken: "✓ Installed. Start: notoken start stable-diffusion"
9
+ * User: "ok try it"
10
+ * → executes "notoken start stable-diffusion"
11
+ *
12
+ * NoToken: "Update available: 1.5.0 → 1.7.0"
13
+ * User: "yes do it"
14
+ * → executes update
15
+ */
16
+ export interface PendingAction {
17
+ /** What to execute — either an intent name or a raw command */
18
+ action: string;
19
+ /** Human description */
20
+ description: string;
21
+ /** Type: intent to parse, or command to run directly */
22
+ type: "intent" | "command";
23
+ /** When it was suggested (auto-set by suggestAction) */
24
+ timestamp?: number;
25
+ /** The raw text/fields for the intent */
26
+ fields?: Record<string, unknown>;
27
+ }
28
+ /**
29
+ * Store an action that NoToken is suggesting to the user.
30
+ * Most recent is at the end.
31
+ */
32
+ export declare function suggestAction(action: PendingAction): void;
33
+ /**
34
+ * Get the most recent pending action (if any, and if not too old).
35
+ * Actions expire after 5 minutes.
36
+ */
37
+ export declare function getLastPendingAction(): PendingAction | null;
38
+ /**
39
+ * Pop (consume) the last pending action.
40
+ */
41
+ export declare function consumePendingAction(): PendingAction | null;
42
+ /**
43
+ * Check if user is giving a directive about the pending action.
44
+ * E.g. "put it on F drive", "install it on D:", "no use /mnt/f"
45
+ * Returns the resolved new text if yes, null otherwise.
46
+ */
47
+ export declare function isRedirectingPendingAction(text: string): string | null;
48
+ /**
49
+ * Check if user input is an affirmation to execute a pending action.
50
+ */
51
+ export declare function isAffirmation(text: string): boolean;
52
+ /**
53
+ * Clear all pending actions.
54
+ */
55
+ export declare function clearPendingActions(): void;