@hsafa/cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/commands/agent.js +853 -0
- package/dist/commands/auth.js +168 -0
- package/dist/commands/chat.js +233 -0
- package/dist/commands/doc.js +37 -0
- package/dist/commands/gql.js +106 -0
- package/dist/commands/invite.js +147 -0
- package/dist/commands/kb.js +155 -0
- package/dist/commands/key.js +87 -0
- package/dist/commands/mcp.js +56 -0
- package/dist/commands/member.js +103 -0
- package/dist/commands/profile.js +65 -0
- package/dist/commands/project.js +90 -0
- package/dist/commands/system.js +27 -0
- package/dist/commands/user.js +48 -0
- package/dist/commands/workspace.js +77 -0
- package/dist/index.js +56 -0
- package/dist/templates/basic-assistant.json +57 -0
- package/dist/templates/researcher.json +58 -0
- package/dist/utils/api.js +13 -0
- package/dist/utils/config.js +22 -0
- package/dist/utils/graphql.js +17 -0
- package/package.json +43 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import Table from 'cli-table3';
|
|
3
|
+
import api from '../utils/api.js';
|
|
4
|
+
import { graphqlRequest } from '../utils/graphql.js';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
export function registerKbCommands(program) {
|
|
7
|
+
const kb = program.command('kb').description('Knowledge Base management');
|
|
8
|
+
kb
|
|
9
|
+
.command('list')
|
|
10
|
+
.description('List your knowledge bases')
|
|
11
|
+
.action(async () => {
|
|
12
|
+
const spinner = ora('Fetching knowledge bases...').start();
|
|
13
|
+
try {
|
|
14
|
+
const query = `
|
|
15
|
+
query {
|
|
16
|
+
myKnowledgeBases {
|
|
17
|
+
id
|
|
18
|
+
name
|
|
19
|
+
description
|
|
20
|
+
createdAt
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
const data = await graphqlRequest(query);
|
|
25
|
+
const kbs = data.myKnowledgeBases;
|
|
26
|
+
spinner.stop();
|
|
27
|
+
if (kbs.length === 0) {
|
|
28
|
+
console.log(chalk.yellow('No knowledge bases found.'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const table = new Table({
|
|
32
|
+
head: [chalk.cyan('ID'), chalk.cyan('Name'), chalk.cyan('Description'), chalk.cyan('Created')],
|
|
33
|
+
colWidths: [36, 20, 30, 25]
|
|
34
|
+
});
|
|
35
|
+
kbs.forEach((k) => {
|
|
36
|
+
table.push([k.id, k.name, k.description || 'N/A', k.createdAt]);
|
|
37
|
+
});
|
|
38
|
+
console.log(table.toString());
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
spinner.fail(`Failed to fetch KB: ${error.message}`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
kb
|
|
45
|
+
.command('docs <id>')
|
|
46
|
+
.description('List documents in a knowledge base')
|
|
47
|
+
.action(async (id) => {
|
|
48
|
+
const spinner = ora('Fetching documents...').start();
|
|
49
|
+
try {
|
|
50
|
+
const query = `
|
|
51
|
+
query($id: ID!) {
|
|
52
|
+
knowledgeBaseDocuments(knowledgeBaseId: $id) {
|
|
53
|
+
id
|
|
54
|
+
fileName
|
|
55
|
+
fileType
|
|
56
|
+
status
|
|
57
|
+
uploadedAt
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
const data = await graphqlRequest(query, { id });
|
|
62
|
+
const docs = data.knowledgeBaseDocuments;
|
|
63
|
+
spinner.stop();
|
|
64
|
+
if (docs.length === 0) {
|
|
65
|
+
console.log(chalk.yellow('No documents found in this KB.'));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const table = new Table({
|
|
69
|
+
head: [chalk.cyan('ID'), chalk.cyan('File Name'), chalk.cyan('Status'), chalk.cyan('Uploaded')],
|
|
70
|
+
colWidths: [36, 30, 15, 25]
|
|
71
|
+
});
|
|
72
|
+
docs.forEach((d) => {
|
|
73
|
+
table.push([d.id, d.fileName, d.status, d.uploadedAt]);
|
|
74
|
+
});
|
|
75
|
+
console.log(table.toString());
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
spinner.fail(`Failed to fetch documents: ${error.message}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
kb
|
|
82
|
+
.command('create <name>')
|
|
83
|
+
.description('Create a new knowledge base')
|
|
84
|
+
.option('-d, --description <desc>', 'KB description')
|
|
85
|
+
.action(async (name, options) => {
|
|
86
|
+
const spinner = ora('Creating knowledge base...').start();
|
|
87
|
+
try {
|
|
88
|
+
const mutation = `
|
|
89
|
+
mutation($input: CreateKnowledgeBaseInput!) {
|
|
90
|
+
createKnowledgeBase(input: $input) {
|
|
91
|
+
id
|
|
92
|
+
name
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
const data = await graphqlRequest(mutation, {
|
|
97
|
+
input: {
|
|
98
|
+
name,
|
|
99
|
+
description: options.description || '',
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
spinner.succeed(chalk.green(`Knowledge base "${data.createKnowledgeBase.name}" created with ID: ${data.createKnowledgeBase.id}`));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
spinner.fail(`Failed to create KB: ${error.message}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
kb
|
|
109
|
+
.command('doc-add <kbId> <file>')
|
|
110
|
+
.description('Upload and add a document to a knowledge base')
|
|
111
|
+
.action(async (kbId, file) => {
|
|
112
|
+
const fs = await import('fs');
|
|
113
|
+
const path = await import('path');
|
|
114
|
+
const FormData = (await import('form-data')).default;
|
|
115
|
+
const filePath = path.resolve(file);
|
|
116
|
+
if (!fs.existsSync(filePath)) {
|
|
117
|
+
console.error(chalk.red(`File not found: ${file}`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const spinner = ora('Uploading and adding document...').start();
|
|
121
|
+
try {
|
|
122
|
+
// 1. Upload file
|
|
123
|
+
const formData = new FormData();
|
|
124
|
+
formData.append('file', fs.createReadStream(filePath));
|
|
125
|
+
const uploadResponse = await api.post('/api/uploads', formData, {
|
|
126
|
+
headers: {
|
|
127
|
+
...formData.getHeaders(),
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const uploadData = uploadResponse.data;
|
|
131
|
+
spinner.text = 'Adding document to KB...';
|
|
132
|
+
// 2. Add to KB via GraphQL
|
|
133
|
+
const mutation = `
|
|
134
|
+
mutation($kbId: ID!, $docs: [CreateKnowledgeDocumentInput!]!) {
|
|
135
|
+
addKnowledgeDocuments(knowledgeBaseId: $kbId, documents: $docs) {
|
|
136
|
+
id
|
|
137
|
+
fileName
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
`;
|
|
141
|
+
await graphqlRequest(mutation, {
|
|
142
|
+
kbId,
|
|
143
|
+
docs: [{
|
|
144
|
+
fileName: uploadData.name,
|
|
145
|
+
fileType: uploadData.mimeType,
|
|
146
|
+
storagePath: uploadData.url, // Using URL as storagePath as it's what the server returns
|
|
147
|
+
}]
|
|
148
|
+
});
|
|
149
|
+
spinner.succeed(chalk.green(`Document "${uploadData.name}" added to KB ${kbId}`));
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
spinner.fail(`Failed: ${error.message}`);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import Table from 'cli-table3';
|
|
3
|
+
import { graphqlRequest } from '../utils/graphql.js';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
export function registerKeyCommands(program) {
|
|
6
|
+
const key = program.command('key').description('API Key management');
|
|
7
|
+
key
|
|
8
|
+
.command('list')
|
|
9
|
+
.description('List your API keys')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
const spinner = ora('Fetching API keys...').start();
|
|
12
|
+
try {
|
|
13
|
+
const query = `
|
|
14
|
+
query {
|
|
15
|
+
apiKeys {
|
|
16
|
+
id
|
|
17
|
+
name
|
|
18
|
+
provider
|
|
19
|
+
keyValue
|
|
20
|
+
isActive
|
|
21
|
+
lastUsedAt
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
const data = await graphqlRequest(query);
|
|
26
|
+
const keys = data.apiKeys;
|
|
27
|
+
spinner.stop();
|
|
28
|
+
if (keys.length === 0) {
|
|
29
|
+
console.log(chalk.yellow('No API keys found.'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const table = new Table({
|
|
33
|
+
head: [chalk.cyan('ID'), chalk.cyan('Name'), chalk.cyan('Provider'), chalk.cyan('Status'), chalk.cyan('Last Used')],
|
|
34
|
+
colWidths: [36, 20, 15, 10, 25]
|
|
35
|
+
});
|
|
36
|
+
keys.forEach((k) => {
|
|
37
|
+
const status = k.isActive ? chalk.green('Active') : chalk.red('Inactive');
|
|
38
|
+
table.push([k.id, k.name, k.provider, status, k.lastUsedAt || 'Never']);
|
|
39
|
+
});
|
|
40
|
+
console.log(table.toString());
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
spinner.fail(`Failed to fetch keys: ${error.message}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
key
|
|
47
|
+
.command('add <name> <provider> <value>')
|
|
48
|
+
.description('Add a new API key (Providers: openai, anthropic, google, groq, perplexity, xai, openrouter)')
|
|
49
|
+
.action(async (name, provider, value) => {
|
|
50
|
+
const spinner = ora('Adding API key...').start();
|
|
51
|
+
try {
|
|
52
|
+
const mutation = `
|
|
53
|
+
mutation($input: CreateApiKeyInput!) {
|
|
54
|
+
createApiKey(input: $input) {
|
|
55
|
+
id
|
|
56
|
+
name
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
const data = await graphqlRequest(mutation, {
|
|
61
|
+
input: { name, provider, keyValue: value }
|
|
62
|
+
});
|
|
63
|
+
spinner.succeed(chalk.green(`API key "${data.createApiKey.name}" added successfully.`));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
spinner.fail(`Failed to add key: ${error.message}`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
key
|
|
70
|
+
.command('delete <id>')
|
|
71
|
+
.description('Delete an API key')
|
|
72
|
+
.action(async (id) => {
|
|
73
|
+
const spinner = ora('Deleting API key...').start();
|
|
74
|
+
try {
|
|
75
|
+
const mutation = `
|
|
76
|
+
mutation($id: ID!) {
|
|
77
|
+
deleteApiKey(id: $id)
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
await graphqlRequest(mutation, { id });
|
|
81
|
+
spinner.succeed(chalk.green('API key deleted.'));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
spinner.fail(`Failed to delete key: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import Table from 'cli-table3';
|
|
3
|
+
import { graphqlRequest } from '../utils/graphql.js';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
export function registerMcpCommands(program) {
|
|
6
|
+
const mcp = program.command('mcp').description('Model Context Protocol (MCP) management');
|
|
7
|
+
mcp
|
|
8
|
+
.command('agent <agentId>')
|
|
9
|
+
.description('List MCP servers configured for an agent')
|
|
10
|
+
.action(async (agentId) => {
|
|
11
|
+
const globalOptions = program.opts();
|
|
12
|
+
const spinner = !globalOptions.json ? ora('Checking MCP status...').start() : null;
|
|
13
|
+
try {
|
|
14
|
+
const query = `
|
|
15
|
+
query($id: ID!) {
|
|
16
|
+
agentWithFlow(id: $id) {
|
|
17
|
+
flowNodes {
|
|
18
|
+
nodeId
|
|
19
|
+
nodeType
|
|
20
|
+
data
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
const data = await graphqlRequest(query, { id: agentId });
|
|
26
|
+
const mcpNodes = data.agentWithFlow.flowNodes.filter((n) => n.nodeType === 'custom_mcp');
|
|
27
|
+
if (spinner)
|
|
28
|
+
spinner.stop();
|
|
29
|
+
if (globalOptions.json) {
|
|
30
|
+
console.log(JSON.stringify(mcpNodes, null, 2));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (mcpNodes.length === 0) {
|
|
34
|
+
console.log(chalk.yellow('No MCP nodes found for this agent.'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const table = new Table({
|
|
38
|
+
head: [chalk.cyan('Node ID'), chalk.cyan('Type'), chalk.cyan('Preset/Label'), chalk.cyan('Status')],
|
|
39
|
+
colWidths: [36, 20, 20, 15]
|
|
40
|
+
});
|
|
41
|
+
mcpNodes.forEach((n) => {
|
|
42
|
+
const nodeData = n.data || {};
|
|
43
|
+
const status = nodeData.connectionStatus || 'unknown';
|
|
44
|
+
const label = nodeData.title || n.nodeType;
|
|
45
|
+
table.push([n.nodeId, n.nodeType, label, status === 'connected' ? chalk.green(status) : chalk.red(status)]);
|
|
46
|
+
});
|
|
47
|
+
console.log(table.toString());
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (spinner)
|
|
51
|
+
spinner.fail(`Failed: ${error.message}`);
|
|
52
|
+
else
|
|
53
|
+
console.error(JSON.stringify({ error: error.message }));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import Table from 'cli-table3';
|
|
3
|
+
import { graphqlRequest } from '../utils/graphql.js';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
export function registerMemberCommands(program) {
|
|
6
|
+
const member = program.command('member').description('Member management');
|
|
7
|
+
member
|
|
8
|
+
.command('list <workspaceId>')
|
|
9
|
+
.description('List all members of a workspace')
|
|
10
|
+
.action(async (workspaceId) => {
|
|
11
|
+
const globalOptions = program.opts();
|
|
12
|
+
const spinner = !globalOptions.json ? ora('Fetching members...').start() : null;
|
|
13
|
+
try {
|
|
14
|
+
const query = `
|
|
15
|
+
query($workspaceId: ID!) {
|
|
16
|
+
workspaceMembers(workspaceId: $workspaceId) {
|
|
17
|
+
id
|
|
18
|
+
role
|
|
19
|
+
joinedAt
|
|
20
|
+
user {
|
|
21
|
+
id
|
|
22
|
+
name
|
|
23
|
+
email
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
const data = await graphqlRequest(query, { workspaceId });
|
|
29
|
+
const members = data.workspaceMembers;
|
|
30
|
+
if (spinner)
|
|
31
|
+
spinner.stop();
|
|
32
|
+
if (globalOptions.json) {
|
|
33
|
+
console.log(JSON.stringify(members, null, 2));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (members.length === 0) {
|
|
37
|
+
console.log(chalk.yellow('No members found.'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const table = new Table({
|
|
41
|
+
head: [chalk.cyan('Member ID'), chalk.cyan('Name'), chalk.cyan('Email'), chalk.cyan('Role'), chalk.cyan('Joined')],
|
|
42
|
+
colWidths: [36, 20, 30, 10, 25]
|
|
43
|
+
});
|
|
44
|
+
members.forEach((m) => {
|
|
45
|
+
table.push([m.user.id, m.user.name || 'N/A', m.user.email, m.role, m.joinedAt]);
|
|
46
|
+
});
|
|
47
|
+
console.log(table.toString());
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (spinner)
|
|
51
|
+
spinner.fail(`Failed to fetch members: ${error.message}`);
|
|
52
|
+
else
|
|
53
|
+
console.error(JSON.stringify({ error: error.message }));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
member
|
|
57
|
+
.command('update <workspaceId> <memberId>')
|
|
58
|
+
.description('Update a member role (OWNER, EDITOR, VIEWER)')
|
|
59
|
+
.option('-r, --role <role>', 'New role')
|
|
60
|
+
.action(async (workspaceId, memberId, options) => {
|
|
61
|
+
if (!options.role) {
|
|
62
|
+
console.error(chalk.red('Role is required. Use -r <role>'));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const spinner = ora('Updating member role...').start();
|
|
66
|
+
try {
|
|
67
|
+
const mutation = `
|
|
68
|
+
mutation($memberId: ID!, $input: UpdateWorkspaceMemberRoleInput!) {
|
|
69
|
+
updateWorkspaceMemberRole(memberId: $memberId, input: $input) {
|
|
70
|
+
id
|
|
71
|
+
role
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
await graphqlRequest(mutation, {
|
|
76
|
+
memberId,
|
|
77
|
+
input: { role: options.role.toUpperCase() }
|
|
78
|
+
});
|
|
79
|
+
spinner.succeed(chalk.green(`Member role updated to ${options.role.toUpperCase()}.`));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
spinner.fail(`Failed to update member: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
member
|
|
86
|
+
.command('remove <workspaceId> <memberId>')
|
|
87
|
+
.description('Remove a member from a workspace')
|
|
88
|
+
.action(async (workspaceId, memberId) => {
|
|
89
|
+
const spinner = ora('Removing member...').start();
|
|
90
|
+
try {
|
|
91
|
+
const mutation = `
|
|
92
|
+
mutation($memberId: ID!) {
|
|
93
|
+
removeWorkspaceMember(memberId: $memberId)
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
await graphqlRequest(mutation, { memberId });
|
|
97
|
+
spinner.succeed(chalk.green('Member removed from workspace.'));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
spinner.fail(`Failed to remove member: ${error.message}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import config from '../utils/config.js';
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
|
+
export function registerProfileCommands(program) {
|
|
5
|
+
const profile = program.command('profile').description('Environment profiles management');
|
|
6
|
+
profile
|
|
7
|
+
.command('list')
|
|
8
|
+
.description('List all profiles')
|
|
9
|
+
.action(() => {
|
|
10
|
+
const profiles = config.get('profiles') || {};
|
|
11
|
+
const current = config.get('currentProfile');
|
|
12
|
+
if (Object.keys(profiles).length === 0) {
|
|
13
|
+
console.log(chalk.yellow('No profiles found. Use hsafa profile add <name> <url>'));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const table = new Table({
|
|
17
|
+
head: [chalk.cyan('Active'), chalk.cyan('Name'), chalk.cyan('Server URL')],
|
|
18
|
+
colWidths: [10, 20, 40]
|
|
19
|
+
});
|
|
20
|
+
Object.entries(profiles).forEach(([name, data]) => {
|
|
21
|
+
table.push([name === current ? chalk.green(' * ') : '', name, data.serverUrl]);
|
|
22
|
+
});
|
|
23
|
+
console.log(table.toString());
|
|
24
|
+
});
|
|
25
|
+
profile
|
|
26
|
+
.command('add <name> <url>')
|
|
27
|
+
.description('Add a new profile')
|
|
28
|
+
.action((name, url) => {
|
|
29
|
+
const profiles = config.get('profiles') || {};
|
|
30
|
+
profiles[name] = { serverUrl: url };
|
|
31
|
+
config.set('profiles', profiles);
|
|
32
|
+
console.log(chalk.green(`Profile "${name}" added.`));
|
|
33
|
+
});
|
|
34
|
+
profile
|
|
35
|
+
.command('use <name>')
|
|
36
|
+
.description('Switch to a profile')
|
|
37
|
+
.action((name) => {
|
|
38
|
+
const profiles = config.get('profiles') || {};
|
|
39
|
+
if (!profiles[name]) {
|
|
40
|
+
console.error(chalk.red(`Profile "${name}" not found.`));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const current = profiles[name];
|
|
44
|
+
config.set('currentProfile', name);
|
|
45
|
+
config.set('serverUrl', current.serverUrl);
|
|
46
|
+
config.set('token', current.token || '');
|
|
47
|
+
console.log(chalk.green(`Switched to profile "${name}" (${current.serverUrl})`));
|
|
48
|
+
});
|
|
49
|
+
profile
|
|
50
|
+
.command('delete <name>')
|
|
51
|
+
.description('Delete a profile')
|
|
52
|
+
.action((name) => {
|
|
53
|
+
const profiles = config.get('profiles') || {};
|
|
54
|
+
if (!profiles[name]) {
|
|
55
|
+
console.error(chalk.red(`Profile "${name}" not found.`));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
delete profiles[name];
|
|
59
|
+
config.set('profiles', profiles);
|
|
60
|
+
if (config.get('currentProfile') === name) {
|
|
61
|
+
config.delete('currentProfile');
|
|
62
|
+
}
|
|
63
|
+
console.log(chalk.green(`Profile "${name}" deleted.`));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { graphqlRequest } from '../utils/graphql.js';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
export function registerProjectCommands(program) {
|
|
9
|
+
const project = program.command('project').description('Project management');
|
|
10
|
+
project
|
|
11
|
+
.command('init <name>')
|
|
12
|
+
.description('Initialize a new project with a template')
|
|
13
|
+
.option('-w, --workspace <id>', 'Workspace ID (if not provided, will use the first one)')
|
|
14
|
+
.option('-t, --template <name>', 'Template name (basic-assistant, researcher)', 'basic-assistant')
|
|
15
|
+
.action(async (name, options) => {
|
|
16
|
+
const spinner = ora('Initializing project...').start();
|
|
17
|
+
try {
|
|
18
|
+
// 1. Get Workspace ID
|
|
19
|
+
let workspaceId = options.workspace;
|
|
20
|
+
if (!workspaceId) {
|
|
21
|
+
const workspacesQuery = `query { workspaces { id name } }`;
|
|
22
|
+
const wsData = await graphqlRequest(workspacesQuery);
|
|
23
|
+
if (wsData.workspaces.length === 0) {
|
|
24
|
+
spinner.fail('No workspaces found. Create a workspace first using the UI or GraphQL.');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
workspaceId = wsData.workspaces[0].id;
|
|
28
|
+
spinner.info(`Using workspace: ${wsData.workspaces[0].name} (${workspaceId})`);
|
|
29
|
+
spinner.start('Creating project...');
|
|
30
|
+
}
|
|
31
|
+
// 2. Create Project
|
|
32
|
+
const createProjectMutation = `
|
|
33
|
+
mutation($input: CreateProjectInput!) {
|
|
34
|
+
createProject(input: $input) {
|
|
35
|
+
id
|
|
36
|
+
name
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
// Note: The GraphQL schema for createProject might vary, let's assume it takes workspaceId
|
|
41
|
+
// Looking at current server code, projects are often created within a workspace scope.
|
|
42
|
+
// However, I'll use the createAgent mutation which we know works well.
|
|
43
|
+
// Let's create an agent directly in the workspace as per current hsafa server structure
|
|
44
|
+
const createAgentMutation = `
|
|
45
|
+
mutation($workspaceId: ID!, $input: CreateAgentInput!) {
|
|
46
|
+
createAgent(workspaceId: $workspaceId, input: $input) {
|
|
47
|
+
id
|
|
48
|
+
name
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
const agentData = await graphqlRequest(createAgentMutation, {
|
|
53
|
+
workspaceId,
|
|
54
|
+
input: {
|
|
55
|
+
name,
|
|
56
|
+
description: `Project initialized from ${options.template} template`,
|
|
57
|
+
type: 'AI Agent'
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const agentId = agentData.createAgent.id;
|
|
61
|
+
spinner.text = `Applying template ${options.template}...`;
|
|
62
|
+
// 3. Load Template
|
|
63
|
+
const templatePath = path.join(__dirname, '..', 'templates', `${options.template}.json`);
|
|
64
|
+
if (!fs.existsSync(templatePath)) {
|
|
65
|
+
spinner.fail(`Template ${options.template} not found.`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
|
|
69
|
+
// 4. Apply Template Flow
|
|
70
|
+
const saveFlowMutation = `
|
|
71
|
+
mutation($agentId: ID!, $input: SaveAgentFlowInput!) {
|
|
72
|
+
saveAgentFlow(agentId: $agentId, input: $input)
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
await graphqlRequest(saveFlowMutation, {
|
|
76
|
+
agentId,
|
|
77
|
+
input: {
|
|
78
|
+
nodes: template.nodes,
|
|
79
|
+
edges: template.edges
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
spinner.succeed(chalk.green(`Project "${name}" initialized successfully!`));
|
|
83
|
+
console.log(`\nAgent ID: ${chalk.cyan(agentId)}`);
|
|
84
|
+
console.log(`Run chat: ${chalk.bold(`hsafa agent chat ${agentId}`)}`);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
spinner.fail(`Initialization failed: ${error.message}`);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import api from '../utils/api.js';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import Table from 'cli-table3';
|
|
5
|
+
export function registerSystemCommands(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('status')
|
|
8
|
+
.description('Check HSAFA system status')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
const spinner = ora('Checking system status...').start();
|
|
11
|
+
try {
|
|
12
|
+
const response = await api.get('/api/health');
|
|
13
|
+
const data = response.data;
|
|
14
|
+
spinner.stop();
|
|
15
|
+
console.log(chalk.bold('\nHSAFA System Status:'));
|
|
16
|
+
const table = new Table();
|
|
17
|
+
table.push({ 'Status': data.status === 'healthy' ? chalk.green(data.status) : chalk.red(data.status) }, { 'Timestamp': data.timestamp }, { 'Database': data.database === 'connected' ? chalk.green(data.database) : chalk.red(data.database) }, { 'Server': chalk.green(data.server) });
|
|
18
|
+
if (data.error) {
|
|
19
|
+
table.push({ 'Error': chalk.red(data.error) });
|
|
20
|
+
}
|
|
21
|
+
console.log(table.toString());
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
spinner.fail(`Failed to check status: ${error.message}`);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { graphqlRequest } from '../utils/graphql.js';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import Table from 'cli-table3';
|
|
5
|
+
export function registerUserCommands(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('me')
|
|
8
|
+
.description('Show current user profile')
|
|
9
|
+
.action(async () => {
|
|
10
|
+
const globalOptions = program.opts();
|
|
11
|
+
const spinner = !globalOptions.json ? ora('Fetching profile...').start() : null;
|
|
12
|
+
try {
|
|
13
|
+
const query = `
|
|
14
|
+
query {
|
|
15
|
+
me {
|
|
16
|
+
id
|
|
17
|
+
email
|
|
18
|
+
name
|
|
19
|
+
avatarUrl
|
|
20
|
+
createdAt
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
const data = await graphqlRequest(query);
|
|
25
|
+
const user = data.me;
|
|
26
|
+
if (spinner)
|
|
27
|
+
spinner.stop();
|
|
28
|
+
if (globalOptions.json) {
|
|
29
|
+
console.log(JSON.stringify(user, null, 2));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!user) {
|
|
33
|
+
console.log(chalk.red('Not logged in or user not found.'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const table = new Table();
|
|
37
|
+
table.push({ 'ID': user.id }, { 'Name': user.name || 'N/A' }, { 'Email': user.email }, { 'Created At': user.createdAt });
|
|
38
|
+
console.log(chalk.bold('\nYour Profile:'));
|
|
39
|
+
console.log(table.toString());
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (spinner)
|
|
43
|
+
spinner.fail(`Failed to fetch profile: ${error.message}`);
|
|
44
|
+
else
|
|
45
|
+
console.error(JSON.stringify({ error: error.message }));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|