azerclaw 1.0.0 → 1.0.2-beta.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/README.md +8 -0
- package/bin/azerclaw.ts +575 -18
- package/dist/bin/azerclaw.d.ts +17 -8
- package/dist/bin/azerclaw.d.ts.map +1 -1
- package/dist/bin/azerclaw.js +506 -17
- package/dist/bin/azerclaw.js.map +1 -1
- package/dist/src/agents/builtin.js +1 -1
- package/dist/src/agents/builtin.js.map +1 -1
- package/dist/src/channels/adapter.d.ts +10 -1
- package/dist/src/channels/adapter.d.ts.map +1 -1
- package/dist/src/channels/adapter.js +89 -5
- package/dist/src/channels/adapter.js.map +1 -1
- package/dist/src/channels/pairing.d.ts +38 -0
- package/dist/src/channels/pairing.d.ts.map +1 -0
- package/dist/src/channels/pairing.js +171 -0
- package/dist/src/channels/pairing.js.map +1 -0
- package/dist/src/channels/routing.d.ts +14 -0
- package/dist/src/channels/routing.d.ts.map +1 -0
- package/dist/src/channels/routing.js +45 -0
- package/dist/src/channels/routing.js.map +1 -0
- package/dist/src/channels/security.d.ts +18 -0
- package/dist/src/channels/security.d.ts.map +1 -0
- package/dist/src/channels/security.js +80 -0
- package/dist/src/channels/security.js.map +1 -0
- package/dist/src/channels/slack.js +1 -1
- package/dist/src/channels/slack.js.map +1 -1
- package/dist/src/cli/animations/fish.d.ts +10 -7
- package/dist/src/cli/animations/fish.d.ts.map +1 -1
- package/dist/src/cli/animations/fish.js +116 -122
- package/dist/src/cli/animations/fish.js.map +1 -1
- package/dist/src/cli/commands/channels.d.ts +17 -0
- package/dist/src/cli/commands/channels.d.ts.map +1 -0
- package/dist/src/cli/commands/channels.js +173 -0
- package/dist/src/cli/commands/channels.js.map +1 -0
- package/dist/src/cli/commands/chat.d.ts +16 -0
- package/dist/src/cli/commands/chat.d.ts.map +1 -1
- package/dist/src/cli/commands/chat.js +157 -35
- package/dist/src/cli/commands/chat.js.map +1 -1
- package/dist/src/cli/commands/config.d.ts +1 -0
- package/dist/src/cli/commands/config.d.ts.map +1 -1
- package/dist/src/cli/commands/config.js +37 -11
- package/dist/src/cli/commands/config.js.map +1 -1
- package/dist/src/cli/commands/doctor.d.ts.map +1 -1
- package/dist/src/cli/commands/doctor.js +46 -2
- package/dist/src/cli/commands/doctor.js.map +1 -1
- package/dist/src/cli/commands/onboard.d.ts +12 -1
- package/dist/src/cli/commands/onboard.d.ts.map +1 -1
- package/dist/src/cli/commands/onboard.js +384 -64
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/pairing.d.ts +7 -0
- package/dist/src/cli/commands/pairing.d.ts.map +1 -0
- package/dist/src/cli/commands/pairing.js +64 -0
- package/dist/src/cli/commands/pairing.js.map +1 -0
- package/dist/src/cli/commands/run.d.ts.map +1 -1
- package/dist/src/cli/commands/run.js +3 -2
- package/dist/src/cli/commands/run.js.map +1 -1
- package/dist/src/cli/commands/sandbox.d.ts +7 -0
- package/dist/src/cli/commands/sandbox.d.ts.map +1 -0
- package/dist/src/cli/commands/sandbox.js +98 -0
- package/dist/src/cli/commands/sandbox.js.map +1 -0
- package/dist/src/cli/commands/settings.d.ts +31 -0
- package/dist/src/cli/commands/settings.d.ts.map +1 -0
- package/dist/src/cli/commands/settings.js +566 -0
- package/dist/src/cli/commands/settings.js.map +1 -0
- package/dist/src/cli/commands/tui.d.ts +3 -0
- package/dist/src/cli/commands/tui.d.ts.map +1 -1
- package/dist/src/cli/commands/tui.js +212 -71
- package/dist/src/cli/commands/tui.js.map +1 -1
- package/dist/src/config/manager.d.ts +121 -9
- package/dist/src/config/manager.d.ts.map +1 -1
- package/dist/src/config/manager.js +363 -11
- package/dist/src/config/manager.js.map +1 -1
- package/dist/src/config/schema.d.ts +457 -9
- package/dist/src/config/schema.d.ts.map +1 -1
- package/dist/src/config/schema.js +117 -8
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/core/runtime.d.ts +9 -0
- package/dist/src/core/runtime.d.ts.map +1 -1
- package/dist/src/core/runtime.js +78 -24
- package/dist/src/core/runtime.js.map +1 -1
- package/dist/src/core/sandbox.d.ts +34 -0
- package/dist/src/core/sandbox.d.ts.map +1 -0
- package/dist/src/core/sandbox.js +127 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/server.d.ts +15 -0
- package/dist/src/core/server.d.ts.map +1 -0
- package/dist/src/core/server.js +127 -0
- package/dist/src/core/server.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/providers/router.d.ts.map +1 -1
- package/dist/src/providers/router.js +16 -0
- package/dist/src/providers/router.js.map +1 -1
- package/dist/src/tools/advanced.d.ts.map +1 -1
- package/dist/src/tools/advanced.js +3 -0
- package/dist/src/tools/advanced.js.map +1 -1
- package/dist/src/tools/filesystem.d.ts.map +1 -1
- package/dist/src/tools/filesystem.js +4 -0
- package/dist/src/tools/filesystem.js.map +1 -1
- package/dist/src/tools/index.d.ts +9 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +73 -0
- package/dist/src/tools/index.js.map +1 -0
- package/dist/src/tools/loader.d.ts +14 -0
- package/dist/src/tools/loader.d.ts.map +1 -0
- package/dist/src/tools/loader.js +115 -0
- package/dist/src/tools/loader.js.map +1 -0
- package/dist/src/tools/registry.d.ts +29 -1
- package/dist/src/tools/registry.d.ts.map +1 -1
- package/dist/src/tools/registry.js +84 -6
- package/dist/src/tools/registry.js.map +1 -1
- package/dist/src/tools/shell.d.ts.map +1 -1
- package/dist/src/tools/shell.js +1 -0
- package/dist/src/tools/shell.js.map +1 -1
- package/package.json +9 -10
package/bin/azerclaw.ts
CHANGED
|
@@ -7,39 +7,76 @@
|
|
|
7
7
|
* Inspired by OpenClaw, themed with a fish 🐟 instead of a lobster.
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
|
-
* azerclaw
|
|
11
|
-
* azerclaw chat
|
|
12
|
-
* azerclaw run "task"
|
|
13
|
-
* azerclaw tui
|
|
14
|
-
* azerclaw onboard
|
|
15
|
-
* azerclaw config
|
|
16
|
-
* azerclaw
|
|
17
|
-
* azerclaw
|
|
10
|
+
* azerclaw — Launch interactive session (or onboard if first run)
|
|
11
|
+
* azerclaw chat — Interactive chat
|
|
12
|
+
* azerclaw run "task" — Execute a task
|
|
13
|
+
* azerclaw tui — Premium terminal UI
|
|
14
|
+
* azerclaw onboard — Setup wizard
|
|
15
|
+
* azerclaw config — Manage configuration
|
|
16
|
+
* azerclaw config provider — Switch provider
|
|
17
|
+
* azerclaw config model — Switch model
|
|
18
|
+
* azerclaw config apikey — Set API key
|
|
19
|
+
* azerclaw config fallback — Configure fallback
|
|
20
|
+
* azerclaw config channels — DM policy + routing controls
|
|
21
|
+
* azerclaw config sandbox — Session sandbox controls
|
|
22
|
+
* azerclaw pairing — Manage DM pairing approvals
|
|
23
|
+
* azerclaw init — Initialize project (AZERCLAW.md)
|
|
24
|
+
* azerclaw models — Manage AI models
|
|
25
|
+
* azerclaw doctor — Health check
|
|
26
|
+
* azerclaw status — Show current status
|
|
18
27
|
*/
|
|
19
28
|
|
|
20
29
|
const { Command } = require('commander');
|
|
21
|
-
const
|
|
30
|
+
const chalk = require('chalk');
|
|
31
|
+
const { playSplashScreen, printQuickSplash, fishError, fishInfo, fishSuccess } = require('../src/cli/animations/fish');
|
|
22
32
|
const { getConfigManager } = require('../src/config/manager');
|
|
23
33
|
|
|
24
|
-
const VERSION = '1.
|
|
34
|
+
const VERSION = '1.1.4-beta';
|
|
25
35
|
const program = new Command();
|
|
26
36
|
|
|
27
37
|
// ─── Program Setup ──────────────────────────────────────────────
|
|
28
38
|
|
|
29
39
|
program
|
|
30
40
|
.name('azerclaw')
|
|
31
|
-
.description('🐟 AZERCLAW —
|
|
41
|
+
.description('🐟 AZERCLAW — Diabolical AI · Scorched Earth · Your Way')
|
|
32
42
|
.version(VERSION, '-v, --version', 'Display version')
|
|
33
43
|
.option('--no-splash', 'Skip the splash screen')
|
|
34
|
-
.option('--no-color', 'Disable colors')
|
|
44
|
+
.option('--no-color', 'Disable colors')
|
|
45
|
+
.hook('preAction', async () => {
|
|
46
|
+
// Global initialization
|
|
47
|
+
const { registerAllTools } = require('../src/tools');
|
|
48
|
+
await registerAllTools();
|
|
49
|
+
});
|
|
35
50
|
|
|
36
51
|
// ─── Default Action (no command) ────────────────────────────────
|
|
37
52
|
|
|
38
53
|
program
|
|
39
|
-
.
|
|
54
|
+
.argument('[task]', 'Optional task to execute immediately (one-off mode)')
|
|
55
|
+
.action(async (task: string | undefined, opts: any) => {
|
|
40
56
|
const config = getConfigManager();
|
|
41
57
|
config.resolveEnvOverrides();
|
|
42
58
|
|
|
59
|
+
// Check for positional task
|
|
60
|
+
if (task) {
|
|
61
|
+
const { runTask } = require('../src/cli/commands/run');
|
|
62
|
+
await runTask(task.trim(), opts);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check for piped input (stdin)
|
|
67
|
+
if (!process.stdin.isTTY) {
|
|
68
|
+
let input = '';
|
|
69
|
+
process.stdin.setEncoding('utf-8');
|
|
70
|
+
for await (const chunk of process.stdin) {
|
|
71
|
+
input += chunk;
|
|
72
|
+
}
|
|
73
|
+
if (input.trim()) {
|
|
74
|
+
const { runTask } = require('../src/cli/commands/run');
|
|
75
|
+
await runTask(input.trim(), opts);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
43
80
|
if (config.isFirstRun()) {
|
|
44
81
|
// First run: show full splash + onboard
|
|
45
82
|
await playSplashScreen(VERSION);
|
|
@@ -59,16 +96,30 @@ program
|
|
|
59
96
|
.description('Start an interactive chat session')
|
|
60
97
|
.option('-m, --model <model>', 'Override the default model')
|
|
61
98
|
.option('-p, --provider <provider>', 'Override the default provider')
|
|
99
|
+
.option('-f, --file <path>', 'Include a file in the conversation context')
|
|
62
100
|
.action(async (opts: any) => {
|
|
63
101
|
const config = getConfigManager();
|
|
64
102
|
config.resolveEnvOverrides();
|
|
65
103
|
|
|
104
|
+
// Apply CLI flag overrides
|
|
105
|
+
if (opts.model || opts.provider) {
|
|
106
|
+
config.applyRuntimeOverrides(opts);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (opts.file) {
|
|
110
|
+
const fs = require('fs');
|
|
111
|
+
if (fs.existsSync(opts.file)) {
|
|
112
|
+
const content = fs.readFileSync(opts.file, 'utf-8');
|
|
113
|
+
opts.initialMessage = `I've attached the file: ${opts.file}\n\n\`\`\`\n${content}\n\`\`\``;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
66
117
|
if (!opts.parent?.splash === false) {
|
|
67
118
|
printQuickSplash(VERSION);
|
|
68
119
|
}
|
|
69
120
|
|
|
70
121
|
if (config.isFirstRun()) {
|
|
71
|
-
fishInfo('First time?
|
|
122
|
+
fishInfo('First time? Running setup wizard...');
|
|
72
123
|
const { runOnboard } = require('../src/cli/commands/onboard');
|
|
73
124
|
await runOnboard();
|
|
74
125
|
return;
|
|
@@ -81,17 +132,61 @@ program
|
|
|
81
132
|
// ─── Run Command ────────────────────────────────────────────────
|
|
82
133
|
|
|
83
134
|
program
|
|
84
|
-
.command('run
|
|
135
|
+
.command('run [task]')
|
|
85
136
|
.description('Execute a single task')
|
|
86
137
|
.option('-m, --model <model>', 'Override the default model')
|
|
138
|
+
.option('-p, --provider <provider>', 'Override the default provider')
|
|
139
|
+
.option('-f, --file <path>', 'Include a file in the task context')
|
|
87
140
|
.option('-V, --verbose', 'Show tool calls in detail')
|
|
88
|
-
.action(async (task: string, opts: any) => {
|
|
141
|
+
.action(async (task: string | undefined, opts: any) => {
|
|
89
142
|
const config = getConfigManager();
|
|
90
143
|
config.resolveEnvOverrides();
|
|
144
|
+
|
|
145
|
+
if (opts.model || opts.provider) {
|
|
146
|
+
config.applyRuntimeOverrides(opts);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let finalTask = task || '';
|
|
150
|
+
|
|
151
|
+
// Handle piped input if task is missing
|
|
152
|
+
if (!finalTask && !process.stdin.isTTY) {
|
|
153
|
+
process.stdin.setEncoding('utf-8');
|
|
154
|
+
for await (const chunk of process.stdin) {
|
|
155
|
+
finalTask += chunk;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!finalTask.trim()) {
|
|
160
|
+
fishError('No task provided. Usage: azerclaw run "your task" or echo "task" | azerclaw run');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (opts.file) {
|
|
165
|
+
const fs = require('fs');
|
|
166
|
+
if (fs.existsSync(opts.file)) {
|
|
167
|
+
const content = fs.readFileSync(opts.file, 'utf-8');
|
|
168
|
+
finalTask = `Context from file ${opts.file}:\n\`\`\`\n${content}\n\`\`\`\n\nTask: ${finalTask}`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
91
172
|
printQuickSplash(VERSION);
|
|
92
173
|
|
|
93
174
|
const { runTask } = require('../src/cli/commands/run');
|
|
94
|
-
await runTask(
|
|
175
|
+
await runTask(finalTask.trim(), opts);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// ─── Serve Command ──────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
program
|
|
181
|
+
.command('serve')
|
|
182
|
+
.description('Start the AZERCLAW local WebSocket daemon for desktop apps')
|
|
183
|
+
.option('-p, --port <port>', 'Port to listen on', '8080')
|
|
184
|
+
.action((opts: any) => {
|
|
185
|
+
printQuickSplash(VERSION);
|
|
186
|
+
const { AzerclawServer } = require('../src/core/server');
|
|
187
|
+
const port = parseInt(opts.port, 10) || 8080;
|
|
188
|
+
const server = new AzerclawServer(port);
|
|
189
|
+
server.start();
|
|
95
190
|
});
|
|
96
191
|
|
|
97
192
|
// ─── TUI Command ────────────────────────────────────────────────
|
|
@@ -99,10 +194,16 @@ program
|
|
|
99
194
|
program
|
|
100
195
|
.command('tui')
|
|
101
196
|
.description('Launch the premium terminal UI')
|
|
102
|
-
.
|
|
197
|
+
.option('-m, --model <model>', 'Override the default model')
|
|
198
|
+
.option('-p, --provider <provider>', 'Override the default provider')
|
|
199
|
+
.action(async (opts: any) => {
|
|
103
200
|
const config = getConfigManager();
|
|
104
201
|
config.resolveEnvOverrides();
|
|
105
202
|
|
|
203
|
+
if (opts.model || opts.provider) {
|
|
204
|
+
config.applyRuntimeOverrides(opts);
|
|
205
|
+
}
|
|
206
|
+
|
|
106
207
|
const { runTUI } = require('../src/cli/commands/tui');
|
|
107
208
|
await runTUI();
|
|
108
209
|
});
|
|
@@ -118,6 +219,28 @@ program
|
|
|
118
219
|
await runOnboard();
|
|
119
220
|
});
|
|
120
221
|
|
|
222
|
+
// ─── Init Command (Project) ────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
program
|
|
225
|
+
.command('init')
|
|
226
|
+
.description('Initialize AZERCLAW for this project (creates AZERCLAW.md + .azerclaw/)')
|
|
227
|
+
.action(() => {
|
|
228
|
+
const { initProject } = require('../src/cli/commands/settings');
|
|
229
|
+
initProject();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// ─── Status Command ─────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
program
|
|
235
|
+
.command('status')
|
|
236
|
+
.description('Show current model, provider, auth, and project status')
|
|
237
|
+
.action(() => {
|
|
238
|
+
const config = getConfigManager();
|
|
239
|
+
config.resolveEnvOverrides();
|
|
240
|
+
const { showStatus } = require('../src/cli/commands/settings');
|
|
241
|
+
showStatus();
|
|
242
|
+
});
|
|
243
|
+
|
|
121
244
|
// ─── Config Command ─────────────────────────────────────────────
|
|
122
245
|
|
|
123
246
|
const configCmd = program
|
|
@@ -156,6 +279,222 @@ configCmd
|
|
|
156
279
|
configReset();
|
|
157
280
|
});
|
|
158
281
|
|
|
282
|
+
configCmd
|
|
283
|
+
.command('provider [name]')
|
|
284
|
+
.description('Switch the active AI provider (interactive if no name given)')
|
|
285
|
+
.action(async (name?: string) => {
|
|
286
|
+
if (name) {
|
|
287
|
+
const { cliSwitchProvider } = require('../src/cli/commands/settings');
|
|
288
|
+
cliSwitchProvider(name);
|
|
289
|
+
} else {
|
|
290
|
+
const { interactiveProviderSwitch } = require('../src/cli/commands/settings');
|
|
291
|
+
await interactiveProviderSwitch();
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
configCmd
|
|
296
|
+
.command('model [id]')
|
|
297
|
+
.description('Switch the default model (interactive if no id given)')
|
|
298
|
+
.option('-p, --provider <provider>', 'Target provider (defaults to active)')
|
|
299
|
+
.action(async (id?: string, opts?: any) => {
|
|
300
|
+
if (id) {
|
|
301
|
+
const { cliSwitchModel } = require('../src/cli/commands/settings');
|
|
302
|
+
cliSwitchModel(id, opts?.provider);
|
|
303
|
+
} else {
|
|
304
|
+
const { interactiveModelSwitch } = require('../src/cli/commands/settings');
|
|
305
|
+
await interactiveModelSwitch();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
configCmd
|
|
310
|
+
.command('apikey [provider] [key]')
|
|
311
|
+
.description('Set or change an API key (interactive if no args given)')
|
|
312
|
+
.action(async (provider?: string, key?: string) => {
|
|
313
|
+
if (provider && key) {
|
|
314
|
+
const { cliSetApiKey } = require('../src/cli/commands/settings');
|
|
315
|
+
cliSetApiKey(provider, key);
|
|
316
|
+
} else {
|
|
317
|
+
const { interactiveApiKeyChange } = require('../src/cli/commands/settings');
|
|
318
|
+
await interactiveApiKeyChange();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
configCmd
|
|
323
|
+
.command('fallback [provider]')
|
|
324
|
+
.description('Set or change the fallback provider (interactive if no arg given)')
|
|
325
|
+
.action(async (provider?: string) => {
|
|
326
|
+
if (provider) {
|
|
327
|
+
const { cliSetFallback } = require('../src/cli/commands/settings');
|
|
328
|
+
cliSetFallback(provider);
|
|
329
|
+
} else {
|
|
330
|
+
const { interactiveFallbackConfig } = require('../src/cli/commands/settings');
|
|
331
|
+
await interactiveFallbackConfig();
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
configCmd
|
|
336
|
+
.command('settings')
|
|
337
|
+
.description('Open the full interactive settings menu')
|
|
338
|
+
.action(async () => {
|
|
339
|
+
const { interactiveSettingsMenu } = require('../src/cli/commands/settings');
|
|
340
|
+
await interactiveSettingsMenu();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const configChannelsCmd = configCmd
|
|
344
|
+
.command('channels')
|
|
345
|
+
.description('Manage channel DM policy, allowlists, and session routing');
|
|
346
|
+
|
|
347
|
+
configChannelsCmd
|
|
348
|
+
.command('list')
|
|
349
|
+
.description('Show channel DM policy and routing config')
|
|
350
|
+
.action(() => {
|
|
351
|
+
const { channelsConfigList } = require('../src/cli/commands/channels');
|
|
352
|
+
channelsConfigList();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
configChannelsCmd
|
|
356
|
+
.command('dm-policy <platform> <policy>')
|
|
357
|
+
.description('Set dmPolicy for a channel platform (pairing|open|closed)')
|
|
358
|
+
.action((platform: string, policy: string) => {
|
|
359
|
+
const { setChannelDmPolicy } = require('../src/cli/commands/channels');
|
|
360
|
+
setChannelDmPolicy(platform, policy);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const configChannelsAllowCmd = configChannelsCmd
|
|
364
|
+
.command('allow')
|
|
365
|
+
.description('Manage channel allowFrom list');
|
|
366
|
+
|
|
367
|
+
configChannelsAllowCmd
|
|
368
|
+
.command('list <platform>')
|
|
369
|
+
.description('List allowFrom entries for a platform')
|
|
370
|
+
.action((platform: string) => {
|
|
371
|
+
const { listChannelAllowFrom } = require('../src/cli/commands/channels');
|
|
372
|
+
listChannelAllowFrom(platform);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
configChannelsAllowCmd
|
|
376
|
+
.command('add <platform> <senderId>')
|
|
377
|
+
.description('Add senderId to allowFrom')
|
|
378
|
+
.action((platform: string, senderId: string) => {
|
|
379
|
+
const { addChannelAllowFrom } = require('../src/cli/commands/channels');
|
|
380
|
+
addChannelAllowFrom(platform, senderId);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
configChannelsAllowCmd
|
|
384
|
+
.command('remove <platform> <senderId>')
|
|
385
|
+
.description('Remove senderId from allowFrom')
|
|
386
|
+
.action((platform: string, senderId: string) => {
|
|
387
|
+
const { removeChannelAllowFrom } = require('../src/cli/commands/channels');
|
|
388
|
+
removeChannelAllowFrom(platform, senderId);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const configChannelsRoutingCmd = configChannelsCmd
|
|
392
|
+
.command('routing')
|
|
393
|
+
.description('Manage channel session routing rules');
|
|
394
|
+
|
|
395
|
+
configChannelsRoutingCmd
|
|
396
|
+
.command('strategy <strategy>')
|
|
397
|
+
.description('Set routing strategy (channel|platform_channel|platform_sender)')
|
|
398
|
+
.action((strategy: string) => {
|
|
399
|
+
const { setRoutingStrategy } = require('../src/cli/commands/channels');
|
|
400
|
+
setRoutingStrategy(strategy);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
configChannelsRoutingCmd
|
|
404
|
+
.command('add <sessionId>')
|
|
405
|
+
.description('Add routing rule')
|
|
406
|
+
.option('--platform <platform>', 'Platform matcher')
|
|
407
|
+
.option('--channel <channelId>', 'Channel matcher')
|
|
408
|
+
.option('--sender <senderId>', 'Sender matcher')
|
|
409
|
+
.action((sessionId: string, opts: any) => {
|
|
410
|
+
const { addRoutingRule } = require('../src/cli/commands/channels');
|
|
411
|
+
addRoutingRule(sessionId, { platform: opts.platform, channel: opts.channel, sender: opts.sender });
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
configChannelsRoutingCmd
|
|
415
|
+
.command('remove <sessionId>')
|
|
416
|
+
.description('Remove routing rule(s) for sessionId and optional matchers')
|
|
417
|
+
.option('--platform <platform>', 'Platform matcher')
|
|
418
|
+
.option('--channel <channelId>', 'Channel matcher')
|
|
419
|
+
.option('--sender <senderId>', 'Sender matcher')
|
|
420
|
+
.action((sessionId: string, opts: any) => {
|
|
421
|
+
const { removeRoutingRule } = require('../src/cli/commands/channels');
|
|
422
|
+
removeRoutingRule(sessionId, { platform: opts.platform, channel: opts.channel, sender: opts.sender });
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
configChannelsRoutingCmd
|
|
426
|
+
.command('list')
|
|
427
|
+
.description('List DM policy and routing settings')
|
|
428
|
+
.action(() => {
|
|
429
|
+
const { channelsConfigList } = require('../src/cli/commands/channels');
|
|
430
|
+
channelsConfigList();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const configSandboxCmd = configCmd
|
|
434
|
+
.command('sandbox')
|
|
435
|
+
.description('Manage session sandbox isolation');
|
|
436
|
+
|
|
437
|
+
configSandboxCmd
|
|
438
|
+
.command('status')
|
|
439
|
+
.description('Show sandbox mode and tool policy')
|
|
440
|
+
.action(() => {
|
|
441
|
+
const { sandboxStatus } = require('../src/cli/commands/sandbox');
|
|
442
|
+
sandboxStatus();
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
configSandboxCmd
|
|
446
|
+
.command('mode <mode>')
|
|
447
|
+
.description('Set sandbox mode (off|non-main|all)')
|
|
448
|
+
.action((mode: string) => {
|
|
449
|
+
const { setSandboxMode } = require('../src/cli/commands/sandbox');
|
|
450
|
+
setSandboxMode(mode);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
const configSandboxAllowCmd = configSandboxCmd
|
|
454
|
+
.command('allow')
|
|
455
|
+
.description('Manage sandbox allowed tool list');
|
|
456
|
+
|
|
457
|
+
configSandboxAllowCmd
|
|
458
|
+
.command('add <toolName>')
|
|
459
|
+
.description('Add allowed tool')
|
|
460
|
+
.action((toolName: string) => {
|
|
461
|
+
const { addSandboxAllowedTool } = require('../src/cli/commands/sandbox');
|
|
462
|
+
addSandboxAllowedTool(toolName);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
configSandboxAllowCmd
|
|
466
|
+
.command('remove <toolName>')
|
|
467
|
+
.description('Remove allowed tool')
|
|
468
|
+
.action((toolName: string) => {
|
|
469
|
+
const { removeSandboxAllowedTool } = require('../src/cli/commands/sandbox');
|
|
470
|
+
removeSandboxAllowedTool(toolName);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const configSandboxDenyCmd = configSandboxCmd
|
|
474
|
+
.command('deny')
|
|
475
|
+
.description('Manage sandbox denied tool list');
|
|
476
|
+
|
|
477
|
+
configSandboxDenyCmd
|
|
478
|
+
.command('add <toolName>')
|
|
479
|
+
.description('Add denied tool')
|
|
480
|
+
.action((toolName: string) => {
|
|
481
|
+
const { addSandboxDeniedTool } = require('../src/cli/commands/sandbox');
|
|
482
|
+
addSandboxDeniedTool(toolName);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
configSandboxDenyCmd
|
|
486
|
+
.command('remove <toolName>')
|
|
487
|
+
.description('Remove denied tool')
|
|
488
|
+
.action((toolName: string) => {
|
|
489
|
+
const { removeSandboxDeniedTool } = require('../src/cli/commands/sandbox');
|
|
490
|
+
removeSandboxDeniedTool(toolName);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
configSandboxCmd.action(() => {
|
|
494
|
+
const { sandboxStatus } = require('../src/cli/commands/sandbox');
|
|
495
|
+
sandboxStatus();
|
|
496
|
+
});
|
|
497
|
+
|
|
159
498
|
// Default config action (no sub-command) shows list
|
|
160
499
|
configCmd.action(() => {
|
|
161
500
|
const { configList } = require('../src/cli/commands/config');
|
|
@@ -214,6 +553,8 @@ program
|
|
|
214
553
|
|
|
215
554
|
const fs = require('fs');
|
|
216
555
|
const config = getConfigManager();
|
|
556
|
+
const { auditDmPolicies, applySafeDmDefaults } = require('../src/channels/security');
|
|
557
|
+
const { auditSandboxPosture, applySafeSandboxDefaults } = require('../src/core/sandbox');
|
|
217
558
|
const issues: string[] = [];
|
|
218
559
|
|
|
219
560
|
// Check config file permissions
|
|
@@ -236,6 +577,36 @@ program
|
|
|
236
577
|
fishInfo(`${key} found in environment (normal for CI/CD, prefer config file for local use)`);
|
|
237
578
|
}
|
|
238
579
|
}
|
|
580
|
+
|
|
581
|
+
const dmAudit = auditDmPolicies(config.getAll().channels);
|
|
582
|
+
for (const issue of dmAudit.failures) {
|
|
583
|
+
issues.push(issue.message);
|
|
584
|
+
}
|
|
585
|
+
for (const issue of dmAudit.warnings) {
|
|
586
|
+
issues.push(issue.message);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (opts.fix && dmAudit.failures.length > 0) {
|
|
590
|
+
const changes = applySafeDmDefaults(config);
|
|
591
|
+
for (const change of changes) {
|
|
592
|
+
fishInfo(`Fixed: ${change}`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const sandboxAudit = auditSandboxPosture(config.getAll());
|
|
597
|
+
for (const issue of sandboxAudit.failures) {
|
|
598
|
+
issues.push(issue.message);
|
|
599
|
+
}
|
|
600
|
+
for (const issue of sandboxAudit.warnings) {
|
|
601
|
+
issues.push(issue.message);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (opts.fix && sandboxAudit.failures.length > 0) {
|
|
605
|
+
const changes = applySafeSandboxDefaults(config);
|
|
606
|
+
for (const change of changes) {
|
|
607
|
+
fishInfo(`Fixed: ${change}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
239
610
|
|
|
240
611
|
if (issues.length === 0) {
|
|
241
612
|
const { fishSuccess } = require('../src/cli/animations/fish');
|
|
@@ -248,6 +619,43 @@ program
|
|
|
248
619
|
}
|
|
249
620
|
});
|
|
250
621
|
|
|
622
|
+
// ─── Pairing Command ──────────────────────────────────────────────
|
|
623
|
+
|
|
624
|
+
const pairingCmd = program
|
|
625
|
+
.command('pairing')
|
|
626
|
+
.description('Manage DM pairing approvals for channel adapters');
|
|
627
|
+
|
|
628
|
+
pairingCmd
|
|
629
|
+
.command('list')
|
|
630
|
+
.description('List approved pairings')
|
|
631
|
+
.option('--pending', 'Include pending pairing requests')
|
|
632
|
+
.option('-p, --platform <platform>', 'Filter by platform')
|
|
633
|
+
.action((opts: any) => {
|
|
634
|
+
const { pairingList } = require('../src/cli/commands/pairing');
|
|
635
|
+
pairingList({ pending: opts.pending, platform: opts.platform });
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
pairingCmd
|
|
639
|
+
.command('approve <platform> <code>')
|
|
640
|
+
.description('Approve a pending pairing code')
|
|
641
|
+
.action((platform: string, code: string) => {
|
|
642
|
+
const { pairingApprove } = require('../src/cli/commands/pairing');
|
|
643
|
+
pairingApprove(platform, code);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
pairingCmd
|
|
647
|
+
.command('revoke <platform> <senderId>')
|
|
648
|
+
.description('Revoke an approved sender pairing')
|
|
649
|
+
.action((platform: string, senderId: string) => {
|
|
650
|
+
const { pairingRevoke } = require('../src/cli/commands/pairing');
|
|
651
|
+
pairingRevoke(platform, senderId);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
pairingCmd.action(() => {
|
|
655
|
+
const { pairingList } = require('../src/cli/commands/pairing');
|
|
656
|
+
pairingList({ pending: true });
|
|
657
|
+
});
|
|
658
|
+
|
|
251
659
|
// ─── Agents Command ─────────────────────────────────────────────
|
|
252
660
|
|
|
253
661
|
const agentsCmd = program
|
|
@@ -285,6 +693,155 @@ agentsCmd.action(() => {
|
|
|
285
693
|
agentsList();
|
|
286
694
|
});
|
|
287
695
|
|
|
696
|
+
// ─── Workflow Command ───────────────────────────────────────────
|
|
697
|
+
|
|
698
|
+
const workflowCmd = program
|
|
699
|
+
.command('workflow')
|
|
700
|
+
.description('Manage and run Fishbone workflows');
|
|
701
|
+
|
|
702
|
+
workflowCmd
|
|
703
|
+
.command('run <file>')
|
|
704
|
+
.description('Run a .fishbone workflow file')
|
|
705
|
+
.action(async (file: string) => {
|
|
706
|
+
printQuickSplash(VERSION);
|
|
707
|
+
const { parseFishboneFile, FishboneEngine } = require('../src/workflow/engine');
|
|
708
|
+
const path = require('path');
|
|
709
|
+
const fs = require('fs');
|
|
710
|
+
|
|
711
|
+
const filePath = path.resolve(process.cwd(), file);
|
|
712
|
+
if (!fs.existsSync(filePath)) {
|
|
713
|
+
fishError(`Workflow file not found: ${filePath}`);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const workflow = parseFishboneFile(filePath);
|
|
718
|
+
fishInfo(`Running workflow: ${workflow.name} (v${workflow.version})`);
|
|
719
|
+
|
|
720
|
+
const engine = new FishboneEngine();
|
|
721
|
+
await engine.execute(workflow, {}, async (event: any) => {
|
|
722
|
+
if (event.type === 'step_start') console.log(chalk.hex('#60a5fa')(`[Step] ${event.stepName}...`));
|
|
723
|
+
if (event.type === 'approval_needed') {
|
|
724
|
+
console.log(chalk.hex('#fbbf24')(`[Approval] Needed for: ${event.content}`));
|
|
725
|
+
console.log(chalk.hex('#34d399')(`Resume token: ${event.resumeToken}`));
|
|
726
|
+
}
|
|
727
|
+
if (event.type === 'step_error') console.log(chalk.hex('#ef4444')(`[Error] ${event.content}`));
|
|
728
|
+
if (event.type === 'workflow_complete') fishSuccess('Workflow completed successfully');
|
|
729
|
+
if (event.type === 'workflow_error') fishError(`Workflow failed: ${event.content}`);
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
workflowCmd
|
|
734
|
+
.command('resume <id> <token>')
|
|
735
|
+
.description('Resume a paused workflow')
|
|
736
|
+
.action(async (id: string, token: string) => {
|
|
737
|
+
printQuickSplash(VERSION);
|
|
738
|
+
const { FishboneEngine } = require('../src/workflow/engine');
|
|
739
|
+
const engine = new FishboneEngine();
|
|
740
|
+
const resumed = await engine.resume(id, token);
|
|
741
|
+
if (resumed) {
|
|
742
|
+
fishSuccess(`Workflow ${id} resumed successfully.`);
|
|
743
|
+
} else {
|
|
744
|
+
fishError(`Failed to resume workflow ${id}. Invalid token or session not found.`);
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
// ─── Tools Command ──────────────────────────────────────────────
|
|
749
|
+
|
|
750
|
+
const toolsCmd = program
|
|
751
|
+
.command('tools')
|
|
752
|
+
.description('Manage AZERCLAW tools and plugins');
|
|
753
|
+
|
|
754
|
+
toolsCmd
|
|
755
|
+
.command('list')
|
|
756
|
+
.description('List all registered tools')
|
|
757
|
+
.action(() => {
|
|
758
|
+
const { getToolRegistry } = require('../src/tools/registry');
|
|
759
|
+
const registry = getToolRegistry();
|
|
760
|
+
const tools = registry.getAll();
|
|
761
|
+
|
|
762
|
+
console.log('');
|
|
763
|
+
fishInfo(`Registered Tools (${tools.length})`);
|
|
764
|
+
console.log('');
|
|
765
|
+
|
|
766
|
+
const Table = require('cli-table3');
|
|
767
|
+
const table = new Table({
|
|
768
|
+
head: [chalk.hex('#60a5fa')('Name'), chalk.hex('#60a5fa')('Version'), chalk.hex('#60a5fa')('Description')],
|
|
769
|
+
colWidths: [20, 10, 50],
|
|
770
|
+
wordWrap: true,
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
tools.forEach((tool: any) => {
|
|
774
|
+
table.push([
|
|
775
|
+
chalk.hex('#34d399')(tool.name),
|
|
776
|
+
chalk.dim(tool.version),
|
|
777
|
+
tool.description.slice(0, 100) + (tool.description.length > 100 ? '...' : '')
|
|
778
|
+
]);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
console.log(table.toString());
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
toolsCmd
|
|
785
|
+
.command('info <name>')
|
|
786
|
+
.description('Show detailed information about a tool')
|
|
787
|
+
.action((name: string) => {
|
|
788
|
+
const { getToolRegistry } = require('../src/tools/registry');
|
|
789
|
+
const tool = getToolRegistry().get(name);
|
|
790
|
+
if (!tool) {
|
|
791
|
+
fishError(`Tool not found: ${name}`);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
console.log('');
|
|
796
|
+
console.log(chalk.hex('#60a5fa').bold(`Tool: ${tool.name}`));
|
|
797
|
+
console.log(chalk.dim(`Version: ${tool.version}`));
|
|
798
|
+
if (tool.author) console.log(chalk.dim(`Author: ${tool.author}`));
|
|
799
|
+
console.log('');
|
|
800
|
+
console.log(tool.description);
|
|
801
|
+
console.log('');
|
|
802
|
+
console.log(chalk.hex('#fbbf24')('Parameters:'));
|
|
803
|
+
console.log(JSON.stringify(tool.parameters, null, 2));
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
toolsCmd
|
|
807
|
+
.command('docs')
|
|
808
|
+
.description('Generate markdown documentation for all tools')
|
|
809
|
+
.option('-o, --output <file>', 'Output file path', 'TOOLS.md')
|
|
810
|
+
.action(async (opts: any) => {
|
|
811
|
+
const { getToolRegistry } = require('../src/tools/registry');
|
|
812
|
+
const fs = require('fs');
|
|
813
|
+
const path = require('path');
|
|
814
|
+
|
|
815
|
+
const tools = getToolRegistry().getAll();
|
|
816
|
+
let markdown = `# 🐟 AZERCLAW Tools Documentation\n\n`;
|
|
817
|
+
markdown += `Generated on ${new Date().toLocaleDateString()}\n\n`;
|
|
818
|
+
|
|
819
|
+
tools.forEach((tool: any) => {
|
|
820
|
+
markdown += `## ${tool.name} (v${tool.version})\n\n`;
|
|
821
|
+
markdown += `${tool.description}\n\n`;
|
|
822
|
+
markdown += `### Parameters\n\n\`\`\`json\n${JSON.stringify(tool.parameters, null, 2)}\n\`\`\`\n\n`;
|
|
823
|
+
markdown += `---\n\n`;
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
const outputPath = path.resolve(process.cwd(), opts.output);
|
|
828
|
+
fs.writeFileSync(outputPath, markdown);
|
|
829
|
+
fishSuccess(`Documentation generated at ${outputPath}`);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
toolsCmd
|
|
833
|
+
.command('install <url_or_path>')
|
|
834
|
+
.description('Install a tool plugin from a URL or local file (coming soon)')
|
|
835
|
+
.action((src: string) => {
|
|
836
|
+
fishInfo(`Plugin installation for '${src}' will be available in the next release.`);
|
|
837
|
+
fishInfo('For now, manually place your .js/.ts files in the ./plugins directory.');
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
toolsCmd.action(() => {
|
|
841
|
+
program.helpInformation();
|
|
842
|
+
});
|
|
843
|
+
|
|
288
844
|
// ─── Parse & Run ────────────────────────────────────────────────
|
|
289
845
|
|
|
846
|
+
|
|
290
847
|
program.parse(process.argv);
|