@startanaicompany/techsaac-cli 0.0.2
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 +63 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +395 -0
- package/dist/client.d.ts +30 -0
- package/dist/client.js +86 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @startanaicompany/techsaac-cli
|
|
2
|
+
|
|
3
|
+
CLI and Node.js client for the TechSAAC AI Agent Orchestrator.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @startanaicompany/techsaac-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
export MCP_ORCHESTRATOR_API_KEY='us_...'
|
|
15
|
+
export MCP_ORCHESTRATOR_URL='https://tech.startanaicompany.com/api/mcp'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## CLI Usage
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# List agents
|
|
22
|
+
techsaac list-agents --limit 10
|
|
23
|
+
|
|
24
|
+
# Get agent details
|
|
25
|
+
techsaac get-agent --agent-id <uuid-or-name>
|
|
26
|
+
|
|
27
|
+
# Start / stop / restart
|
|
28
|
+
techsaac start --agent-id <uuid>
|
|
29
|
+
techsaac stop --agent-id <uuid>
|
|
30
|
+
techsaac restart --agent-id <uuid>
|
|
31
|
+
|
|
32
|
+
# Send message to agent
|
|
33
|
+
techsaac send --agent-id <uuid> --message "deploy to staging"
|
|
34
|
+
|
|
35
|
+
# JSON output
|
|
36
|
+
techsaac list-orgs --format json
|
|
37
|
+
|
|
38
|
+
# Call any API tool directly
|
|
39
|
+
techsaac call list_agents '{"limit":5}'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Run `techsaac help` for the full command list.
|
|
43
|
+
|
|
44
|
+
## Programmatic Usage
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { SaacClient } from '@startanaicompany/techsaac-cli';
|
|
48
|
+
|
|
49
|
+
const client = new SaacClient({
|
|
50
|
+
apiKey: process.env.MCP_ORCHESTRATOR_API_KEY!,
|
|
51
|
+
url: process.env.MCP_ORCHESTRATOR_URL!,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Or auto-detect from env vars
|
|
55
|
+
const client2 = SaacClient.fromEnv();
|
|
56
|
+
|
|
57
|
+
const result = await client.call('list_agents', { limit: 5 });
|
|
58
|
+
console.log(result);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* TechSAAC CLI — command-line interface for the TechSAAC AI Agent Orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* Env vars:
|
|
6
|
+
* MCP_ORCHESTRATOR_API_KEY — API key (us_... format)
|
|
7
|
+
* MCP_ORCHESTRATOR_URL — Orchestrator API URL (e.g. https://tech.startanaicompany.com/api/mcp)
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* techsaac list-agents --limit 5
|
|
11
|
+
* techsaac get-agent --agent-id <uuid>
|
|
12
|
+
* techsaac start-agent --agent-id <uuid>
|
|
13
|
+
* techsaac call <tool_name> '{"key":"value"}'
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* TechSAAC CLI — command-line interface for the TechSAAC AI Agent Orchestrator.
|
|
4
|
+
*
|
|
5
|
+
* Env vars:
|
|
6
|
+
* MCP_ORCHESTRATOR_API_KEY — API key (us_... format)
|
|
7
|
+
* MCP_ORCHESTRATOR_URL — Orchestrator API URL (e.g. https://tech.startanaicompany.com/api/mcp)
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* techsaac list-agents --limit 5
|
|
11
|
+
* techsaac get-agent --agent-id <uuid>
|
|
12
|
+
* techsaac start-agent --agent-id <uuid>
|
|
13
|
+
* techsaac call <tool_name> '{"key":"value"}'
|
|
14
|
+
*/
|
|
15
|
+
import { SaacClient } from './client.js';
|
|
16
|
+
// ─── CLI argument parsing ───────────────────────────────────────────────────
|
|
17
|
+
function parseArgs(argv) {
|
|
18
|
+
const [command, ...rest] = argv;
|
|
19
|
+
const flags = {};
|
|
20
|
+
const positional = [];
|
|
21
|
+
for (let i = 0; i < rest.length; i++) {
|
|
22
|
+
const arg = rest[i];
|
|
23
|
+
if (arg.startsWith('--')) {
|
|
24
|
+
const key = arg.slice(2);
|
|
25
|
+
const next = rest[i + 1];
|
|
26
|
+
if (next && !next.startsWith('--')) {
|
|
27
|
+
flags[key] = next;
|
|
28
|
+
i++;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
flags[key] = 'true';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
positional.push(arg);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { command: command || 'help', flags, positional };
|
|
39
|
+
}
|
|
40
|
+
function kebabToSnake(s) {
|
|
41
|
+
return s.replace(/-/g, '_');
|
|
42
|
+
}
|
|
43
|
+
function snakeToKebab(s) {
|
|
44
|
+
return s.replace(/_/g, '-');
|
|
45
|
+
}
|
|
46
|
+
// ─── Output formatting ─────────────────────────────────────────────────────
|
|
47
|
+
function formatOutput(data, format) {
|
|
48
|
+
if (format === 'json')
|
|
49
|
+
return JSON.stringify(data, null, 2);
|
|
50
|
+
if (Array.isArray(data)) {
|
|
51
|
+
if (data.length === 0)
|
|
52
|
+
return '(empty)';
|
|
53
|
+
return formatTable(data);
|
|
54
|
+
}
|
|
55
|
+
if (data && typeof data === 'object') {
|
|
56
|
+
const obj = data;
|
|
57
|
+
// If the object has a single array property, show that as a table
|
|
58
|
+
const keys = Object.keys(obj);
|
|
59
|
+
const arrayKey = keys.find(k => Array.isArray(obj[k]));
|
|
60
|
+
if (arrayKey && Array.isArray(obj[arrayKey])) {
|
|
61
|
+
const arr = obj[arrayKey];
|
|
62
|
+
// Print any scalar top-level fields first
|
|
63
|
+
const meta = keys.filter(k => k !== arrayKey && !Array.isArray(obj[k]));
|
|
64
|
+
let out = '';
|
|
65
|
+
if (meta.length > 0) {
|
|
66
|
+
out += meta.map(k => `${k}: ${obj[k]}`).join(' ') + '\n\n';
|
|
67
|
+
}
|
|
68
|
+
if (arr.length === 0)
|
|
69
|
+
return out + `${arrayKey}: (empty)`;
|
|
70
|
+
return out + formatTable(arr);
|
|
71
|
+
}
|
|
72
|
+
// Single object — key/value list
|
|
73
|
+
const maxKeyLen = Math.max(...keys.map(k => k.length));
|
|
74
|
+
return keys
|
|
75
|
+
.map(k => {
|
|
76
|
+
const v = obj[k];
|
|
77
|
+
const val = typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v ?? '');
|
|
78
|
+
return `${k.padEnd(maxKeyLen)} ${val}`;
|
|
79
|
+
})
|
|
80
|
+
.join('\n');
|
|
81
|
+
}
|
|
82
|
+
return String(data);
|
|
83
|
+
}
|
|
84
|
+
function formatTable(rows) {
|
|
85
|
+
if (rows.length === 0)
|
|
86
|
+
return '(empty)';
|
|
87
|
+
const first = rows[0];
|
|
88
|
+
// Pick columns — skip large nested objects and long text
|
|
89
|
+
const allKeys = Object.keys(first);
|
|
90
|
+
const cols = allKeys.filter(k => {
|
|
91
|
+
const v = first[k];
|
|
92
|
+
if (Array.isArray(v))
|
|
93
|
+
return false;
|
|
94
|
+
if (typeof v === 'object' && v !== null)
|
|
95
|
+
return false;
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
if (cols.length === 0)
|
|
99
|
+
return JSON.stringify(rows, null, 2);
|
|
100
|
+
// Calculate widths
|
|
101
|
+
const widths = cols.map(c => Math.min(50, Math.max(c.length, ...rows.map(r => String(r[c] ?? '').length))));
|
|
102
|
+
const header = cols.map((c, i) => c.padEnd(widths[i])).join(' ');
|
|
103
|
+
const separator = widths.map(w => '─'.repeat(w)).join('──');
|
|
104
|
+
const body = rows.map(r => cols
|
|
105
|
+
.map((c, i) => {
|
|
106
|
+
const val = String((r[c]) ?? '');
|
|
107
|
+
return val.slice(0, widths[i]).padEnd(widths[i]);
|
|
108
|
+
})
|
|
109
|
+
.join(' '));
|
|
110
|
+
return [header, separator, ...body].join('\n');
|
|
111
|
+
}
|
|
112
|
+
// Flag values that should be parsed as non-strings
|
|
113
|
+
function autoType(value) {
|
|
114
|
+
if (value === 'true')
|
|
115
|
+
return true;
|
|
116
|
+
if (value === 'false')
|
|
117
|
+
return false;
|
|
118
|
+
if (/^\d+$/.test(value))
|
|
119
|
+
return parseInt(value, 10);
|
|
120
|
+
// Try JSON parse for objects/arrays
|
|
121
|
+
if (value.startsWith('{') || value.startsWith('[')) {
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(value);
|
|
124
|
+
}
|
|
125
|
+
catch { /* fall through */ }
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function flagsToArgs(flags) {
|
|
130
|
+
const args = {};
|
|
131
|
+
for (const [k, v] of Object.entries(flags)) {
|
|
132
|
+
if (k === 'format')
|
|
133
|
+
continue;
|
|
134
|
+
args[kebabToSnake(k)] = autoType(v);
|
|
135
|
+
}
|
|
136
|
+
return args;
|
|
137
|
+
}
|
|
138
|
+
const COMMANDS = {
|
|
139
|
+
// Organizations
|
|
140
|
+
'list-orgs': { tool: 'list_organizations', desc: 'List organizations' },
|
|
141
|
+
'create-org': { tool: 'create_organization', desc: 'Create organization' },
|
|
142
|
+
'update-org': { tool: 'update_organization', desc: 'Update organization' },
|
|
143
|
+
// Products
|
|
144
|
+
'list-products': { tool: 'list_products', desc: 'List products' },
|
|
145
|
+
'get-product': { tool: 'get_product', desc: 'Get product details' },
|
|
146
|
+
'create-product': { tool: 'create_product', desc: 'Create product' },
|
|
147
|
+
'update-product': { tool: 'update_product', desc: 'Update product' },
|
|
148
|
+
'get-product-context': { tool: 'get_product_context', desc: 'Get product context files' },
|
|
149
|
+
'update-product-context': { tool: 'update_product_context', desc: 'Update product context' },
|
|
150
|
+
'move-product': { tool: 'move_product', desc: 'Move product to another org' },
|
|
151
|
+
// Agents — core
|
|
152
|
+
'list-agents': { tool: 'list_agents', desc: 'List agents' },
|
|
153
|
+
'get-agent': { tool: 'get_agent', desc: 'Get agent details' },
|
|
154
|
+
'create-agent': { tool: 'create_agent', desc: 'Create agent' },
|
|
155
|
+
'update-agent': { tool: 'update_agent', desc: 'Update agent' },
|
|
156
|
+
'delete-agent': { tool: 'delete_agent', desc: 'Delete agent' },
|
|
157
|
+
'clone-agent': { tool: 'clone_agent', desc: 'Clone agent with config' },
|
|
158
|
+
'move-agent': { tool: 'move_agent', desc: 'Move agent to another org' },
|
|
159
|
+
'move-agent-to-product': { tool: 'move_agent_to_product', desc: 'Move agent to another product' },
|
|
160
|
+
// Agent config & status
|
|
161
|
+
'get-config': { tool: 'get_agent_config', desc: 'Get agent config' },
|
|
162
|
+
'update-config': { tool: 'update_agent_config', desc: 'Update agent config' },
|
|
163
|
+
'get-status': { tool: 'get_agent_status', desc: 'Get agent status' },
|
|
164
|
+
'set-status': { tool: 'set_agent_status', desc: 'Set agent status' },
|
|
165
|
+
'get-env': { tool: 'get_agent_env_vars', desc: 'Get agent env vars (merged)' },
|
|
166
|
+
'set-env': { tool: 'set_agent_env_var', desc: 'Set agent env var' },
|
|
167
|
+
'delete-env': { tool: 'delete_agent_env_var', desc: 'Delete agent env var' },
|
|
168
|
+
'set-product-env': { tool: 'set_product_env_var', desc: 'Set product env var' },
|
|
169
|
+
'delete-product-env': { tool: 'delete_product_env_var', desc: 'Delete product env var' },
|
|
170
|
+
// Agent context
|
|
171
|
+
'get-context': { tool: 'get_agent_context', desc: 'Get agent context files' },
|
|
172
|
+
'update-context': { tool: 'update_agent_context', desc: 'Update agent context' },
|
|
173
|
+
// Agent lifecycle
|
|
174
|
+
'start': { tool: 'start_agent', desc: 'Start agent' },
|
|
175
|
+
'stop': { tool: 'stop_agent', desc: 'Stop agent' },
|
|
176
|
+
'restart': { tool: 'restart_agent', desc: 'Restart agent' },
|
|
177
|
+
'sync-files': { tool: 'sync_agent_files', desc: 'Hot-reload agent context files' },
|
|
178
|
+
// Prompts
|
|
179
|
+
'list-prompts': { tool: 'list_agent_prompts', desc: 'List agent prompts' },
|
|
180
|
+
'create-prompt': { tool: 'create_agent_prompt', desc: 'Create agent prompt' },
|
|
181
|
+
'update-prompt': { tool: 'update_agent_prompt', desc: 'Update prompt' },
|
|
182
|
+
'delete-prompt': { tool: 'delete_agent_prompt', desc: 'Delete prompt' },
|
|
183
|
+
// Activation rules
|
|
184
|
+
'list-rules': { tool: 'list_activation_rules', desc: 'List activation rules' },
|
|
185
|
+
'get-rule': { tool: 'get_activation_rule', desc: 'Get activation rule' },
|
|
186
|
+
'create-rule': { tool: 'create_activation_rule', desc: 'Create activation rule' },
|
|
187
|
+
'update-rule': { tool: 'update_activation_rule', desc: 'Update activation rule' },
|
|
188
|
+
'delete-rule': { tool: 'delete_activation_rule', desc: 'Delete activation rule' },
|
|
189
|
+
'toggle-rule': { tool: 'toggle_activation_rule', desc: 'Toggle activation rule' },
|
|
190
|
+
'rule-fields': { tool: 'get_activation_rule_fields', desc: 'Get available rule condition fields' },
|
|
191
|
+
// Container & Claude accounts
|
|
192
|
+
'list-images': { tool: 'list_container_images', desc: 'List available container images' },
|
|
193
|
+
'set-image': { tool: 'set_agent_container_image', desc: 'Set agent container image' },
|
|
194
|
+
'list-accounts': { tool: 'list_claude_accounts', desc: 'List Claude accounts' },
|
|
195
|
+
'set-account': { tool: 'set_agent_claude_account', desc: 'Assign Claude account to agent' },
|
|
196
|
+
// Communication
|
|
197
|
+
'send': { tool: 'send_orchat_message', desc: 'Send message to agent' },
|
|
198
|
+
'history': { tool: 'get_orchat_history', desc: 'Get orchat history' },
|
|
199
|
+
'send-telegram': { tool: 'send_telegram_message', desc: 'Send Telegram message' },
|
|
200
|
+
// HIVE
|
|
201
|
+
'hive-status': { tool: 'get_agent_hive_status', desc: 'Get HIVE identity status' },
|
|
202
|
+
'hive-rename': { tool: 'rename_agent_hive', desc: 'Rename HIVE identity' },
|
|
203
|
+
'hive-groups': { tool: 'list_agent_hive_groups', desc: 'List agent HIVE groups' },
|
|
204
|
+
'hive-add': { tool: 'add_agent_to_hive_group', desc: 'Add agent to HIVE group' },
|
|
205
|
+
'hive-remove': { tool: 'remove_agent_from_hive_group', desc: 'Remove agent from HIVE group' },
|
|
206
|
+
'hive-repair': { tool: 'repair_agent_hive_groups', desc: 'Repair HIVE group memberships' },
|
|
207
|
+
'hive-org-groups': { tool: 'list_organization_hive_groups', desc: 'List org HIVE groups' },
|
|
208
|
+
'hive-members': { tool: 'list_hive_group_members', desc: 'List HIVE group members' },
|
|
209
|
+
'hive-connect': { tool: 'enable_agent_communication', desc: 'Enable inter-agent communication' },
|
|
210
|
+
// GitHub
|
|
211
|
+
'list-repos': { tool: 'list_github_repos', desc: 'List GitHub repos' },
|
|
212
|
+
'list-github-orgs': { tool: 'list_github_orgs', desc: 'List GitHub organizations' },
|
|
213
|
+
'list-org-repos': { tool: 'list_github_org_repos', desc: 'List repos in GitHub org' },
|
|
214
|
+
'list-branches': { tool: 'list_github_branches', desc: 'List repo branches' },
|
|
215
|
+
'agent-repos': { tool: 'get_agent_repositories', desc: 'Get agent linked repos' },
|
|
216
|
+
'link-repo': { tool: 'link_agent_repository', desc: 'Link repo to agent' },
|
|
217
|
+
'update-repo': { tool: 'update_agent_repository', desc: 'Update repo settings' },
|
|
218
|
+
'unlink-repo': { tool: 'unlink_agent_repository', desc: 'Unlink repo from agent' },
|
|
219
|
+
'list-tokens': { tool: 'list_github_tokens', desc: 'List GitHub tokens' },
|
|
220
|
+
'save-token': { tool: 'save_github_token', desc: 'Save GitHub token' },
|
|
221
|
+
'delete-token': { tool: 'delete_github_token', desc: 'Delete GitHub token' },
|
|
222
|
+
'validate-token': { tool: 'validate_github_token', desc: 'Validate GitHub token' },
|
|
223
|
+
// Scripts & cron
|
|
224
|
+
'list-scripts': { tool: 'list_agent_scripts', desc: 'List agent scripts' },
|
|
225
|
+
'get-script': { tool: 'get_agent_script', desc: 'Get agent script content' },
|
|
226
|
+
'sync-scripts': { tool: 'sync_agent_scripts', desc: 'Sync scripts from container' },
|
|
227
|
+
'list-crontabs': { tool: 'list_agent_crontabs', desc: 'List agent cron jobs' },
|
|
228
|
+
'sync-crontabs': { tool: 'sync_agent_crontabs', desc: 'Sync cron jobs from container' },
|
|
229
|
+
// Personas & company
|
|
230
|
+
'search-personas': { tool: 'search_persona_templates', desc: 'Search persona templates' },
|
|
231
|
+
'get-persona': { tool: 'get_persona_template', desc: 'Get persona template' },
|
|
232
|
+
'persona-categories': { tool: 'get_persona_categories', desc: 'List persona categories' },
|
|
233
|
+
'create-company': { tool: 'create_company', desc: 'Create AI company (background)' },
|
|
234
|
+
// Integrations
|
|
235
|
+
'list-integrations': { tool: 'list_integration_catalog', desc: 'List available integrations' },
|
|
236
|
+
'agent-integrations': { tool: 'list_agent_integrations', desc: 'List agent integrations' },
|
|
237
|
+
'connect': { tool: 'connect_integration', desc: 'Connect integration' },
|
|
238
|
+
'disconnect': { tool: 'disconnect_integration', desc: 'Disconnect integration' },
|
|
239
|
+
'integration-status': { tool: 'get_integration_status', desc: 'Get integration status' },
|
|
240
|
+
// Workspace
|
|
241
|
+
'workspace-keys': { tool: 'get_workspace_keys', desc: 'List Workspace API keys for all orgs' },
|
|
242
|
+
// Usage & diagnostics
|
|
243
|
+
'usage': { tool: 'get_token_usage', desc: 'Get token usage' },
|
|
244
|
+
'usage-range': { tool: 'get_token_usage_range', desc: 'Get token usage time series' },
|
|
245
|
+
'diagnose': { tool: 'diagnose_assistant', desc: 'Diagnose assistant' },
|
|
246
|
+
'logs': { tool: 'get_activity_logs', desc: 'Get activity logs' },
|
|
247
|
+
};
|
|
248
|
+
// ─── Help ───────────────────────────────────────────────────────────────────
|
|
249
|
+
function printHelp() {
|
|
250
|
+
console.log(`
|
|
251
|
+
TechSAAC CLI — AI Agent Orchestrator
|
|
252
|
+
|
|
253
|
+
Usage: techsaac <command> [flags]
|
|
254
|
+
|
|
255
|
+
Environment:
|
|
256
|
+
MCP_ORCHESTRATOR_API_KEY API key (required)
|
|
257
|
+
MCP_ORCHESTRATOR_URL Orchestrator API URL (required)
|
|
258
|
+
|
|
259
|
+
Global flags:
|
|
260
|
+
--format json Output as JSON (default: table)
|
|
261
|
+
|
|
262
|
+
Commands:`);
|
|
263
|
+
const groups = {};
|
|
264
|
+
const groupOrder = [
|
|
265
|
+
'Organizations', 'Products', 'Agents', 'Config & Status', 'Context',
|
|
266
|
+
'Lifecycle', 'Prompts', 'Activation Rules', 'Container & Accounts',
|
|
267
|
+
'Communication', 'HIVE', 'GitHub', 'Scripts & Cron',
|
|
268
|
+
'Personas & Company', 'Integrations', 'Workspace', 'Usage & Diagnostics',
|
|
269
|
+
];
|
|
270
|
+
const commandGroups = {};
|
|
271
|
+
for (const cmd of Object.keys(COMMANDS)) {
|
|
272
|
+
let group = 'Other';
|
|
273
|
+
if (['list-orgs', 'create-org', 'update-org'].includes(cmd))
|
|
274
|
+
group = 'Organizations';
|
|
275
|
+
else if (cmd.includes('product'))
|
|
276
|
+
group = 'Products';
|
|
277
|
+
else if (['list-agents', 'get-agent', 'create-agent', 'update-agent', 'delete-agent', 'clone-agent', 'move-agent', 'move-agent-to-product'].includes(cmd))
|
|
278
|
+
group = 'Agents';
|
|
279
|
+
else if (['get-config', 'update-config', 'get-status', 'set-status', 'get-env', 'set-env', 'delete-env', 'set-product-env', 'delete-product-env'].includes(cmd))
|
|
280
|
+
group = 'Config & Status';
|
|
281
|
+
else if (cmd.includes('context'))
|
|
282
|
+
group = 'Context';
|
|
283
|
+
else if (['start', 'stop', 'restart', 'sync-files'].includes(cmd))
|
|
284
|
+
group = 'Lifecycle';
|
|
285
|
+
else if (cmd.includes('prompt'))
|
|
286
|
+
group = 'Prompts';
|
|
287
|
+
else if (cmd.includes('rule') || cmd === 'rule-fields')
|
|
288
|
+
group = 'Activation Rules';
|
|
289
|
+
else if (['list-images', 'set-image', 'list-accounts', 'set-account'].includes(cmd))
|
|
290
|
+
group = 'Container & Accounts';
|
|
291
|
+
else if (['send', 'history', 'send-telegram'].includes(cmd))
|
|
292
|
+
group = 'Communication';
|
|
293
|
+
else if (cmd.startsWith('hive'))
|
|
294
|
+
group = 'HIVE';
|
|
295
|
+
else if (cmd.includes('repo') || cmd.includes('branch') || cmd.includes('token') || cmd.includes('github'))
|
|
296
|
+
group = 'GitHub';
|
|
297
|
+
else if (cmd.includes('script') || cmd.includes('cron'))
|
|
298
|
+
group = 'Scripts & Cron';
|
|
299
|
+
else if (cmd.includes('persona') || cmd === 'create-company')
|
|
300
|
+
group = 'Personas & Company';
|
|
301
|
+
else if (cmd.includes('integr') || cmd === 'connect' || cmd === 'disconnect')
|
|
302
|
+
group = 'Integrations';
|
|
303
|
+
else if (cmd === 'workspace-keys')
|
|
304
|
+
group = 'Workspace';
|
|
305
|
+
else if (['usage', 'usage-range', 'diagnose', 'logs'].includes(cmd))
|
|
306
|
+
group = 'Usage & Diagnostics';
|
|
307
|
+
commandGroups[cmd] = group;
|
|
308
|
+
}
|
|
309
|
+
for (const group of groupOrder) {
|
|
310
|
+
const cmds = Object.keys(COMMANDS).filter(c => commandGroups[c] === group);
|
|
311
|
+
if (cmds.length === 0)
|
|
312
|
+
continue;
|
|
313
|
+
console.log(`\n ${group}:`);
|
|
314
|
+
for (const cmd of cmds) {
|
|
315
|
+
console.log(` ${cmd.padEnd(26)} ${COMMANDS[cmd].desc}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
console.log(`
|
|
319
|
+
Other:
|
|
320
|
+
call <tool> [json] Call any API tool directly
|
|
321
|
+
tools List all available API tools
|
|
322
|
+
help Show this help
|
|
323
|
+
|
|
324
|
+
Examples:
|
|
325
|
+
techsaac list-agents --limit 5
|
|
326
|
+
techsaac get-agent --agent-id <uuid-or-name>
|
|
327
|
+
techsaac start --agent-id <uuid>
|
|
328
|
+
techsaac send --agent-id <uuid> --message "deploy to staging"
|
|
329
|
+
techsaac call list_agents '{"limit":5}'
|
|
330
|
+
`);
|
|
331
|
+
}
|
|
332
|
+
// ─── Main ───────────────────────────────────────────────────────────────────
|
|
333
|
+
const VERSION = '0.0.2';
|
|
334
|
+
async function main() {
|
|
335
|
+
const args = process.argv.slice(2);
|
|
336
|
+
if (args.length === 0 || args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
|
|
337
|
+
printHelp();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (args[0] === 'version' || args[0] === '--version' || args[0] === '-v') {
|
|
341
|
+
console.log(`techsaac ${VERSION}`);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const { command, flags, positional } = parseArgs(args);
|
|
345
|
+
const format = flags.format || 'table';
|
|
346
|
+
let client;
|
|
347
|
+
try {
|
|
348
|
+
client = SaacClient.fromEnv();
|
|
349
|
+
}
|
|
350
|
+
catch (e) {
|
|
351
|
+
console.error(`Error: ${e.message}`);
|
|
352
|
+
console.error('Set MCP_ORCHESTRATOR_API_KEY and MCP_ORCHESTRATOR_URL environment variables.');
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
// Special commands
|
|
357
|
+
if (command === 'tools') {
|
|
358
|
+
const tools = await client.listTools();
|
|
359
|
+
const rows = tools.map(t => ({ name: t.name, description: t.description.slice(0, 80) }));
|
|
360
|
+
console.log(formatOutput(rows, format));
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (command === 'call') {
|
|
364
|
+
const toolName = positional[0];
|
|
365
|
+
if (!toolName) {
|
|
366
|
+
console.error('Usage: hive call <tool_name> [json_args]');
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
let callArgs = {};
|
|
370
|
+
if (positional[1]) {
|
|
371
|
+
callArgs = JSON.parse(positional[1]);
|
|
372
|
+
}
|
|
373
|
+
// Also merge --flags
|
|
374
|
+
Object.assign(callArgs, flagsToArgs(flags));
|
|
375
|
+
const result = await client.call(toolName, callArgs);
|
|
376
|
+
console.log(formatOutput(result, format));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
// Named command
|
|
380
|
+
const def = COMMANDS[command];
|
|
381
|
+
if (!def) {
|
|
382
|
+
console.error(`Unknown command: ${command}`);
|
|
383
|
+
console.error(`Run 'hive help' for available commands.`);
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
const toolArgs = def.mapFlags ? def.mapFlags(flags) : flagsToArgs(flags);
|
|
387
|
+
const result = await client.call(def.tool, toolArgs);
|
|
388
|
+
console.log(formatOutput(result, format));
|
|
389
|
+
}
|
|
390
|
+
catch (e) {
|
|
391
|
+
console.error(`Error: ${e.message}`);
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
main();
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TechSAAC Orchestrator API Client
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for the orchestrator API at tech.startanaicompany.com.
|
|
5
|
+
* Uses the JSON-RPC endpoint under /api/mcp for tool invocation.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { SaacClient } from '@startanaicompany/techsaac-cli';
|
|
9
|
+
* const client = new SaacClient({ apiKey: 'us_...', url: 'https://tech.startanaicompany.com/api/mcp' });
|
|
10
|
+
* const orgs = await client.call('list_organizations', { limit: 10 });
|
|
11
|
+
*/
|
|
12
|
+
export interface SaacClientOptions {
|
|
13
|
+
apiKey: string;
|
|
14
|
+
url: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class SaacClient {
|
|
17
|
+
private apiKey;
|
|
18
|
+
private url;
|
|
19
|
+
private reqId;
|
|
20
|
+
constructor(opts: SaacClientOptions);
|
|
21
|
+
static fromEnv(): SaacClient;
|
|
22
|
+
call(tool: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
23
|
+
listTools(): Promise<Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
inputSchema: unknown;
|
|
27
|
+
}>>;
|
|
28
|
+
}
|
|
29
|
+
export { SaacClient as HiveClient };
|
|
30
|
+
export type { SaacClientOptions as HiveClientOptions };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TechSAAC Orchestrator API Client
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for the orchestrator API at tech.startanaicompany.com.
|
|
5
|
+
* Uses the JSON-RPC endpoint under /api/mcp for tool invocation.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { SaacClient } from '@startanaicompany/techsaac-cli';
|
|
9
|
+
* const client = new SaacClient({ apiKey: 'us_...', url: 'https://tech.startanaicompany.com/api/mcp' });
|
|
10
|
+
* const orgs = await client.call('list_organizations', { limit: 10 });
|
|
11
|
+
*/
|
|
12
|
+
export class SaacClient {
|
|
13
|
+
apiKey;
|
|
14
|
+
url;
|
|
15
|
+
reqId = 0;
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.apiKey = opts.apiKey;
|
|
18
|
+
this.url = opts.url;
|
|
19
|
+
}
|
|
20
|
+
static fromEnv() {
|
|
21
|
+
const apiKey = process.env.MCP_ORCHESTRATOR_API_KEY;
|
|
22
|
+
const url = process.env.MCP_ORCHESTRATOR_URL;
|
|
23
|
+
if (!apiKey)
|
|
24
|
+
throw new Error('MCP_ORCHESTRATOR_API_KEY not set');
|
|
25
|
+
if (!url)
|
|
26
|
+
throw new Error('MCP_ORCHESTRATOR_URL not set');
|
|
27
|
+
return new SaacClient({ apiKey, url });
|
|
28
|
+
}
|
|
29
|
+
async call(tool, args = {}) {
|
|
30
|
+
const body = {
|
|
31
|
+
jsonrpc: '2.0',
|
|
32
|
+
id: String(++this.reqId),
|
|
33
|
+
method: 'tools/call',
|
|
34
|
+
params: { name: tool, arguments: args },
|
|
35
|
+
};
|
|
36
|
+
const res = await fetch(this.url, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
Authorization: `ApiKey ${this.apiKey}`,
|
|
41
|
+
},
|
|
42
|
+
body: JSON.stringify(body),
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
throw new Error(`HTTP ${res.status}: ${await res.text()}`);
|
|
46
|
+
}
|
|
47
|
+
const json = (await res.json());
|
|
48
|
+
if (json.error) {
|
|
49
|
+
throw new Error(json.error.message);
|
|
50
|
+
}
|
|
51
|
+
const content = json.result?.content?.[0]?.text;
|
|
52
|
+
if (json.result?.isError) {
|
|
53
|
+
throw new Error(content || 'Unknown error');
|
|
54
|
+
}
|
|
55
|
+
if (!content)
|
|
56
|
+
return json.result;
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(content);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return content;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async listTools() {
|
|
65
|
+
const body = {
|
|
66
|
+
jsonrpc: '2.0',
|
|
67
|
+
id: String(++this.reqId),
|
|
68
|
+
method: 'tools/list',
|
|
69
|
+
params: {},
|
|
70
|
+
};
|
|
71
|
+
const res = await fetch(this.url, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: {
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
Authorization: `ApiKey ${this.apiKey}`,
|
|
76
|
+
},
|
|
77
|
+
body: JSON.stringify(body),
|
|
78
|
+
});
|
|
79
|
+
if (!res.ok)
|
|
80
|
+
throw new Error(`HTTP ${res.status}`);
|
|
81
|
+
const json = (await res.json());
|
|
82
|
+
return json.result?.tools || [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Backwards compat
|
|
86
|
+
export { SaacClient as HiveClient };
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startanaicompany/techsaac-cli",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "CLI for the TechSAAC AI Agent Orchestrator",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"techsaac": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/client.js",
|
|
10
|
+
"types": "./dist/client.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/client.js",
|
|
14
|
+
"types": "./dist/client.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"!dist/*.test.*",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"test": "node --test dist/cli.test.js",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": ["techsaac", "saac", "ai", "agents", "orchestrator", "cli"],
|
|
28
|
+
"author": "StartAnAiCompany <support@startanaicompany.com>",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/startanaicompany/techsaac-cli.git"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/startanaicompany/techsaac-cli#readme",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/startanaicompany/techsaac-cli/issues"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18.0.0"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"typescript": "^5.5.0",
|
|
46
|
+
"@types/node": "^20.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|