notoken-core 1.6.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/ascii-art.json +12 -0
- package/config/chat-responses.json +1019 -0
- package/config/cheat-sheets.json +94 -0
- package/config/concept-clusters.json +31 -0
- package/config/daily-tips.json +105 -0
- package/config/entities.json +93 -0
- package/config/history-today.json +9762 -0
- package/config/image-prompts.json +20 -0
- package/config/intent-vectors.json +1 -0
- package/config/intents.json +5749 -85
- package/config/ollama-models.json +193 -0
- package/config/rules.json +32 -1
- package/config/startup-quotes.json +45 -0
- package/dist/automation/discordPatchright.d.ts +35 -0
- package/dist/automation/discordPatchright.js +437 -0
- package/dist/automation/discordSetup.d.ts +31 -0
- package/dist/automation/discordSetup.js +338 -0
- package/dist/automation/smAutomation.d.ts +82 -0
- package/dist/automation/smAutomation.js +448 -0
- package/dist/conversation/coreference.js +44 -4
- package/dist/conversation/pendingActions.d.ts +55 -0
- package/dist/conversation/pendingActions.js +127 -0
- package/dist/conversation/store.d.ts +72 -0
- package/dist/conversation/store.js +140 -1
- package/dist/conversation/topicTracker.d.ts +36 -0
- package/dist/conversation/topicTracker.js +141 -0
- package/dist/execution/ssh.d.ts +42 -1
- package/dist/execution/ssh.js +538 -3
- package/dist/handlers/executor.d.ts +2 -0
- package/dist/handlers/executor.js +4669 -31
- package/dist/index.d.ts +39 -5
- package/dist/index.js +56 -4
- package/dist/nlp/batchParser.d.ts +30 -0
- package/dist/nlp/batchParser.js +77 -0
- package/dist/nlp/conceptExpansion.d.ts +54 -0
- package/dist/nlp/conceptExpansion.js +136 -0
- package/dist/nlp/conceptRouter.d.ts +49 -0
- package/dist/nlp/conceptRouter.js +302 -0
- package/dist/nlp/confidenceCalibrator.d.ts +62 -0
- package/dist/nlp/confidenceCalibrator.js +116 -0
- package/dist/nlp/correctionLearner.d.ts +45 -0
- package/dist/nlp/correctionLearner.js +207 -0
- package/dist/nlp/entitySpellCorrect.d.ts +35 -0
- package/dist/nlp/entitySpellCorrect.js +141 -0
- package/dist/nlp/knowledgeGraph.d.ts +70 -0
- package/dist/nlp/knowledgeGraph.js +380 -0
- package/dist/nlp/llmFallback.d.ts +47 -0
- package/dist/nlp/llmFallback.js +175 -36
- package/dist/nlp/llmParser.d.ts +5 -1
- package/dist/nlp/llmParser.js +43 -24
- package/dist/nlp/multiClassifier.js +91 -6
- package/dist/nlp/multiIntent.d.ts +43 -0
- package/dist/nlp/multiIntent.js +154 -0
- package/dist/nlp/parseIntent.d.ts +6 -1
- package/dist/nlp/parseIntent.js +199 -6
- package/dist/nlp/ruleParser.js +348 -0
- package/dist/nlp/semanticSimilarity.d.ts +30 -0
- package/dist/nlp/semanticSimilarity.js +174 -0
- package/dist/nlp/vocabularyBuilder.d.ts +43 -0
- package/dist/nlp/vocabularyBuilder.js +224 -0
- package/dist/nlp/wikidata.d.ts +49 -0
- package/dist/nlp/wikidata.js +228 -0
- package/dist/policy/confirm.d.ts +10 -0
- package/dist/policy/confirm.js +39 -0
- package/dist/policy/safety.js +6 -4
- package/dist/types/intent.d.ts +8 -0
- package/dist/types/intent.js +1 -0
- package/dist/utils/achievements.d.ts +38 -0
- package/dist/utils/achievements.js +126 -0
- package/dist/utils/aliases.d.ts +5 -0
- package/dist/utils/aliases.js +39 -0
- package/dist/utils/analysis.js +71 -15
- package/dist/utils/bookmarks.d.ts +13 -0
- package/dist/utils/bookmarks.js +51 -0
- package/dist/utils/browser.d.ts +64 -0
- package/dist/utils/browser.js +364 -0
- package/dist/utils/commandHistory.d.ts +20 -0
- package/dist/utils/commandHistory.js +108 -0
- package/dist/utils/completer.d.ts +17 -0
- package/dist/utils/completer.js +79 -0
- package/dist/utils/config.js +32 -2
- package/dist/utils/dbQuery.d.ts +25 -0
- package/dist/utils/dbQuery.js +248 -0
- package/dist/utils/devTools.d.ts +35 -0
- package/dist/utils/devTools.js +95 -0
- package/dist/utils/discordDiag.d.ts +35 -0
- package/dist/utils/discordDiag.js +834 -0
- package/dist/utils/diskCleanup.d.ts +36 -0
- package/dist/utils/diskCleanup.js +775 -0
- package/dist/utils/entityResolver.d.ts +107 -0
- package/dist/utils/entityResolver.js +468 -0
- package/dist/utils/imageGen.d.ts +92 -0
- package/dist/utils/imageGen.js +2031 -0
- package/dist/utils/installTracker.d.ts +57 -0
- package/dist/utils/installTracker.js +160 -0
- package/dist/utils/multiExec.d.ts +21 -0
- package/dist/utils/multiExec.js +141 -0
- package/dist/utils/openclawDiag.d.ts +127 -0
- package/dist/utils/openclawDiag.js +1535 -0
- package/dist/utils/openclawLogParser.d.ts +65 -0
- package/dist/utils/openclawLogParser.js +168 -0
- package/dist/utils/output.js +4 -0
- package/dist/utils/platform.js +2 -1
- package/dist/utils/progressReporter.d.ts +50 -0
- package/dist/utils/progressReporter.js +58 -0
- package/dist/utils/projectDetect.d.ts +44 -0
- package/dist/utils/projectDetect.js +319 -0
- package/dist/utils/projectScanner.d.ts +44 -0
- package/dist/utils/projectScanner.js +312 -0
- package/dist/utils/shellCompat.d.ts +78 -0
- package/dist/utils/shellCompat.js +186 -0
- package/dist/utils/smartArchive.d.ts +16 -0
- package/dist/utils/smartArchive.js +172 -0
- package/dist/utils/smartRetry.d.ts +26 -0
- package/dist/utils/smartRetry.js +114 -0
- package/dist/utils/snippets.d.ts +13 -0
- package/dist/utils/snippets.js +53 -0
- package/dist/utils/stabilityMatrixManager.d.ts +80 -0
- package/dist/utils/stabilityMatrixManager.js +268 -0
- package/dist/utils/teachMode.d.ts +41 -0
- package/dist/utils/teachMode.js +100 -0
- package/dist/utils/timer.d.ts +22 -0
- package/dist/utils/timer.js +52 -0
- package/dist/utils/updater.d.ts +1 -0
- package/dist/utils/updater.js +1 -1
- package/dist/utils/userContext.d.ts +57 -0
- package/dist/utils/userContext.js +133 -0
- package/dist/utils/version.d.ts +20 -0
- package/dist/utils/version.js +212 -0
- package/package.json +6 -3
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell compatibility layer.
|
|
3
|
+
*
|
|
4
|
+
* Abstracts platform differences so commands work on:
|
|
5
|
+
* - Linux (bash)
|
|
6
|
+
* - macOS (zsh/bash)
|
|
7
|
+
* - Windows (cmd.exe / PowerShell)
|
|
8
|
+
* - WSL (bash, but with /mnt/ Windows drives)
|
|
9
|
+
*
|
|
10
|
+
* Use these helpers instead of raw execSync with bash-isms.
|
|
11
|
+
*/
|
|
12
|
+
declare const isWin: boolean;
|
|
13
|
+
declare const isWSL: boolean;
|
|
14
|
+
export { isWin, isWSL };
|
|
15
|
+
/**
|
|
16
|
+
* Check if a command exists on this platform.
|
|
17
|
+
*/
|
|
18
|
+
export declare function commandExists(cmd: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Run a command and get output. Returns null on failure.
|
|
21
|
+
* Handles stderr redirection cross-platform.
|
|
22
|
+
*/
|
|
23
|
+
export declare function tryExec(cmd: string, timeout?: number): string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Get the temp directory.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getTempDir(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Generate a timestamp string (replaces `$(date +%Y%m%d-%H%M%S)` in shell).
|
|
30
|
+
*/
|
|
31
|
+
export declare function timestamp(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Get file size in bytes. Returns 0 if file doesn't exist.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fileSize(path: string): number;
|
|
36
|
+
/**
|
|
37
|
+
* Count lines in a file without shell commands.
|
|
38
|
+
*/
|
|
39
|
+
export declare function lineCount(path: string): number;
|
|
40
|
+
/**
|
|
41
|
+
* Run a command with proper shell for this platform.
|
|
42
|
+
*/
|
|
43
|
+
export declare function shellExec(cmd: string, opts?: {
|
|
44
|
+
cwd?: string;
|
|
45
|
+
timeout?: number;
|
|
46
|
+
stdio?: "inherit" | "pipe";
|
|
47
|
+
}): string;
|
|
48
|
+
/**
|
|
49
|
+
* Build a command that works on both Unix and Windows.
|
|
50
|
+
* Handles: stderr redirection, path separators, etc.
|
|
51
|
+
*/
|
|
52
|
+
export declare function crossPlatformCmd(unixCmd: string, windowsCmd?: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Redirect stderr to null, cross-platform.
|
|
55
|
+
* Unix: 2>/dev/null
|
|
56
|
+
* Windows: 2>NUL
|
|
57
|
+
*/
|
|
58
|
+
export declare function silenceStderr(cmd: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Get the home directory path.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getHome(): string;
|
|
63
|
+
/**
|
|
64
|
+
* Resolve a path that works on this platform.
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolvePath(...parts: string[]): string;
|
|
67
|
+
/**
|
|
68
|
+
* Check if running as root/admin.
|
|
69
|
+
*/
|
|
70
|
+
export declare function isAdmin(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Get the package install command for this platform.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getSystemInstallCmd(pkg: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Platform info for display.
|
|
77
|
+
*/
|
|
78
|
+
export declare function getPlatformSummary(): string;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell compatibility layer.
|
|
3
|
+
*
|
|
4
|
+
* Abstracts platform differences so commands work on:
|
|
5
|
+
* - Linux (bash)
|
|
6
|
+
* - macOS (zsh/bash)
|
|
7
|
+
* - Windows (cmd.exe / PowerShell)
|
|
8
|
+
* - WSL (bash, but with /mnt/ Windows drives)
|
|
9
|
+
*
|
|
10
|
+
* Use these helpers instead of raw execSync with bash-isms.
|
|
11
|
+
*/
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
import { platform as osPlatform, tmpdir, homedir } from "node:os";
|
|
14
|
+
import { statSync, readFileSync } from "node:fs";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
const isWin = osPlatform() === "win32";
|
|
17
|
+
const isWSL = (() => {
|
|
18
|
+
try {
|
|
19
|
+
return !!execSync("grep -qi microsoft /proc/version && echo wsl", {
|
|
20
|
+
encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 2000,
|
|
21
|
+
}).trim();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
export { isWin, isWSL };
|
|
28
|
+
/**
|
|
29
|
+
* Check if a command exists on this platform.
|
|
30
|
+
*/
|
|
31
|
+
export function commandExists(cmd) {
|
|
32
|
+
try {
|
|
33
|
+
if (isWin) {
|
|
34
|
+
execSync(`where ${cmd}`, { stdio: "pipe", timeout: 5000 });
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
execSync(`command -v ${cmd}`, { stdio: "pipe", timeout: 5000 });
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Run a command and get output. Returns null on failure.
|
|
47
|
+
* Handles stderr redirection cross-platform.
|
|
48
|
+
*/
|
|
49
|
+
export function tryExec(cmd, timeout = 5000) {
|
|
50
|
+
try {
|
|
51
|
+
const result = execSync(cmd, {
|
|
52
|
+
encoding: "utf-8",
|
|
53
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
54
|
+
timeout,
|
|
55
|
+
...(isWin ? { shell: "cmd.exe" } : {}),
|
|
56
|
+
});
|
|
57
|
+
return result.trim() || null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get the temp directory.
|
|
65
|
+
*/
|
|
66
|
+
export function getTempDir() {
|
|
67
|
+
return tmpdir();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Generate a timestamp string (replaces `$(date +%Y%m%d-%H%M%S)` in shell).
|
|
71
|
+
*/
|
|
72
|
+
export function timestamp() {
|
|
73
|
+
const d = new Date();
|
|
74
|
+
return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}-${String(d.getHours()).padStart(2, "0")}${String(d.getMinutes()).padStart(2, "0")}${String(d.getSeconds()).padStart(2, "0")}`;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get file size in bytes. Returns 0 if file doesn't exist.
|
|
78
|
+
*/
|
|
79
|
+
export function fileSize(path) {
|
|
80
|
+
try {
|
|
81
|
+
return statSync(path).size;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Count lines in a file without shell commands.
|
|
89
|
+
*/
|
|
90
|
+
export function lineCount(path) {
|
|
91
|
+
try {
|
|
92
|
+
return readFileSync(path, "utf-8").split("\n").length;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Run a command with proper shell for this platform.
|
|
100
|
+
*/
|
|
101
|
+
export function shellExec(cmd, opts = {}) {
|
|
102
|
+
const execOpts = {
|
|
103
|
+
encoding: "utf-8",
|
|
104
|
+
timeout: opts.timeout ?? 30000,
|
|
105
|
+
stdio: opts.stdio === "pipe" ? ["pipe", "pipe", "pipe"] : "inherit",
|
|
106
|
+
};
|
|
107
|
+
if (opts.cwd)
|
|
108
|
+
execOpts.cwd = opts.cwd;
|
|
109
|
+
if (isWin)
|
|
110
|
+
execOpts.shell = "cmd.exe";
|
|
111
|
+
return execSync(cmd, execOpts);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build a command that works on both Unix and Windows.
|
|
115
|
+
* Handles: stderr redirection, path separators, etc.
|
|
116
|
+
*/
|
|
117
|
+
export function crossPlatformCmd(unixCmd, windowsCmd) {
|
|
118
|
+
if (isWin && windowsCmd)
|
|
119
|
+
return windowsCmd;
|
|
120
|
+
return unixCmd;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Redirect stderr to null, cross-platform.
|
|
124
|
+
* Unix: 2>/dev/null
|
|
125
|
+
* Windows: 2>NUL
|
|
126
|
+
*/
|
|
127
|
+
export function silenceStderr(cmd) {
|
|
128
|
+
if (isWin)
|
|
129
|
+
return `${cmd} 2>NUL`;
|
|
130
|
+
return `${cmd} 2>/dev/null`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the home directory path.
|
|
134
|
+
*/
|
|
135
|
+
export function getHome() {
|
|
136
|
+
return homedir();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Resolve a path that works on this platform.
|
|
140
|
+
*/
|
|
141
|
+
export function resolvePath(...parts) {
|
|
142
|
+
return resolve(...parts);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if running as root/admin.
|
|
146
|
+
*/
|
|
147
|
+
export function isAdmin() {
|
|
148
|
+
if (isWin) {
|
|
149
|
+
return !!tryExec("net session 2>NUL");
|
|
150
|
+
}
|
|
151
|
+
return process.getuid?.() === 0;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get the package install command for this platform.
|
|
155
|
+
*/
|
|
156
|
+
export function getSystemInstallCmd(pkg) {
|
|
157
|
+
if (isWin) {
|
|
158
|
+
if (commandExists("winget"))
|
|
159
|
+
return `winget install ${pkg} --accept-source-agreements --accept-package-agreements -h`;
|
|
160
|
+
if (commandExists("choco"))
|
|
161
|
+
return `choco install ${pkg} -y`;
|
|
162
|
+
return `echo "Please install ${pkg} manually"`;
|
|
163
|
+
}
|
|
164
|
+
if (commandExists("apt-get"))
|
|
165
|
+
return `sudo apt-get install -y ${pkg}`;
|
|
166
|
+
if (commandExists("dnf"))
|
|
167
|
+
return `sudo dnf install -y ${pkg}`;
|
|
168
|
+
if (commandExists("brew"))
|
|
169
|
+
return `brew install ${pkg}`;
|
|
170
|
+
if (commandExists("apk"))
|
|
171
|
+
return `apk add ${pkg}`;
|
|
172
|
+
return `echo "Please install ${pkg} manually"`;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Platform info for display.
|
|
176
|
+
*/
|
|
177
|
+
export function getPlatformSummary() {
|
|
178
|
+
const os = osPlatform();
|
|
179
|
+
if (isWSL)
|
|
180
|
+
return "WSL (Windows Subsystem for Linux)";
|
|
181
|
+
if (os === "win32")
|
|
182
|
+
return "Windows";
|
|
183
|
+
if (os === "darwin")
|
|
184
|
+
return "macOS";
|
|
185
|
+
return "Linux";
|
|
186
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart archive creation.
|
|
3
|
+
*
|
|
4
|
+
* Auto-excludes heavy/non-essential directories (node_modules, .git, etc.)
|
|
5
|
+
* unless the user explicitly requests them. Checks disk space before archiving.
|
|
6
|
+
*/
|
|
7
|
+
export interface ArchiveOptions {
|
|
8
|
+
source: string;
|
|
9
|
+
destination?: string;
|
|
10
|
+
includeAll?: boolean;
|
|
11
|
+
excludes?: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Smart archive: checks space, shows what will be excluded, asks before creating.
|
|
15
|
+
*/
|
|
16
|
+
export declare function smartArchive(options: ArchiveOptions): Promise<string>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart archive creation.
|
|
3
|
+
*
|
|
4
|
+
* Auto-excludes heavy/non-essential directories (node_modules, .git, etc.)
|
|
5
|
+
* unless the user explicitly requests them. Checks disk space before archiving.
|
|
6
|
+
*/
|
|
7
|
+
import { exec } from "node:child_process";
|
|
8
|
+
import { promisify } from "node:util";
|
|
9
|
+
import { existsSync, statSync } from "node:fs";
|
|
10
|
+
import { resolve, basename } from "node:path";
|
|
11
|
+
import { detectLocalPlatform } from "./platform.js";
|
|
12
|
+
import { askForConfirmation } from "../policy/confirm.js";
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
const c = {
|
|
15
|
+
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
|
16
|
+
green: "\x1b[32m", yellow: "\x1b[33m", red: "\x1b[31m", cyan: "\x1b[36m",
|
|
17
|
+
};
|
|
18
|
+
/** Directories that are safe to exclude by default (regenerable/cached). */
|
|
19
|
+
const DEFAULT_EXCLUDES = [
|
|
20
|
+
"node_modules",
|
|
21
|
+
".git",
|
|
22
|
+
".next",
|
|
23
|
+
".nuxt",
|
|
24
|
+
"dist",
|
|
25
|
+
"build",
|
|
26
|
+
"__pycache__",
|
|
27
|
+
".venv",
|
|
28
|
+
"venv",
|
|
29
|
+
".env.local",
|
|
30
|
+
".cache",
|
|
31
|
+
".turbo",
|
|
32
|
+
"vendor", // PHP composer
|
|
33
|
+
"target", // Rust cargo
|
|
34
|
+
".gradle",
|
|
35
|
+
".idea",
|
|
36
|
+
".vscode",
|
|
37
|
+
"*.pyc",
|
|
38
|
+
"*.o",
|
|
39
|
+
"*.class",
|
|
40
|
+
".DS_Store",
|
|
41
|
+
"Thumbs.db",
|
|
42
|
+
];
|
|
43
|
+
/** Estimate the size of a directory (excluding default excludes). */
|
|
44
|
+
async function estimateSize(source, excludes) {
|
|
45
|
+
const plat = detectLocalPlatform();
|
|
46
|
+
try {
|
|
47
|
+
// Total size
|
|
48
|
+
const { stdout: totalOut } = await execAsync(`du -sb "${source}" 2>/dev/null | cut -f1`, { timeout: 30_000 });
|
|
49
|
+
const totalBytes = parseInt(totalOut.trim()) || 0;
|
|
50
|
+
// Size of excluded dirs
|
|
51
|
+
let excludedBytes = 0;
|
|
52
|
+
for (const ex of excludes) {
|
|
53
|
+
if (ex.startsWith("*"))
|
|
54
|
+
continue; // skip glob patterns
|
|
55
|
+
const exPath = resolve(source, ex);
|
|
56
|
+
if (existsSync(exPath)) {
|
|
57
|
+
try {
|
|
58
|
+
const { stdout } = await execAsync(`du -sb "${exPath}" 2>/dev/null | cut -f1`, { timeout: 10_000 });
|
|
59
|
+
excludedBytes += parseInt(stdout.trim()) || 0;
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
totalGB: totalBytes / 1073741824,
|
|
66
|
+
excludedGB: excludedBytes / 1073741824,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { totalGB: 0, excludedGB: 0 };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Check available disk space at a path. */
|
|
74
|
+
async function getAvailableSpaceGB(path) {
|
|
75
|
+
try {
|
|
76
|
+
const { stdout } = await execAsync(`df -B1 "${path}" 2>/dev/null | tail -1 | awk '{print $4}'`, { timeout: 5000 });
|
|
77
|
+
return parseInt(stdout.trim()) / 1073741824;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return -1; // unknown
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Smart archive: checks space, shows what will be excluded, asks before creating.
|
|
85
|
+
*/
|
|
86
|
+
export async function smartArchive(options) {
|
|
87
|
+
const source = resolve(options.source);
|
|
88
|
+
const lines = [];
|
|
89
|
+
if (!existsSync(source)) {
|
|
90
|
+
return `${c.red}✗ Source not found: ${source}${c.reset}`;
|
|
91
|
+
}
|
|
92
|
+
// Determine destination
|
|
93
|
+
const destName = options.destination || `${basename(source)}.tar.gz`;
|
|
94
|
+
const dest = resolve(destName.endsWith(".tar.gz") || destName.endsWith(".tgz") ? destName : destName + ".tar.gz");
|
|
95
|
+
// Determine exclusions
|
|
96
|
+
const excludes = options.includeAll ? [] : DEFAULT_EXCLUDES;
|
|
97
|
+
const foundExcludes = [];
|
|
98
|
+
// Check which excludable dirs actually exist in source
|
|
99
|
+
if (!options.includeAll) {
|
|
100
|
+
for (const ex of DEFAULT_EXCLUDES) {
|
|
101
|
+
if (ex.startsWith("*"))
|
|
102
|
+
continue;
|
|
103
|
+
const exPath = resolve(source, ex);
|
|
104
|
+
if (existsSync(exPath)) {
|
|
105
|
+
try {
|
|
106
|
+
const stat = statSync(exPath);
|
|
107
|
+
if (stat.isDirectory()) {
|
|
108
|
+
const { stdout } = await execAsync(`du -sb "${exPath}" 2>/dev/null | cut -f1`, { timeout: 10_000 });
|
|
109
|
+
const sizeGB = parseInt(stdout.trim()) / 1073741824;
|
|
110
|
+
foundExcludes.push({ name: ex, sizeGB });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch { }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Estimate sizes
|
|
118
|
+
const sizes = await estimateSize(source, options.includeAll ? [] : DEFAULT_EXCLUDES.filter(e => !e.startsWith("*")));
|
|
119
|
+
const archiveEstimateGB = (sizes.totalGB - sizes.excludedGB) * 0.3; // rough compression ratio
|
|
120
|
+
const availableGB = await getAvailableSpaceGB(resolve(dest, ".."));
|
|
121
|
+
lines.push(`\n${c.bold}${c.cyan}── Smart Archive ──${c.reset}\n`);
|
|
122
|
+
lines.push(` Source: ${c.bold}${source}${c.reset}`);
|
|
123
|
+
lines.push(` Destination: ${c.bold}${dest}${c.reset}`);
|
|
124
|
+
lines.push(` Source size: ${c.bold}${sizes.totalGB.toFixed(2)} GB${c.reset}`);
|
|
125
|
+
if (foundExcludes.length > 0) {
|
|
126
|
+
const totalExcluded = foundExcludes.reduce((s, e) => s + e.sizeGB, 0);
|
|
127
|
+
lines.push(`\n ${c.bold}Auto-excluding (${totalExcluded.toFixed(2)} GB saved):${c.reset}`);
|
|
128
|
+
for (const ex of foundExcludes.sort((a, b) => b.sizeGB - a.sizeGB)) {
|
|
129
|
+
const sizeStr = ex.sizeGB >= 1 ? `${ex.sizeGB.toFixed(2)} GB` : `${(ex.sizeGB * 1024).toFixed(0)} MB`;
|
|
130
|
+
lines.push(` ${c.yellow}${sizeStr.padStart(10)}${c.reset} ${ex.name}/`);
|
|
131
|
+
}
|
|
132
|
+
lines.push(`\n ${c.dim}To include everything: add "include all" or "with node_modules"${c.reset}`);
|
|
133
|
+
}
|
|
134
|
+
lines.push(`\n Estimated archive: ${c.bold}~${archiveEstimateGB.toFixed(2)} GB${c.reset} ${c.dim}(compressed)${c.reset}`);
|
|
135
|
+
// Space check
|
|
136
|
+
if (availableGB >= 0) {
|
|
137
|
+
lines.push(` Available space: ${c.bold}${availableGB.toFixed(2)} GB${c.reset}`);
|
|
138
|
+
if (archiveEstimateGB > availableGB * 0.9) {
|
|
139
|
+
lines.push(`\n ${c.red}${c.bold}⚠ NOT ENOUGH SPACE!${c.reset} Archive (~${archiveEstimateGB.toFixed(2)} GB) may exceed available space (${availableGB.toFixed(2)} GB).`);
|
|
140
|
+
lines.push(` ${c.yellow}Free up space first: notoken "free up space"${c.reset}`);
|
|
141
|
+
return lines.join("\n");
|
|
142
|
+
}
|
|
143
|
+
else if (archiveEstimateGB > availableGB * 0.5) {
|
|
144
|
+
lines.push(` ${c.yellow}⚠ This will use ${Math.round((archiveEstimateGB / availableGB) * 100)}% of remaining space.${c.reset}`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
lines.push(` ${c.green}✓ Plenty of space.${c.reset}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
console.log(lines.join("\n"));
|
|
151
|
+
// Confirm
|
|
152
|
+
const ok = await askForConfirmation(`\nCreate archive?`);
|
|
153
|
+
if (!ok) {
|
|
154
|
+
return `${c.dim}Cancelled.${c.reset}`;
|
|
155
|
+
}
|
|
156
|
+
// Build tar command
|
|
157
|
+
const excludeFlags = excludes.map((e) => `--exclude='${e}'`).join(" ");
|
|
158
|
+
const tarCmd = `tar -czf "${dest}" ${excludeFlags} -C "${resolve(source, "..")}" "${basename(source)}"`;
|
|
159
|
+
console.log(`\n${c.dim}→ ${tarCmd}${c.reset}`);
|
|
160
|
+
try {
|
|
161
|
+
await execAsync(tarCmd, { timeout: 600_000 }); // 10 min timeout for large archives
|
|
162
|
+
// Show result
|
|
163
|
+
const stat = statSync(dest);
|
|
164
|
+
const resultGB = stat.size / 1073741824;
|
|
165
|
+
const sizeStr = resultGB >= 1 ? `${resultGB.toFixed(2)} GB` : `${(resultGB * 1024).toFixed(0)} MB`;
|
|
166
|
+
return `\n${c.green}${c.bold}✓ Archive created: ${dest}${c.reset}\n Size: ${c.bold}${sizeStr}${c.reset}`;
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
170
|
+
return `\n${c.red}✗ Archive failed: ${msg.split("\n")[0]}${c.reset}`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Retry — analyzes command failures and suggests fixes.
|
|
3
|
+
*
|
|
4
|
+
* When a command fails, this module inspects the error message,
|
|
5
|
+
* identifies common patterns, and suggests an actionable fix
|
|
6
|
+
* that can be registered as a pending action.
|
|
7
|
+
*/
|
|
8
|
+
export interface FailureAnalysis {
|
|
9
|
+
/** Whether this failure has an automated fix */
|
|
10
|
+
canFix: boolean;
|
|
11
|
+
/** Short human-friendly suggestion shown to the user */
|
|
12
|
+
suggestion: string;
|
|
13
|
+
/** The command/intent text to execute as the fix */
|
|
14
|
+
fixCommand: string;
|
|
15
|
+
/** Why this fix should help */
|
|
16
|
+
explanation: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Analyze a command failure and suggest a fix.
|
|
20
|
+
*
|
|
21
|
+
* @param intent - The intent name or raw text that failed
|
|
22
|
+
* @param error - The error (string or Error)
|
|
23
|
+
* @param fields - Parsed intent fields (service name, tool, path, etc.)
|
|
24
|
+
* @returns A FailureAnalysis if a known pattern matches, or null
|
|
25
|
+
*/
|
|
26
|
+
export declare function analyzeFailure(intent: string, error: string | Error, fields?: Record<string, unknown>): FailureAnalysis | null;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Retry — analyzes command failures and suggests fixes.
|
|
3
|
+
*
|
|
4
|
+
* When a command fails, this module inspects the error message,
|
|
5
|
+
* identifies common patterns, and suggests an actionable fix
|
|
6
|
+
* that can be registered as a pending action.
|
|
7
|
+
*/
|
|
8
|
+
const patterns = [
|
|
9
|
+
{
|
|
10
|
+
pattern: /command not found[:\s]*(\S+)|(\S+):\s*not found|'(\S+)' is not recognized/i,
|
|
11
|
+
build: (m) => {
|
|
12
|
+
const tool = (m[1] || m[2] || m[3]).replace(/['"]/g, "");
|
|
13
|
+
return {
|
|
14
|
+
canFix: true,
|
|
15
|
+
suggestion: `${tool} is not installed. Install it?`,
|
|
16
|
+
fixCommand: `install ${tool}`,
|
|
17
|
+
explanation: `The command "${tool}" was not found on this system. Installing it should fix the issue.`,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
pattern: /connection refused|ECONNREFUSED/i,
|
|
23
|
+
build: (_m, _intent, fields) => {
|
|
24
|
+
const service = fields.service || fields.tool || "the service";
|
|
25
|
+
return {
|
|
26
|
+
canFix: true,
|
|
27
|
+
suggestion: `Can't connect — ${service} may not be running. Start it?`,
|
|
28
|
+
fixCommand: `start ${service}`,
|
|
29
|
+
explanation: `Connection was refused, which usually means the target service is not running.`,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: /permission denied|EACCES|access denied/i,
|
|
35
|
+
build: (_m, intent) => ({
|
|
36
|
+
canFix: true,
|
|
37
|
+
suggestion: "Permission denied. Retry with elevated privileges?",
|
|
38
|
+
fixCommand: `sudo ${intent}`,
|
|
39
|
+
explanation: "The command failed due to insufficient permissions. Running with sudo may resolve it.",
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
pattern: /no such file or directory[:\s]*(.+)|ENOENT[:\s]*(.+)/i,
|
|
44
|
+
build: (m) => {
|
|
45
|
+
const raw = (m[1] || m[2] || "").trim().replace(/['"]/g, "");
|
|
46
|
+
const filename = raw.split("/").pop() || raw;
|
|
47
|
+
return {
|
|
48
|
+
canFix: true,
|
|
49
|
+
suggestion: `File not found: ${filename}. Search for it?`,
|
|
50
|
+
fixCommand: `find ${filename}`,
|
|
51
|
+
explanation: `The path "${raw}" does not exist. A search may locate the correct path.`,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
pattern: /address already in use|port.*already in use|EADDRINUSE/i,
|
|
57
|
+
build: () => ({
|
|
58
|
+
canFix: true,
|
|
59
|
+
suggestion: "Port already in use. Check what's using it?",
|
|
60
|
+
fixCommand: "check ports",
|
|
61
|
+
explanation: "Another process is occupying the required port.",
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
pattern: /no space left|disk full|ENOSPC/i,
|
|
66
|
+
build: () => ({
|
|
67
|
+
canFix: true,
|
|
68
|
+
suggestion: "Disk is full. Free up space?",
|
|
69
|
+
fixCommand: "free up space",
|
|
70
|
+
explanation: "The disk has no remaining space. Clearing caches or temp files may help.",
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
pattern: /timed?\s*out|ETIMEDOUT|ESOCKETTIMEDOUT/i,
|
|
75
|
+
build: (_m, intent) => ({
|
|
76
|
+
canFix: true,
|
|
77
|
+
suggestion: "Command timed out. Try again?",
|
|
78
|
+
fixCommand: intent,
|
|
79
|
+
explanation: "The operation exceeded its time limit. A retry may succeed if the issue was transient.",
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
pattern: /ECONNREFUSED/i,
|
|
84
|
+
build: (_m, _intent, fields) => {
|
|
85
|
+
const service = fields.service || fields.tool || "the service";
|
|
86
|
+
return {
|
|
87
|
+
canFix: true,
|
|
88
|
+
suggestion: `Can't connect to ${service}. Check if it's running?`,
|
|
89
|
+
fixCommand: `check status ${service}`,
|
|
90
|
+
explanation: "The connection was actively refused — the target service may be down.",
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
/**
|
|
96
|
+
* Analyze a command failure and suggest a fix.
|
|
97
|
+
*
|
|
98
|
+
* @param intent - The intent name or raw text that failed
|
|
99
|
+
* @param error - The error (string or Error)
|
|
100
|
+
* @param fields - Parsed intent fields (service name, tool, path, etc.)
|
|
101
|
+
* @returns A FailureAnalysis if a known pattern matches, or null
|
|
102
|
+
*/
|
|
103
|
+
export function analyzeFailure(intent, error, fields = {}) {
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
if (!message)
|
|
106
|
+
return null;
|
|
107
|
+
for (const { pattern, build } of patterns) {
|
|
108
|
+
const match = message.match(pattern);
|
|
109
|
+
if (match) {
|
|
110
|
+
return build(match, intent, fields);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippets — save, list, read, and run code snippets.
|
|
3
|
+
*
|
|
4
|
+
* Each snippet is a file under ~/.notoken/snippets/<name>.<ext>
|
|
5
|
+
*/
|
|
6
|
+
/** Save a code snippet. */
|
|
7
|
+
export declare function saveSnippet(name: string, code: string, language?: string): void;
|
|
8
|
+
/** List saved snippets. */
|
|
9
|
+
export declare function listSnippets(): string;
|
|
10
|
+
/** Read a snippet by name (matches any extension). */
|
|
11
|
+
export declare function getSnippet(name: string): string | undefined;
|
|
12
|
+
/** Run a snippet, auto-detecting interpreter from extension. */
|
|
13
|
+
export declare function runSnippet(name: string): string;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippets — save, list, read, and run code snippets.
|
|
3
|
+
*
|
|
4
|
+
* Each snippet is a file under ~/.notoken/snippets/<name>.<ext>
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync } from "node:fs";
|
|
7
|
+
import { resolve, extname, basename } from "node:path";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? ".";
|
|
10
|
+
const dir = resolve(home, ".notoken", "snippets");
|
|
11
|
+
function ensureDir() { mkdirSync(dir, { recursive: true }); }
|
|
12
|
+
function extFor(lang) {
|
|
13
|
+
if (!lang)
|
|
14
|
+
return ".sh";
|
|
15
|
+
const map = { bash: ".sh", sh: ".sh", node: ".js", javascript: ".js", python: ".py", ts: ".ts", typescript: ".ts" };
|
|
16
|
+
return map[lang.toLowerCase()] ?? `.${lang}`;
|
|
17
|
+
}
|
|
18
|
+
/** Save a code snippet. */
|
|
19
|
+
export function saveSnippet(name, code, language) {
|
|
20
|
+
ensureDir();
|
|
21
|
+
writeFileSync(resolve(dir, `${name}${extFor(language)}`), code, "utf-8");
|
|
22
|
+
}
|
|
23
|
+
/** List saved snippets. */
|
|
24
|
+
export function listSnippets() {
|
|
25
|
+
ensureDir();
|
|
26
|
+
const files = readdirSync(dir);
|
|
27
|
+
if (files.length === 0)
|
|
28
|
+
return "No snippets saved.";
|
|
29
|
+
return files.map((f) => ` ${basename(f, extname(f))} (${extname(f).slice(1)})`).join("\n");
|
|
30
|
+
}
|
|
31
|
+
/** Read a snippet by name (matches any extension). */
|
|
32
|
+
export function getSnippet(name) {
|
|
33
|
+
ensureDir();
|
|
34
|
+
const hit = readdirSync(dir).find((f) => basename(f, extname(f)) === name);
|
|
35
|
+
return hit ? readFileSync(resolve(dir, hit), "utf-8") : undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Run a snippet, auto-detecting interpreter from extension. */
|
|
38
|
+
export function runSnippet(name) {
|
|
39
|
+
ensureDir();
|
|
40
|
+
const hit = readdirSync(dir).find((f) => basename(f, extname(f)) === name);
|
|
41
|
+
if (!hit)
|
|
42
|
+
return `Snippet "${name}" not found.`;
|
|
43
|
+
const fp = resolve(dir, hit);
|
|
44
|
+
const ext = extname(hit);
|
|
45
|
+
const runners = { ".sh": "bash", ".js": "node", ".py": "python3", ".ts": "npx tsx" };
|
|
46
|
+
const runner = runners[ext] ?? "bash";
|
|
47
|
+
try {
|
|
48
|
+
return execSync(`${runner} "${fp}"`, { encoding: "utf-8", timeout: 30_000 }).trim();
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
return `Error: ${e.message?.split("\n")[0] ?? e}`;
|
|
52
|
+
}
|
|
53
|
+
}
|