@arcteninc/core 0.0.142 ā 0.0.143
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/dist/components/ArctenAgent.d.ts.map +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.mjs +2345 -2345
- package/dist/lib/useAgent.d.ts +1 -1
- package/dist/lib/useAgent.d.ts.map +1 -1
- package/package.json +7 -1
- package/scripts/cli-agent-wrapper.cjs +51 -0
- package/scripts/cli-agent.ts +483 -0
- package/scripts/cli-init-wizard.ts +10 -0
- package/scripts/cli-prompt-wrapper.cjs +51 -0
- package/scripts/cli-prompt.ts +306 -0
- package/scripts/cli-sync.ts +18 -2
- package/scripts/cli-tools-wrapper.cjs +51 -0
- package/scripts/cli-tools.ts +320 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* arcten tools - Interactive tool classification
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* arcten tools # Interactive AI chat for reclassification
|
|
7
|
+
* arcten tools --list # Show current safe/sensitive classification
|
|
8
|
+
* arcten tools --reset # Re-run pattern-based classification
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import * as readline from "readline";
|
|
14
|
+
|
|
15
|
+
// Classification patterns (fallback when no AI)
|
|
16
|
+
const safePatterns = /^(get|list|search|find|fetch|load|calculate|count|query|read)/i;
|
|
17
|
+
const sensitivePatterns = /^(create|update|delete|remove|set|add|modify|insert|patch|put|post|reset|regenerate|change|batch)/i;
|
|
18
|
+
const sensitiveReads = /^(get|fetch).*(api.?key|credential|secret|password|token|auth)/i;
|
|
19
|
+
|
|
20
|
+
interface SyncState {
|
|
21
|
+
lastSync: string;
|
|
22
|
+
toolsFile: string;
|
|
23
|
+
functions: Record<string, { classification: "safe" | "sensitive" }>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function findProjectRoot(): string {
|
|
27
|
+
let dir = process.cwd();
|
|
28
|
+
while (dir !== path.dirname(dir)) {
|
|
29
|
+
if (fs.existsSync(path.join(dir, "package.json"))) {
|
|
30
|
+
return dir;
|
|
31
|
+
}
|
|
32
|
+
dir = path.dirname(dir);
|
|
33
|
+
}
|
|
34
|
+
return process.cwd();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readSyncState(projectRoot: string): SyncState | null {
|
|
38
|
+
const syncStatePath = path.join(projectRoot, ".arcten", "sync-state.json");
|
|
39
|
+
if (!fs.existsSync(syncStatePath)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const content = fs.readFileSync(syncStatePath, "utf-8");
|
|
44
|
+
const parsed = JSON.parse(content);
|
|
45
|
+
if (!parsed.functions || typeof parsed.functions !== "object") {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return parsed;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function writeSyncState(projectRoot: string, state: SyncState): void {
|
|
55
|
+
const arctenDir = path.join(projectRoot, ".arcten");
|
|
56
|
+
if (!fs.existsSync(arctenDir)) {
|
|
57
|
+
fs.mkdirSync(arctenDir, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
const syncStatePath = path.join(arctenDir, "sync-state.json");
|
|
60
|
+
fs.writeFileSync(syncStatePath, JSON.stringify(state, null, 2));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function classifyByPattern(name: string): "safe" | "sensitive" {
|
|
64
|
+
// Sensitive reads take priority
|
|
65
|
+
if (sensitiveReads.test(name)) return "sensitive";
|
|
66
|
+
// Then check for sensitive action patterns
|
|
67
|
+
if (sensitivePatterns.test(name)) return "sensitive";
|
|
68
|
+
// Then check for safe read patterns
|
|
69
|
+
if (safePatterns.test(name)) return "safe";
|
|
70
|
+
// Default to sensitive (fail-safe)
|
|
71
|
+
return "sensitive";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function listClassifications(state: SyncState): void {
|
|
75
|
+
const safeTools: string[] = [];
|
|
76
|
+
const sensitiveTools: string[] = [];
|
|
77
|
+
|
|
78
|
+
for (const [name, data] of Object.entries(state.functions)) {
|
|
79
|
+
if (data.classification === "safe") {
|
|
80
|
+
safeTools.push(name);
|
|
81
|
+
} else {
|
|
82
|
+
sensitiveTools.push(name);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log("\nš Tool Classifications\n");
|
|
87
|
+
console.log(`Source: ${state.toolsFile}`);
|
|
88
|
+
console.log(`Last sync: ${state.lastSync}\n`);
|
|
89
|
+
|
|
90
|
+
console.log(`ā
Safe (${safeTools.length}) - auto-execute without approval:`);
|
|
91
|
+
if (safeTools.length === 0) {
|
|
92
|
+
console.log(" (none)");
|
|
93
|
+
} else {
|
|
94
|
+
safeTools.sort().forEach((name) => console.log(` ⢠${name}`));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(`\nā ļø Sensitive (${sensitiveTools.length}) - require user approval:`);
|
|
98
|
+
if (sensitiveTools.length === 0) {
|
|
99
|
+
console.log(" (none)");
|
|
100
|
+
} else {
|
|
101
|
+
sensitiveTools.sort().forEach((name) => console.log(` ⢠${name}`));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(`\nTotal: ${safeTools.length + sensitiveTools.length} tools`);
|
|
105
|
+
console.log("\nTo change classifications, run: arcten tools");
|
|
106
|
+
console.log("After changes, run: arcten sync");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function resetClassifications(state: SyncState): SyncState {
|
|
110
|
+
console.log("\nš Resetting classifications using pattern matching...\n");
|
|
111
|
+
|
|
112
|
+
const newFunctions: Record<string, { classification: "safe" | "sensitive" }> = {};
|
|
113
|
+
let safeCount = 0;
|
|
114
|
+
let sensitiveCount = 0;
|
|
115
|
+
|
|
116
|
+
for (const name of Object.keys(state.functions)) {
|
|
117
|
+
const classification = classifyByPattern(name);
|
|
118
|
+
newFunctions[name] = { classification };
|
|
119
|
+
if (classification === "safe") {
|
|
120
|
+
safeCount++;
|
|
121
|
+
} else {
|
|
122
|
+
sensitiveCount++;
|
|
123
|
+
}
|
|
124
|
+
console.log(` ${classification === "safe" ? "ā
" : "ā ļø"} ${name} ā ${classification}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log(`\nReset complete: ${safeCount} safe, ${sensitiveCount} sensitive`);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
...state,
|
|
131
|
+
functions: newFunctions,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function interactiveClassification(projectRoot: string, state: SyncState): Promise<void> {
|
|
136
|
+
const rl = readline.createInterface({
|
|
137
|
+
input: process.stdin,
|
|
138
|
+
output: process.stdout,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const ask = (question: string): Promise<string> => {
|
|
142
|
+
return new Promise((resolve) => {
|
|
143
|
+
rl.question(question, (answer) => {
|
|
144
|
+
resolve(answer.trim());
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
console.log("\nš§ Interactive Tool Classification\n");
|
|
150
|
+
console.log("Commands:");
|
|
151
|
+
console.log(" list Show all classifications");
|
|
152
|
+
console.log(" safe <tool> Mark tool as safe (auto-execute)");
|
|
153
|
+
console.log(" sensitive <tool> Mark tool as sensitive (requires approval)");
|
|
154
|
+
console.log(" safe <tool1> <tool2> Mark multiple tools as safe");
|
|
155
|
+
console.log(" find <pattern> Find tools matching pattern");
|
|
156
|
+
console.log(" reset Reset all to pattern-based classification");
|
|
157
|
+
console.log(" save Save changes and exit");
|
|
158
|
+
console.log(" quit Exit without saving");
|
|
159
|
+
console.log("");
|
|
160
|
+
|
|
161
|
+
let modified = false;
|
|
162
|
+
let currentState = { ...state };
|
|
163
|
+
|
|
164
|
+
while (true) {
|
|
165
|
+
const input = await ask("\n> ");
|
|
166
|
+
const parts = input.split(/\s+/);
|
|
167
|
+
const command = parts[0]?.toLowerCase();
|
|
168
|
+
|
|
169
|
+
if (!command) continue;
|
|
170
|
+
|
|
171
|
+
if (command === "quit" || command === "exit" || command === "q") {
|
|
172
|
+
if (modified) {
|
|
173
|
+
const confirm = await ask("You have unsaved changes. Quit anyway? (y/n): ");
|
|
174
|
+
if (confirm.toLowerCase() !== "y") continue;
|
|
175
|
+
}
|
|
176
|
+
console.log("Exiting without saving.");
|
|
177
|
+
rl.close();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (command === "save") {
|
|
182
|
+
if (!modified) {
|
|
183
|
+
console.log("No changes to save.");
|
|
184
|
+
} else {
|
|
185
|
+
writeSyncState(projectRoot, currentState);
|
|
186
|
+
console.log("ā
Changes saved to .arcten/sync-state.json");
|
|
187
|
+
console.log(" Run 'arcten sync' to regenerate arcten.tools.ts and sync to dashboard.");
|
|
188
|
+
}
|
|
189
|
+
rl.close();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (command === "list" || command === "ls") {
|
|
194
|
+
listClassifications(currentState);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (command === "reset") {
|
|
199
|
+
currentState = resetClassifications(currentState);
|
|
200
|
+
modified = true;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (command === "find" || command === "search") {
|
|
205
|
+
const pattern = parts.slice(1).join(" ").toLowerCase();
|
|
206
|
+
if (!pattern) {
|
|
207
|
+
console.log("Usage: find <pattern>");
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const matches = Object.entries(currentState.functions)
|
|
211
|
+
.filter(([name]) => name.toLowerCase().includes(pattern))
|
|
212
|
+
.map(([name, data]) => ` ${data.classification === "safe" ? "ā
" : "ā ļø"} ${name}`);
|
|
213
|
+
if (matches.length === 0) {
|
|
214
|
+
console.log(`No tools matching "${pattern}"`);
|
|
215
|
+
} else {
|
|
216
|
+
console.log(`Found ${matches.length} tools:`);
|
|
217
|
+
matches.forEach((m) => console.log(m));
|
|
218
|
+
}
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (command === "safe" || command === "sensitive") {
|
|
223
|
+
const toolNames = parts.slice(1);
|
|
224
|
+
if (toolNames.length === 0) {
|
|
225
|
+
console.log(`Usage: ${command} <tool1> [tool2] ...`);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const classification = command as "safe" | "sensitive";
|
|
230
|
+
let changed = 0;
|
|
231
|
+
|
|
232
|
+
for (const toolName of toolNames) {
|
|
233
|
+
// Find exact match or partial match
|
|
234
|
+
const exactMatch = Object.keys(currentState.functions).find(
|
|
235
|
+
(name) => name.toLowerCase() === toolName.toLowerCase()
|
|
236
|
+
);
|
|
237
|
+
const partialMatches = Object.keys(currentState.functions).filter(
|
|
238
|
+
(name) => name.toLowerCase().includes(toolName.toLowerCase())
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (exactMatch) {
|
|
242
|
+
if (currentState.functions[exactMatch].classification !== classification) {
|
|
243
|
+
currentState.functions[exactMatch].classification = classification;
|
|
244
|
+
console.log(` ${classification === "safe" ? "ā
" : "ā ļø"} ${exactMatch} ā ${classification}`);
|
|
245
|
+
changed++;
|
|
246
|
+
modified = true;
|
|
247
|
+
} else {
|
|
248
|
+
console.log(` āļø ${exactMatch} already ${classification}`);
|
|
249
|
+
}
|
|
250
|
+
} else if (partialMatches.length === 1) {
|
|
251
|
+
const match = partialMatches[0];
|
|
252
|
+
if (currentState.functions[match].classification !== classification) {
|
|
253
|
+
currentState.functions[match].classification = classification;
|
|
254
|
+
console.log(` ${classification === "safe" ? "ā
" : "ā ļø"} ${match} ā ${classification}`);
|
|
255
|
+
changed++;
|
|
256
|
+
modified = true;
|
|
257
|
+
} else {
|
|
258
|
+
console.log(` āļø ${match} already ${classification}`);
|
|
259
|
+
}
|
|
260
|
+
} else if (partialMatches.length > 1) {
|
|
261
|
+
console.log(` ā "${toolName}" matches multiple tools:`);
|
|
262
|
+
partialMatches.slice(0, 5).forEach((m) => console.log(` ⢠${m}`));
|
|
263
|
+
if (partialMatches.length > 5) {
|
|
264
|
+
console.log(` ... and ${partialMatches.length - 5} more`);
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
console.log(` ā Tool not found: ${toolName}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (changed > 0) {
|
|
272
|
+
console.log(`\nChanged ${changed} tool(s). Use 'save' to persist changes.`);
|
|
273
|
+
}
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log(`Unknown command: ${command}`);
|
|
278
|
+
console.log("Type 'list', 'safe <tool>', 'sensitive <tool>', 'save', or 'quit'");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function main(): Promise<void> {
|
|
283
|
+
const args = process.argv.slice(2);
|
|
284
|
+
const showList = args.includes("--list") || args.includes("-l");
|
|
285
|
+
const doReset = args.includes("--reset") || args.includes("-r");
|
|
286
|
+
|
|
287
|
+
const projectRoot = findProjectRoot();
|
|
288
|
+
const state = readSyncState(projectRoot);
|
|
289
|
+
|
|
290
|
+
if (!state) {
|
|
291
|
+
console.error("ā No sync state found. Run 'arcten init' or 'arcten sync' first.");
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (Object.keys(state.functions).length === 0) {
|
|
296
|
+
console.error("ā No tools found in sync state. Run 'arcten sync' to discover tools.");
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (showList) {
|
|
301
|
+
listClassifications(state);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (doReset) {
|
|
306
|
+
const newState = resetClassifications(state);
|
|
307
|
+
writeSyncState(projectRoot, newState);
|
|
308
|
+
console.log("\nā
Classifications saved to .arcten/sync-state.json");
|
|
309
|
+
console.log(" Run 'arcten sync' to regenerate arcten.tools.ts and sync to dashboard.");
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Interactive mode
|
|
314
|
+
await interactiveClassification(projectRoot, state);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
main().catch((error) => {
|
|
318
|
+
console.error("ā Error:", error.message);
|
|
319
|
+
process.exit(1);
|
|
320
|
+
});
|