notoken-core 1.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.
- package/config/file-hints.json +255 -0
- package/config/hosts.json +14 -0
- package/config/intents.json +3920 -0
- package/config/playbooks.json +112 -0
- package/config/rules.json +100 -0
- package/dist/agents/agentSpawner.d.ts +56 -0
- package/dist/agents/agentSpawner.js +180 -0
- package/dist/agents/planner.d.ts +40 -0
- package/dist/agents/planner.js +175 -0
- package/dist/agents/playbookRunner.d.ts +45 -0
- package/dist/agents/playbookRunner.js +120 -0
- package/dist/agents/taskRunner.d.ts +61 -0
- package/dist/agents/taskRunner.js +142 -0
- package/dist/context/history.d.ts +36 -0
- package/dist/context/history.js +115 -0
- package/dist/conversation/coreference.d.ts +27 -0
- package/dist/conversation/coreference.js +147 -0
- package/dist/conversation/secrets.d.ts +43 -0
- package/dist/conversation/secrets.js +129 -0
- package/dist/conversation/store.d.ts +94 -0
- package/dist/conversation/store.js +184 -0
- package/dist/execution/git.d.ts +11 -0
- package/dist/execution/git.js +146 -0
- package/dist/execution/ssh.d.ts +2 -0
- package/dist/execution/ssh.js +17 -0
- package/dist/handlers/executor.d.ts +8 -0
- package/dist/handlers/executor.js +216 -0
- package/dist/healing/claudeHealer.d.ts +17 -0
- package/dist/healing/claudeHealer.js +300 -0
- package/dist/healing/patchPromoter.d.ts +25 -0
- package/dist/healing/patchPromoter.js +118 -0
- package/dist/healing/ruleBuilder.d.ts +5 -0
- package/dist/healing/ruleBuilder.js +111 -0
- package/dist/healing/ruleRepairer.d.ts +8 -0
- package/dist/healing/ruleRepairer.js +29 -0
- package/dist/healing/ruleValidator.d.ts +22 -0
- package/dist/healing/ruleValidator.js +145 -0
- package/dist/healing/runHealer.d.ts +11 -0
- package/dist/healing/runHealer.js +74 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +62 -0
- package/dist/intents/catalog.d.ts +4 -0
- package/dist/intents/catalog.js +7 -0
- package/dist/nlp/disambiguate.d.ts +2 -0
- package/dist/nlp/disambiguate.js +46 -0
- package/dist/nlp/fuzzyResolver.d.ts +14 -0
- package/dist/nlp/fuzzyResolver.js +108 -0
- package/dist/nlp/llmFallback.d.ts +63 -0
- package/dist/nlp/llmFallback.js +338 -0
- package/dist/nlp/llmParser.d.ts +8 -0
- package/dist/nlp/llmParser.js +118 -0
- package/dist/nlp/multiClassifier.d.ts +39 -0
- package/dist/nlp/multiClassifier.js +181 -0
- package/dist/nlp/parseIntent.d.ts +2 -0
- package/dist/nlp/parseIntent.js +34 -0
- package/dist/nlp/ruleParser.d.ts +2 -0
- package/dist/nlp/ruleParser.js +234 -0
- package/dist/nlp/semantic.d.ts +104 -0
- package/dist/nlp/semantic.js +419 -0
- package/dist/nlp/uncertainty.d.ts +42 -0
- package/dist/nlp/uncertainty.js +103 -0
- package/dist/parsers/apacheParser.d.ts +50 -0
- package/dist/parsers/apacheParser.js +152 -0
- package/dist/parsers/bindParser.d.ts +40 -0
- package/dist/parsers/bindParser.js +189 -0
- package/dist/parsers/envFile.d.ts +39 -0
- package/dist/parsers/envFile.js +128 -0
- package/dist/parsers/fileFinder.d.ts +30 -0
- package/dist/parsers/fileFinder.js +226 -0
- package/dist/parsers/index.d.ts +27 -0
- package/dist/parsers/index.js +193 -0
- package/dist/parsers/jsonParser.d.ts +16 -0
- package/dist/parsers/jsonParser.js +57 -0
- package/dist/parsers/nginxParser.d.ts +47 -0
- package/dist/parsers/nginxParser.js +161 -0
- package/dist/parsers/passwd.d.ts +25 -0
- package/dist/parsers/passwd.js +41 -0
- package/dist/parsers/shadow.d.ts +23 -0
- package/dist/parsers/shadow.js +50 -0
- package/dist/parsers/yamlParser.d.ts +13 -0
- package/dist/parsers/yamlParser.js +54 -0
- package/dist/policy/confirm.d.ts +2 -0
- package/dist/policy/confirm.js +29 -0
- package/dist/policy/safety.d.ts +4 -0
- package/dist/policy/safety.js +32 -0
- package/dist/types/intent.d.ts +205 -0
- package/dist/types/intent.js +32 -0
- package/dist/types/rules.d.ts +237 -0
- package/dist/types/rules.js +50 -0
- package/dist/utils/analysis.d.ts +25 -0
- package/dist/utils/analysis.js +307 -0
- package/dist/utils/autoBackup.d.ts +43 -0
- package/dist/utils/autoBackup.js +144 -0
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.js +32 -0
- package/dist/utils/dirAnalysis.d.ts +23 -0
- package/dist/utils/dirAnalysis.js +192 -0
- package/dist/utils/explain.d.ts +8 -0
- package/dist/utils/explain.js +145 -0
- package/dist/utils/logger.d.ts +5 -0
- package/dist/utils/logger.js +29 -0
- package/dist/utils/output.d.ts +2 -0
- package/dist/utils/output.js +26 -0
- package/dist/utils/paths.d.ts +26 -0
- package/dist/utils/paths.js +47 -0
- package/dist/utils/permissions.d.ts +64 -0
- package/dist/utils/permissions.js +298 -0
- package/dist/utils/platform.d.ts +53 -0
- package/dist/utils/platform.js +253 -0
- package/dist/utils/smartFile.d.ts +29 -0
- package/dist/utils/smartFile.js +188 -0
- package/dist/utils/spinner.d.ts +53 -0
- package/dist/utils/spinner.js +140 -0
- package/dist/utils/verbose.d.ts +27 -0
- package/dist/utils/verbose.js +131 -0
- package/dist/utils/wslPaths.d.ts +31 -0
- package/dist/utils/wslPaths.js +145 -0
- package/package.json +39 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const SynonymEntry: z.ZodObject<{
|
|
3
|
+
intent: z.ZodString;
|
|
4
|
+
phrases: z.ZodArray<z.ZodString, "many">;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
intent: string;
|
|
7
|
+
phrases: string[];
|
|
8
|
+
}, {
|
|
9
|
+
intent: string;
|
|
10
|
+
phrases: string[];
|
|
11
|
+
}>;
|
|
12
|
+
export declare const AliasEntry: z.ZodObject<{
|
|
13
|
+
canonical: z.ZodString;
|
|
14
|
+
aliases: z.ZodArray<z.ZodString, "many">;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
canonical: string;
|
|
17
|
+
aliases: string[];
|
|
18
|
+
}, {
|
|
19
|
+
canonical: string;
|
|
20
|
+
aliases: string[];
|
|
21
|
+
}>;
|
|
22
|
+
export declare const RulesConfig: z.ZodObject<{
|
|
23
|
+
version: z.ZodString;
|
|
24
|
+
intentSynonyms: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
|
|
25
|
+
environmentAliases: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
|
|
26
|
+
serviceAliases: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
version: string;
|
|
29
|
+
intentSynonyms: Record<string, string[]>;
|
|
30
|
+
environmentAliases: Record<string, string[]>;
|
|
31
|
+
serviceAliases: Record<string, string[]>;
|
|
32
|
+
}, {
|
|
33
|
+
version: string;
|
|
34
|
+
intentSynonyms: Record<string, string[]>;
|
|
35
|
+
environmentAliases: Record<string, string[]>;
|
|
36
|
+
serviceAliases: Record<string, string[]>;
|
|
37
|
+
}>;
|
|
38
|
+
export type RulesConfig = z.infer<typeof RulesConfig>;
|
|
39
|
+
export declare const RulePatchChange: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
40
|
+
type: z.ZodLiteral<"add_intent_synonym">;
|
|
41
|
+
intent: z.ZodString;
|
|
42
|
+
phrase: z.ZodString;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
type: "add_intent_synonym";
|
|
45
|
+
intent: string;
|
|
46
|
+
phrase: string;
|
|
47
|
+
}, {
|
|
48
|
+
type: "add_intent_synonym";
|
|
49
|
+
intent: string;
|
|
50
|
+
phrase: string;
|
|
51
|
+
}>, z.ZodObject<{
|
|
52
|
+
type: z.ZodLiteral<"add_env_alias">;
|
|
53
|
+
canonical: z.ZodString;
|
|
54
|
+
alias: z.ZodString;
|
|
55
|
+
}, "strip", z.ZodTypeAny, {
|
|
56
|
+
type: "add_env_alias";
|
|
57
|
+
canonical: string;
|
|
58
|
+
alias: string;
|
|
59
|
+
}, {
|
|
60
|
+
type: "add_env_alias";
|
|
61
|
+
canonical: string;
|
|
62
|
+
alias: string;
|
|
63
|
+
}>, z.ZodObject<{
|
|
64
|
+
type: z.ZodLiteral<"add_service_alias">;
|
|
65
|
+
canonical: z.ZodString;
|
|
66
|
+
alias: z.ZodString;
|
|
67
|
+
}, "strip", z.ZodTypeAny, {
|
|
68
|
+
type: "add_service_alias";
|
|
69
|
+
canonical: string;
|
|
70
|
+
alias: string;
|
|
71
|
+
}, {
|
|
72
|
+
type: "add_service_alias";
|
|
73
|
+
canonical: string;
|
|
74
|
+
alias: string;
|
|
75
|
+
}>, z.ZodObject<{
|
|
76
|
+
type: z.ZodLiteral<"remove_intent_synonym">;
|
|
77
|
+
intent: z.ZodString;
|
|
78
|
+
phrase: z.ZodString;
|
|
79
|
+
}, "strip", z.ZodTypeAny, {
|
|
80
|
+
type: "remove_intent_synonym";
|
|
81
|
+
intent: string;
|
|
82
|
+
phrase: string;
|
|
83
|
+
}, {
|
|
84
|
+
type: "remove_intent_synonym";
|
|
85
|
+
intent: string;
|
|
86
|
+
phrase: string;
|
|
87
|
+
}>]>;
|
|
88
|
+
export type RulePatchChange = z.infer<typeof RulePatchChange>;
|
|
89
|
+
export declare const RulePatchTestCase: z.ZodObject<{
|
|
90
|
+
input: z.ZodString;
|
|
91
|
+
expectedIntent: z.ZodOptional<z.ZodString>;
|
|
92
|
+
expectedFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
93
|
+
shouldReject: z.ZodOptional<z.ZodBoolean>;
|
|
94
|
+
}, "strip", z.ZodTypeAny, {
|
|
95
|
+
input: string;
|
|
96
|
+
expectedIntent?: string | undefined;
|
|
97
|
+
expectedFields?: Record<string, unknown> | undefined;
|
|
98
|
+
shouldReject?: boolean | undefined;
|
|
99
|
+
}, {
|
|
100
|
+
input: string;
|
|
101
|
+
expectedIntent?: string | undefined;
|
|
102
|
+
expectedFields?: Record<string, unknown> | undefined;
|
|
103
|
+
shouldReject?: boolean | undefined;
|
|
104
|
+
}>;
|
|
105
|
+
export type RulePatchTestCase = z.infer<typeof RulePatchTestCase>;
|
|
106
|
+
export declare const RulePatch: z.ZodObject<{
|
|
107
|
+
summary: z.ZodString;
|
|
108
|
+
confidence: z.ZodNumber;
|
|
109
|
+
changes: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
110
|
+
type: z.ZodLiteral<"add_intent_synonym">;
|
|
111
|
+
intent: z.ZodString;
|
|
112
|
+
phrase: z.ZodString;
|
|
113
|
+
}, "strip", z.ZodTypeAny, {
|
|
114
|
+
type: "add_intent_synonym";
|
|
115
|
+
intent: string;
|
|
116
|
+
phrase: string;
|
|
117
|
+
}, {
|
|
118
|
+
type: "add_intent_synonym";
|
|
119
|
+
intent: string;
|
|
120
|
+
phrase: string;
|
|
121
|
+
}>, z.ZodObject<{
|
|
122
|
+
type: z.ZodLiteral<"add_env_alias">;
|
|
123
|
+
canonical: z.ZodString;
|
|
124
|
+
alias: z.ZodString;
|
|
125
|
+
}, "strip", z.ZodTypeAny, {
|
|
126
|
+
type: "add_env_alias";
|
|
127
|
+
canonical: string;
|
|
128
|
+
alias: string;
|
|
129
|
+
}, {
|
|
130
|
+
type: "add_env_alias";
|
|
131
|
+
canonical: string;
|
|
132
|
+
alias: string;
|
|
133
|
+
}>, z.ZodObject<{
|
|
134
|
+
type: z.ZodLiteral<"add_service_alias">;
|
|
135
|
+
canonical: z.ZodString;
|
|
136
|
+
alias: z.ZodString;
|
|
137
|
+
}, "strip", z.ZodTypeAny, {
|
|
138
|
+
type: "add_service_alias";
|
|
139
|
+
canonical: string;
|
|
140
|
+
alias: string;
|
|
141
|
+
}, {
|
|
142
|
+
type: "add_service_alias";
|
|
143
|
+
canonical: string;
|
|
144
|
+
alias: string;
|
|
145
|
+
}>, z.ZodObject<{
|
|
146
|
+
type: z.ZodLiteral<"remove_intent_synonym">;
|
|
147
|
+
intent: z.ZodString;
|
|
148
|
+
phrase: z.ZodString;
|
|
149
|
+
}, "strip", z.ZodTypeAny, {
|
|
150
|
+
type: "remove_intent_synonym";
|
|
151
|
+
intent: string;
|
|
152
|
+
phrase: string;
|
|
153
|
+
}, {
|
|
154
|
+
type: "remove_intent_synonym";
|
|
155
|
+
intent: string;
|
|
156
|
+
phrase: string;
|
|
157
|
+
}>]>, "many">;
|
|
158
|
+
tests: z.ZodArray<z.ZodObject<{
|
|
159
|
+
input: z.ZodString;
|
|
160
|
+
expectedIntent: z.ZodOptional<z.ZodString>;
|
|
161
|
+
expectedFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
162
|
+
shouldReject: z.ZodOptional<z.ZodBoolean>;
|
|
163
|
+
}, "strip", z.ZodTypeAny, {
|
|
164
|
+
input: string;
|
|
165
|
+
expectedIntent?: string | undefined;
|
|
166
|
+
expectedFields?: Record<string, unknown> | undefined;
|
|
167
|
+
shouldReject?: boolean | undefined;
|
|
168
|
+
}, {
|
|
169
|
+
input: string;
|
|
170
|
+
expectedIntent?: string | undefined;
|
|
171
|
+
expectedFields?: Record<string, unknown> | undefined;
|
|
172
|
+
shouldReject?: boolean | undefined;
|
|
173
|
+
}>, "many">;
|
|
174
|
+
warnings: z.ZodArray<z.ZodString, "many">;
|
|
175
|
+
}, "strip", z.ZodTypeAny, {
|
|
176
|
+
confidence: number;
|
|
177
|
+
summary: string;
|
|
178
|
+
changes: ({
|
|
179
|
+
type: "add_intent_synonym";
|
|
180
|
+
intent: string;
|
|
181
|
+
phrase: string;
|
|
182
|
+
} | {
|
|
183
|
+
type: "add_env_alias";
|
|
184
|
+
canonical: string;
|
|
185
|
+
alias: string;
|
|
186
|
+
} | {
|
|
187
|
+
type: "add_service_alias";
|
|
188
|
+
canonical: string;
|
|
189
|
+
alias: string;
|
|
190
|
+
} | {
|
|
191
|
+
type: "remove_intent_synonym";
|
|
192
|
+
intent: string;
|
|
193
|
+
phrase: string;
|
|
194
|
+
})[];
|
|
195
|
+
tests: {
|
|
196
|
+
input: string;
|
|
197
|
+
expectedIntent?: string | undefined;
|
|
198
|
+
expectedFields?: Record<string, unknown> | undefined;
|
|
199
|
+
shouldReject?: boolean | undefined;
|
|
200
|
+
}[];
|
|
201
|
+
warnings: string[];
|
|
202
|
+
}, {
|
|
203
|
+
confidence: number;
|
|
204
|
+
summary: string;
|
|
205
|
+
changes: ({
|
|
206
|
+
type: "add_intent_synonym";
|
|
207
|
+
intent: string;
|
|
208
|
+
phrase: string;
|
|
209
|
+
} | {
|
|
210
|
+
type: "add_env_alias";
|
|
211
|
+
canonical: string;
|
|
212
|
+
alias: string;
|
|
213
|
+
} | {
|
|
214
|
+
type: "add_service_alias";
|
|
215
|
+
canonical: string;
|
|
216
|
+
alias: string;
|
|
217
|
+
} | {
|
|
218
|
+
type: "remove_intent_synonym";
|
|
219
|
+
intent: string;
|
|
220
|
+
phrase: string;
|
|
221
|
+
})[];
|
|
222
|
+
tests: {
|
|
223
|
+
input: string;
|
|
224
|
+
expectedIntent?: string | undefined;
|
|
225
|
+
expectedFields?: Record<string, unknown> | undefined;
|
|
226
|
+
shouldReject?: boolean | undefined;
|
|
227
|
+
}[];
|
|
228
|
+
warnings: string[];
|
|
229
|
+
}>;
|
|
230
|
+
export type RulePatch = z.infer<typeof RulePatch>;
|
|
231
|
+
export interface FailureLog {
|
|
232
|
+
rawText: string;
|
|
233
|
+
timestamp: string;
|
|
234
|
+
parsedIntent: string | null;
|
|
235
|
+
confidence: number;
|
|
236
|
+
error?: string;
|
|
237
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const SynonymEntry = z.object({
|
|
3
|
+
intent: z.string(),
|
|
4
|
+
phrases: z.array(z.string()),
|
|
5
|
+
});
|
|
6
|
+
export const AliasEntry = z.object({
|
|
7
|
+
canonical: z.string(),
|
|
8
|
+
aliases: z.array(z.string()),
|
|
9
|
+
});
|
|
10
|
+
export const RulesConfig = z.object({
|
|
11
|
+
version: z.string(),
|
|
12
|
+
intentSynonyms: z.record(z.array(z.string())),
|
|
13
|
+
environmentAliases: z.record(z.array(z.string())),
|
|
14
|
+
serviceAliases: z.record(z.array(z.string())),
|
|
15
|
+
});
|
|
16
|
+
export const RulePatchChange = z.discriminatedUnion("type", [
|
|
17
|
+
z.object({
|
|
18
|
+
type: z.literal("add_intent_synonym"),
|
|
19
|
+
intent: z.string(),
|
|
20
|
+
phrase: z.string(),
|
|
21
|
+
}),
|
|
22
|
+
z.object({
|
|
23
|
+
type: z.literal("add_env_alias"),
|
|
24
|
+
canonical: z.string(),
|
|
25
|
+
alias: z.string(),
|
|
26
|
+
}),
|
|
27
|
+
z.object({
|
|
28
|
+
type: z.literal("add_service_alias"),
|
|
29
|
+
canonical: z.string(),
|
|
30
|
+
alias: z.string(),
|
|
31
|
+
}),
|
|
32
|
+
z.object({
|
|
33
|
+
type: z.literal("remove_intent_synonym"),
|
|
34
|
+
intent: z.string(),
|
|
35
|
+
phrase: z.string(),
|
|
36
|
+
}),
|
|
37
|
+
]);
|
|
38
|
+
export const RulePatchTestCase = z.object({
|
|
39
|
+
input: z.string(),
|
|
40
|
+
expectedIntent: z.string().optional(),
|
|
41
|
+
expectedFields: z.record(z.unknown()).optional(),
|
|
42
|
+
shouldReject: z.boolean().optional(),
|
|
43
|
+
});
|
|
44
|
+
export const RulePatch = z.object({
|
|
45
|
+
summary: z.string(),
|
|
46
|
+
confidence: z.number(),
|
|
47
|
+
changes: z.array(RulePatchChange),
|
|
48
|
+
tests: z.array(RulePatchTestCase),
|
|
49
|
+
warnings: z.array(z.string()),
|
|
50
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intelligent output analysis.
|
|
3
|
+
*
|
|
4
|
+
* Parses raw command output and adds human-readable commentary:
|
|
5
|
+
* - Load: checks vCPUs vs load average, flags overload
|
|
6
|
+
* - Disk: flags partitions above thresholds, checks specific paths
|
|
7
|
+
* - Memory: flags low memory, high swap usage
|
|
8
|
+
* - Directory: detects project types, file breakdowns
|
|
9
|
+
*/
|
|
10
|
+
export declare function analyzeLoad(output: string): string;
|
|
11
|
+
export interface DiskPartition {
|
|
12
|
+
filesystem: string;
|
|
13
|
+
size: string;
|
|
14
|
+
used: string;
|
|
15
|
+
available: string;
|
|
16
|
+
usePercent: number;
|
|
17
|
+
mountPoint: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function analyzeDisk(output: string, specificPath?: string): string;
|
|
20
|
+
export declare function analyzeMemory(output: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Analyze command output based on intent and add commentary.
|
|
23
|
+
* Returns empty string if no analysis applicable.
|
|
24
|
+
*/
|
|
25
|
+
export declare function analyzeOutput(intent: string, output: string, fields: Record<string, unknown>): string;
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intelligent output analysis.
|
|
3
|
+
*
|
|
4
|
+
* Parses raw command output and adds human-readable commentary:
|
|
5
|
+
* - Load: checks vCPUs vs load average, flags overload
|
|
6
|
+
* - Disk: flags partitions above thresholds, checks specific paths
|
|
7
|
+
* - Memory: flags low memory, high swap usage
|
|
8
|
+
* - Directory: detects project types, file breakdowns
|
|
9
|
+
*/
|
|
10
|
+
import { analyzeDirectory as analyzeDirectoryImpl } from "./dirAnalysis.js";
|
|
11
|
+
function analyzeDirectoryOutput(output) {
|
|
12
|
+
return analyzeDirectoryImpl(output);
|
|
13
|
+
}
|
|
14
|
+
const c = {
|
|
15
|
+
reset: "\x1b[0m",
|
|
16
|
+
bold: "\x1b[1m",
|
|
17
|
+
dim: "\x1b[2m",
|
|
18
|
+
green: "\x1b[32m",
|
|
19
|
+
yellow: "\x1b[33m",
|
|
20
|
+
red: "\x1b[31m",
|
|
21
|
+
cyan: "\x1b[36m",
|
|
22
|
+
};
|
|
23
|
+
// ─── Load Analysis ───────────────────────────────────────────────────────────
|
|
24
|
+
export function analyzeLoad(output) {
|
|
25
|
+
const lines = [];
|
|
26
|
+
// Extract load averages from uptime output
|
|
27
|
+
// "load average: 0.52, 0.58, 0.59"
|
|
28
|
+
const loadMatch = output.match(/load average:\s*([\d.]+),?\s*([\d.]+),?\s*([\d.]+)/);
|
|
29
|
+
if (!loadMatch)
|
|
30
|
+
return "";
|
|
31
|
+
const load1 = parseFloat(loadMatch[1]);
|
|
32
|
+
const load5 = parseFloat(loadMatch[2]);
|
|
33
|
+
const load15 = parseFloat(loadMatch[3]);
|
|
34
|
+
// Extract CPU count from nproc or lscpu output
|
|
35
|
+
let cpuCount = 1;
|
|
36
|
+
const nprocMatch = output.match(/^(\d+)$/m);
|
|
37
|
+
const lscpuMatch = output.match(/CPU\(s\):\s*(\d+)/);
|
|
38
|
+
if (nprocMatch)
|
|
39
|
+
cpuCount = parseInt(nprocMatch[1]);
|
|
40
|
+
else if (lscpuMatch)
|
|
41
|
+
cpuCount = parseInt(lscpuMatch[1]);
|
|
42
|
+
// Also try /proc/cpuinfo count
|
|
43
|
+
const procMatch = output.match(/processor\s*:\s*(\d+)/g);
|
|
44
|
+
if (procMatch && procMatch.length > cpuCount)
|
|
45
|
+
cpuCount = procMatch.length;
|
|
46
|
+
lines.push(`\n${c.bold}${c.cyan}── Analysis ──${c.reset}`);
|
|
47
|
+
lines.push(` vCPUs: ${c.bold}${cpuCount}${c.reset}`);
|
|
48
|
+
lines.push(` Load: ${c.bold}${load1}${c.reset} (1m) ${load5} (5m) ${load15} (15m)`);
|
|
49
|
+
// Load ratio: load / cpuCount
|
|
50
|
+
const ratio1 = load1 / cpuCount;
|
|
51
|
+
const ratio5 = load5 / cpuCount;
|
|
52
|
+
if (ratio1 > 2.0) {
|
|
53
|
+
lines.push(` ${c.red}⚠ CRITICAL: Load is ${ratio1.toFixed(1)}x your CPU capacity!${c.reset}`);
|
|
54
|
+
lines.push(` ${c.red} System is severely overloaded. Processes are queuing.${c.reset}`);
|
|
55
|
+
}
|
|
56
|
+
else if (ratio1 > 1.0) {
|
|
57
|
+
lines.push(` ${c.yellow}⚠ HIGH: Load exceeds CPU count (${ratio1.toFixed(1)}x capacity).${c.reset}`);
|
|
58
|
+
lines.push(` ${c.yellow} Some processes are waiting for CPU time.${c.reset}`);
|
|
59
|
+
}
|
|
60
|
+
else if (ratio1 > 0.7) {
|
|
61
|
+
lines.push(` ${c.yellow}⚠ MODERATE: ${(ratio1 * 100).toFixed(0)}% CPU utilization.${c.reset}`);
|
|
62
|
+
lines.push(` ${c.dim} Approaching capacity — monitor closely.${c.reset}`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
lines.push(` ${c.green}✓ OK: ${(ratio1 * 100).toFixed(0)}% CPU utilization. Healthy.${c.reset}`);
|
|
66
|
+
}
|
|
67
|
+
// Trend
|
|
68
|
+
if (load1 > load15 * 1.5) {
|
|
69
|
+
lines.push(` ${c.yellow}↑ Load is trending UP (${load15} → ${load1}).${c.reset}`);
|
|
70
|
+
}
|
|
71
|
+
else if (load1 < load15 * 0.5) {
|
|
72
|
+
lines.push(` ${c.green}↓ Load is trending DOWN (${load15} → ${load1}).${c.reset}`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
lines.push(` ${c.dim}→ Load is stable.${c.reset}`);
|
|
76
|
+
}
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
export function analyzeDisk(output, specificPath) {
|
|
80
|
+
const lines = [];
|
|
81
|
+
const partitions = parseDfOutput(output);
|
|
82
|
+
if (partitions.length === 0)
|
|
83
|
+
return "";
|
|
84
|
+
lines.push(`\n${c.bold}${c.cyan}── Analysis ──${c.reset}`);
|
|
85
|
+
// If asking about a specific path, find the matching partition
|
|
86
|
+
if (specificPath) {
|
|
87
|
+
const match = findPartitionForPath(partitions, specificPath);
|
|
88
|
+
if (match) {
|
|
89
|
+
lines.push(` Path ${c.bold}${specificPath}${c.reset} is on ${c.bold}${match.mountPoint}${c.reset}`);
|
|
90
|
+
lines.push(` ${formatPartitionHealth(match)}`);
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
lines.push(` ${c.yellow}Could not find partition for path: ${specificPath}${c.reset}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Overall analysis
|
|
98
|
+
let criticalCount = 0;
|
|
99
|
+
let warningCount = 0;
|
|
100
|
+
// Filter to real filesystems (skip snap, tmpfs, etc.)
|
|
101
|
+
const realPartitions = partitions.filter((p) => !p.filesystem.startsWith("snap") &&
|
|
102
|
+
!p.filesystem.startsWith("tmpfs") &&
|
|
103
|
+
!p.filesystem.startsWith("none") &&
|
|
104
|
+
!p.filesystem.startsWith("rootfs") &&
|
|
105
|
+
p.mountPoint !== "/snap" &&
|
|
106
|
+
!p.mountPoint.startsWith("/snap/"));
|
|
107
|
+
for (const p of realPartitions) {
|
|
108
|
+
if (p.usePercent >= 95) {
|
|
109
|
+
lines.push(` ${c.red}⚠ CRITICAL: ${p.mountPoint} is ${p.usePercent}% full (${p.available} free)${c.reset}`);
|
|
110
|
+
criticalCount++;
|
|
111
|
+
}
|
|
112
|
+
else if (p.usePercent >= 85) {
|
|
113
|
+
lines.push(` ${c.yellow}⚠ WARNING: ${p.mountPoint} is ${p.usePercent}% full (${p.available} free)${c.reset}`);
|
|
114
|
+
warningCount++;
|
|
115
|
+
}
|
|
116
|
+
else if (p.usePercent >= 70) {
|
|
117
|
+
lines.push(` ${c.dim} ${p.mountPoint}: ${p.usePercent}% used (${p.available} free)${c.reset}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (criticalCount === 0 && warningCount === 0) {
|
|
121
|
+
lines.push(` ${c.green}✓ All partitions healthy. No space issues.${c.reset}`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
if (criticalCount > 0) {
|
|
125
|
+
lines.push(` ${c.red} ${criticalCount} partition(s) critically full!${c.reset}`);
|
|
126
|
+
}
|
|
127
|
+
if (warningCount > 0) {
|
|
128
|
+
lines.push(` ${c.yellow} ${warningCount} partition(s) approaching full.${c.reset}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push(` ${c.dim} Tip: Run "disk analysis" playbook for detailed breakdown.${c.reset}`);
|
|
131
|
+
}
|
|
132
|
+
// Highlight largest partitions
|
|
133
|
+
const sorted = [...realPartitions].sort((a, b) => b.usePercent - a.usePercent);
|
|
134
|
+
if (sorted.length > 0 && !specificPath) {
|
|
135
|
+
lines.push(`\n ${c.bold}Top usage:${c.reset}`);
|
|
136
|
+
for (const p of sorted.slice(0, 5)) {
|
|
137
|
+
const bar = usageBar(p.usePercent);
|
|
138
|
+
lines.push(` ${bar} ${p.usePercent.toString().padStart(3)}% ${p.mountPoint} (${p.used}/${p.size})`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return lines.join("\n");
|
|
142
|
+
}
|
|
143
|
+
function parseDfOutput(output) {
|
|
144
|
+
const partitions = [];
|
|
145
|
+
const lines = output.split("\n");
|
|
146
|
+
for (const line of lines) {
|
|
147
|
+
// Match df -h output: Filesystem Size Used Avail Use% Mounted on
|
|
148
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)%\s+(.+)$/);
|
|
149
|
+
if (match) {
|
|
150
|
+
partitions.push({
|
|
151
|
+
filesystem: match[1],
|
|
152
|
+
size: match[2],
|
|
153
|
+
used: match[3],
|
|
154
|
+
available: match[4],
|
|
155
|
+
usePercent: parseInt(match[5]),
|
|
156
|
+
mountPoint: match[6].trim(),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return partitions;
|
|
161
|
+
}
|
|
162
|
+
function findPartitionForPath(partitions, path) {
|
|
163
|
+
// Find the longest matching mount point
|
|
164
|
+
const lower = path.toLowerCase();
|
|
165
|
+
// Map common names to paths
|
|
166
|
+
const aliases = {
|
|
167
|
+
"documents": ["/home", "/mnt/c/Users", "~/Documents"],
|
|
168
|
+
"downloads": ["/home", "/mnt/c/Users", "~/Downloads"],
|
|
169
|
+
"my documents": ["/home", "/mnt/c/Users"],
|
|
170
|
+
"home": ["/home"],
|
|
171
|
+
"root": ["/"],
|
|
172
|
+
"tmp": ["/tmp"],
|
|
173
|
+
"var": ["/var"],
|
|
174
|
+
"log": ["/var/log", "/var"],
|
|
175
|
+
"www": ["/var/www"],
|
|
176
|
+
"c drive": ["/mnt/c"],
|
|
177
|
+
"d drive": ["/mnt/d"],
|
|
178
|
+
"e drive": ["/mnt/e"],
|
|
179
|
+
"f drive": ["/mnt/f"],
|
|
180
|
+
};
|
|
181
|
+
// Check aliases first
|
|
182
|
+
for (const [alias, paths] of Object.entries(aliases)) {
|
|
183
|
+
if (lower.includes(alias)) {
|
|
184
|
+
for (const p of paths) {
|
|
185
|
+
const match = partitions
|
|
186
|
+
.filter((part) => p.startsWith(part.mountPoint) || part.mountPoint.startsWith(p))
|
|
187
|
+
.sort((a, b) => b.mountPoint.length - a.mountPoint.length)[0];
|
|
188
|
+
if (match)
|
|
189
|
+
return match;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Direct path match — find longest mount point that is a prefix
|
|
194
|
+
const sorted = [...partitions].sort((a, b) => b.mountPoint.length - a.mountPoint.length);
|
|
195
|
+
for (const p of sorted) {
|
|
196
|
+
if (path.startsWith(p.mountPoint))
|
|
197
|
+
return p;
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
function formatPartitionHealth(p) {
|
|
202
|
+
if (p.usePercent >= 95)
|
|
203
|
+
return `${c.red}⚠ CRITICAL: ${p.usePercent}% full! Only ${p.available} free on ${p.size} total.${c.reset}`;
|
|
204
|
+
if (p.usePercent >= 85)
|
|
205
|
+
return `${c.yellow}⚠ WARNING: ${p.usePercent}% full. ${p.available} free on ${p.size} total.${c.reset}`;
|
|
206
|
+
if (p.usePercent >= 70)
|
|
207
|
+
return `${c.dim}Moderate: ${p.usePercent}% full. ${p.available} free on ${p.size} total.${c.reset}`;
|
|
208
|
+
return `${c.green}✓ Healthy: ${p.usePercent}% used. ${p.available} free on ${p.size} total.${c.reset}`;
|
|
209
|
+
}
|
|
210
|
+
function usageBar(percent, width = 20) {
|
|
211
|
+
const filled = Math.round((percent / 100) * width);
|
|
212
|
+
const empty = width - filled;
|
|
213
|
+
const color = percent >= 95 ? c.red : percent >= 85 ? c.yellow : c.green;
|
|
214
|
+
return `${color}${"█".repeat(filled)}${"░".repeat(empty)}${c.reset}`;
|
|
215
|
+
}
|
|
216
|
+
// ─── Memory Analysis ─────────────────────────────────────────────────────────
|
|
217
|
+
export function analyzeMemory(output) {
|
|
218
|
+
const lines = [];
|
|
219
|
+
// Parse "free -h" output
|
|
220
|
+
// Mem: 31Gi 15Gi 9.5Gi 9.8Mi 6.3Gi 15Gi
|
|
221
|
+
const memMatch = output.match(/Mem:\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/);
|
|
222
|
+
const swapMatch = output.match(/Swap:\s+(\S+)\s+(\S+)\s+(\S+)/);
|
|
223
|
+
if (!memMatch)
|
|
224
|
+
return "";
|
|
225
|
+
const total = memMatch[1];
|
|
226
|
+
const used = memMatch[2];
|
|
227
|
+
const free = memMatch[3];
|
|
228
|
+
const available = memMatch[6];
|
|
229
|
+
lines.push(`\n${c.bold}${c.cyan}── Analysis ──${c.reset}`);
|
|
230
|
+
// Calculate percentage from raw values
|
|
231
|
+
const totalGB = parseSize(total);
|
|
232
|
+
const usedGB = parseSize(used);
|
|
233
|
+
const availGB = parseSize(available);
|
|
234
|
+
if (totalGB > 0) {
|
|
235
|
+
const usedPercent = Math.round((usedGB / totalGB) * 100);
|
|
236
|
+
const availPercent = Math.round((availGB / totalGB) * 100);
|
|
237
|
+
lines.push(` RAM: ${c.bold}${used}${c.reset} used of ${total} (${availPercent}% available)`);
|
|
238
|
+
lines.push(` ${usageBar(usedPercent)}`);
|
|
239
|
+
if (usedPercent >= 95) {
|
|
240
|
+
lines.push(` ${c.red}⚠ CRITICAL: Memory nearly exhausted! OOM killer may activate.${c.reset}`);
|
|
241
|
+
}
|
|
242
|
+
else if (usedPercent >= 85) {
|
|
243
|
+
lines.push(` ${c.yellow}⚠ HIGH: Memory pressure. Consider scaling up or killing processes.${c.reset}`);
|
|
244
|
+
}
|
|
245
|
+
else if (usedPercent >= 70) {
|
|
246
|
+
lines.push(` ${c.dim}Moderate memory usage. Normal for most workloads.${c.reset}`);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
lines.push(` ${c.green}✓ Memory healthy.${c.reset}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Swap analysis
|
|
253
|
+
if (swapMatch) {
|
|
254
|
+
const swapTotal = swapMatch[1];
|
|
255
|
+
const swapUsed = swapMatch[2];
|
|
256
|
+
const swapTotalGB = parseSize(swapTotal);
|
|
257
|
+
const swapUsedGB = parseSize(swapUsed);
|
|
258
|
+
if (swapTotalGB > 0) {
|
|
259
|
+
const swapPercent = Math.round((swapUsedGB / swapTotalGB) * 100);
|
|
260
|
+
if (swapPercent > 50) {
|
|
261
|
+
lines.push(` ${c.yellow}⚠ Swap: ${swapUsed}/${swapTotal} used (${swapPercent}%). Heavy swapping degrades performance.${c.reset}`);
|
|
262
|
+
}
|
|
263
|
+
else if (swapUsedGB > 0) {
|
|
264
|
+
lines.push(` ${c.dim}Swap: ${swapUsed}/${swapTotal} used. Some swapping is normal.${c.reset}`);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
lines.push(` ${c.green}✓ No swap usage.${c.reset}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return lines.join("\n");
|
|
272
|
+
}
|
|
273
|
+
function parseSize(str) {
|
|
274
|
+
const match = str.match(/([\d.]+)(\w+)/);
|
|
275
|
+
if (!match)
|
|
276
|
+
return 0;
|
|
277
|
+
const num = parseFloat(match[1]);
|
|
278
|
+
const unit = match[2].toLowerCase();
|
|
279
|
+
if (unit.startsWith("t"))
|
|
280
|
+
return num * 1024;
|
|
281
|
+
if (unit.startsWith("g"))
|
|
282
|
+
return num;
|
|
283
|
+
if (unit.startsWith("m"))
|
|
284
|
+
return num / 1024;
|
|
285
|
+
if (unit.startsWith("k"))
|
|
286
|
+
return num / (1024 * 1024);
|
|
287
|
+
return num;
|
|
288
|
+
}
|
|
289
|
+
// ─── Router ──────────────────────────────────────────────────────────────────
|
|
290
|
+
/**
|
|
291
|
+
* Analyze command output based on intent and add commentary.
|
|
292
|
+
* Returns empty string if no analysis applicable.
|
|
293
|
+
*/
|
|
294
|
+
export function analyzeOutput(intent, output, fields) {
|
|
295
|
+
switch (intent) {
|
|
296
|
+
case "server.check_disk":
|
|
297
|
+
return analyzeDisk(output, fields.target);
|
|
298
|
+
case "server.check_memory":
|
|
299
|
+
return analyzeMemory(output);
|
|
300
|
+
case "server.uptime":
|
|
301
|
+
return analyzeLoad(output);
|
|
302
|
+
case "files.list":
|
|
303
|
+
return analyzeDirectoryOutput(output);
|
|
304
|
+
default:
|
|
305
|
+
return "";
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-backup system.
|
|
3
|
+
*
|
|
4
|
+
* Before any file modification (copy, move, remove, env.set),
|
|
5
|
+
* creates a timestamped backup in ~/.notoken/backups/.
|
|
6
|
+
* Backups are kept for a configurable number of hours (default: 6).
|
|
7
|
+
*
|
|
8
|
+
* Also supports manual rollback.
|
|
9
|
+
*/
|
|
10
|
+
export interface BackupRecord {
|
|
11
|
+
id: string;
|
|
12
|
+
originalPath: string;
|
|
13
|
+
backupPath: string;
|
|
14
|
+
timestamp: string;
|
|
15
|
+
intent: string;
|
|
16
|
+
expiresAt: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create a backup of a file before modifying it.
|
|
20
|
+
* Returns the backup record, or null if the file doesn't exist.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createBackup(originalPath: string, intent: string, retentionHours?: number): BackupRecord | null;
|
|
23
|
+
/**
|
|
24
|
+
* Generate the remote backup command to run before modifying a file.
|
|
25
|
+
* This returns a shell command string that creates a backup on the remote server.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getRemoteBackupCommand(filePath: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Rollback a file from backup.
|
|
30
|
+
*/
|
|
31
|
+
export declare function rollback(id: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* List all current backups.
|
|
34
|
+
*/
|
|
35
|
+
export declare function listBackups(): BackupRecord[];
|
|
36
|
+
/**
|
|
37
|
+
* Clean up expired backups.
|
|
38
|
+
*/
|
|
39
|
+
export declare function cleanExpiredBackups(): number;
|
|
40
|
+
/**
|
|
41
|
+
* Format backup list for display.
|
|
42
|
+
*/
|
|
43
|
+
export declare function formatBackupList(records: BackupRecord[]): string;
|