@toothfairyai/cli 1.0.13 → 1.0.14
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 +4 -4
- package/bin/toothfairy.js +2682 -384
- package/package.json +3 -3
- package/src/api.js +1314 -119
- package/src/config.js +186 -117
package/src/config.js
CHANGED
|
@@ -4,147 +4,216 @@ const os = require('os');
|
|
|
4
4
|
const yaml = require('js-yaml');
|
|
5
5
|
|
|
6
6
|
class ToothFairyConfig {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
constructor(baseUrl, aiUrl, aiStreamUrl, apiKey, workspaceId, userId) {
|
|
8
|
+
this.baseUrl = baseUrl;
|
|
9
|
+
this.aiUrl = aiUrl;
|
|
10
|
+
this.aiStreamUrl = aiStreamUrl;
|
|
11
|
+
this.apiKey = apiKey;
|
|
12
|
+
this.workspaceId = workspaceId;
|
|
13
|
+
this.userId = userId;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static fromEnv() {
|
|
17
|
+
return new ToothFairyConfig(
|
|
18
|
+
process.env.TF_BASE_URL || 'https://api.toothfairyai.com',
|
|
19
|
+
process.env.TF_AI_URL || 'https://ai.toothfairyai.com',
|
|
20
|
+
process.env.TF_AI_STREAM_URL || 'https://ais.toothfairyai.com',
|
|
21
|
+
process.env.TF_API_KEY || '',
|
|
22
|
+
process.env.TF_WORKSPACE_ID || '',
|
|
23
|
+
process.env.TF_USER_ID || ''
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static fromFile(configPath) {
|
|
28
|
+
if (!fs.existsSync(configPath)) {
|
|
29
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
13
30
|
}
|
|
14
31
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
process.env.TF_BASE_URL || 'https://api.toothfairyai.com',
|
|
18
|
-
process.env.TF_AI_URL || 'https://ai.toothfairyai.com',
|
|
19
|
-
process.env.TF_AI_STREAM_URL || 'https://ais.toothfairyai.com',
|
|
20
|
-
process.env.TF_API_KEY || '',
|
|
21
|
-
process.env.TF_WORKSPACE_ID || ''
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static fromFile(configPath) {
|
|
26
|
-
if (!fs.existsSync(configPath)) {
|
|
27
|
-
throw new Error(`Config file not found: ${configPath}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const content = fs.readFileSync(configPath, 'utf8');
|
|
31
|
-
let data;
|
|
32
|
-
|
|
33
|
-
if (configPath.endsWith('.yml') || configPath.endsWith('.yaml')) {
|
|
34
|
-
data = yaml.load(content);
|
|
35
|
-
} else {
|
|
36
|
-
data = JSON.parse(content);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return new ToothFairyConfig(
|
|
40
|
-
data.base_url || 'https://api.toothfairyai.com',
|
|
41
|
-
data.ai_url || 'https://ai.toothfairyai.com',
|
|
42
|
-
data.ai_stream_url || 'https://ais.toothfairyai.com',
|
|
43
|
-
data.api_key || '',
|
|
44
|
-
data.workspace_id || ''
|
|
45
|
-
);
|
|
46
|
-
}
|
|
32
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
33
|
+
let data;
|
|
47
34
|
|
|
48
|
-
|
|
49
|
-
|
|
35
|
+
if (configPath.endsWith('.yml') || configPath.endsWith('.yaml')) {
|
|
36
|
+
data = yaml.load(content);
|
|
37
|
+
} else {
|
|
38
|
+
data = JSON.parse(content);
|
|
50
39
|
}
|
|
51
40
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
41
|
+
return new ToothFairyConfig(
|
|
42
|
+
data.base_url || 'https://api.toothfairyai.com',
|
|
43
|
+
data.ai_url || 'https://ai.toothfairyai.com',
|
|
44
|
+
data.ai_stream_url || 'https://ais.toothfairyai.com',
|
|
45
|
+
data.api_key || '',
|
|
46
|
+
data.workspace_id || '',
|
|
47
|
+
data.user_id || ''
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
validate() {
|
|
52
|
+
return !!(this.apiKey && this.workspaceId);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
validateForDocumentOperations() {
|
|
56
|
+
return !!(this.apiKey && this.workspaceId && this.userId);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
toObject() {
|
|
60
|
+
return {
|
|
61
|
+
base_url: this.baseUrl,
|
|
62
|
+
ai_url: this.aiUrl,
|
|
63
|
+
ai_stream_url: this.aiStreamUrl,
|
|
64
|
+
api_key: this.apiKey,
|
|
65
|
+
workspace_id: this.workspaceId,
|
|
66
|
+
user_id: this.userId,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
function getConfigPath() {
|
|
64
|
-
|
|
72
|
+
return path.join(os.homedir(), '.toothfairy', 'config.yml');
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
function loadConfig(configPath = null) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
76
|
+
// Priority order:
|
|
77
|
+
// 1. Provided config file path
|
|
78
|
+
// 2. Default config file (~/.toothfairy/config.yml)
|
|
79
|
+
// 3. Environment variables
|
|
80
|
+
|
|
81
|
+
if (configPath) {
|
|
82
|
+
return ToothFairyConfig.fromFile(configPath);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const defaultConfig = getConfigPath();
|
|
86
|
+
if (fs.existsSync(defaultConfig)) {
|
|
87
|
+
return ToothFairyConfig.fromFile(defaultConfig);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const config = ToothFairyConfig.fromEnv();
|
|
91
|
+
if (!config.validate()) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
'Configuration incomplete. Please provide configuration via:\n' +
|
|
94
|
+
'1. Config file at ~/.toothfairy/config.yml\n' +
|
|
95
|
+
'2. Environment variables: TF_API_KEY, TF_WORKSPACE_ID\n' +
|
|
96
|
+
'3. CLI arguments\n' +
|
|
97
|
+
'Note: TF_BASE_URL and TF_AI_URL default to production endpoints'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return config;
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
function saveConfig(config, configPath = null) {
|
|
97
|
-
|
|
98
|
-
|
|
105
|
+
const filePath = configPath || getConfigPath();
|
|
106
|
+
const dir = path.dirname(filePath);
|
|
99
107
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
108
|
+
// Create directory if it doesn't exist
|
|
109
|
+
if (!fs.existsSync(dir)) {
|
|
110
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
111
|
+
}
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
const content = yaml.dump(config.toObject());
|
|
114
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
function validateConfiguration(config) {
|
|
110
|
-
|
|
111
|
-
|
|
118
|
+
const missingFields = [];
|
|
119
|
+
|
|
120
|
+
if (!config.apiKey) {
|
|
121
|
+
missingFields.push('API Key');
|
|
122
|
+
}
|
|
123
|
+
if (!config.workspaceId) {
|
|
124
|
+
missingFields.push('Workspace ID');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (missingFields.length > 0) {
|
|
128
|
+
const missingStr = missingFields.join(' and ');
|
|
129
|
+
const chalk = require('chalk');
|
|
130
|
+
|
|
131
|
+
console.error(
|
|
132
|
+
chalk.red(`Error: Missing required configuration: ${missingStr}`)
|
|
133
|
+
);
|
|
134
|
+
console.error();
|
|
135
|
+
console.error(chalk.yellow('To fix this, run the configure command:'));
|
|
136
|
+
console.error(
|
|
137
|
+
chalk.dim(
|
|
138
|
+
'tf configure --api-key YOUR_API_KEY --workspace-id YOUR_WORKSPACE_ID'
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
console.error();
|
|
142
|
+
console.error(chalk.yellow('Or set environment variables:'));
|
|
143
|
+
if (!config.apiKey) {
|
|
144
|
+
console.error(chalk.dim('export TF_API_KEY=\'your-api-key\''));
|
|
145
|
+
}
|
|
146
|
+
if (!config.workspaceId) {
|
|
147
|
+
console.error(chalk.dim('export TF_WORKSPACE_ID=\'your-workspace-id\''));
|
|
148
|
+
}
|
|
149
|
+
console.error();
|
|
150
|
+
console.error(
|
|
151
|
+
chalk.yellow('Or create a config file at ~/.toothfairy/config.yml:')
|
|
152
|
+
);
|
|
153
|
+
console.error(chalk.dim('api_key: your-api-key'));
|
|
154
|
+
console.error(chalk.dim('workspace_id: your-workspace-id'));
|
|
155
|
+
|
|
156
|
+
throw new Error(`Configuration incomplete: missing ${missingStr}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function validateDocumentConfiguration(config) {
|
|
161
|
+
const missingFields = [];
|
|
162
|
+
|
|
163
|
+
if (!config.apiKey) {
|
|
164
|
+
missingFields.push('API Key');
|
|
165
|
+
}
|
|
166
|
+
if (!config.workspaceId) {
|
|
167
|
+
missingFields.push('Workspace ID');
|
|
168
|
+
}
|
|
169
|
+
if (!config.userId) {
|
|
170
|
+
missingFields.push('User ID');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (missingFields.length > 0) {
|
|
174
|
+
const missingStr = missingFields.join(' and ');
|
|
175
|
+
const chalk = require('chalk');
|
|
176
|
+
|
|
177
|
+
console.error(
|
|
178
|
+
chalk.red(
|
|
179
|
+
`Error: Missing required configuration for document operations: ${missingStr}`
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
console.error();
|
|
183
|
+
console.error(chalk.yellow('To fix this, run the configure command:'));
|
|
184
|
+
console.error(
|
|
185
|
+
chalk.dim(
|
|
186
|
+
'tf configure --api-key YOUR_API_KEY --workspace-id YOUR_WORKSPACE_ID --user-id YOUR_USER_ID'
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
console.error();
|
|
190
|
+
console.error(chalk.yellow('Or set environment variables:'));
|
|
112
191
|
if (!config.apiKey) {
|
|
113
|
-
|
|
192
|
+
console.error(chalk.dim('export TF_API_KEY=\'your-api-key\''));
|
|
114
193
|
}
|
|
115
194
|
if (!config.workspaceId) {
|
|
116
|
-
|
|
195
|
+
console.error(chalk.dim('export TF_WORKSPACE_ID=\'your-workspace-id\''));
|
|
117
196
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const missingStr = missingFields.join(' and ');
|
|
121
|
-
const chalk = require('chalk');
|
|
122
|
-
|
|
123
|
-
console.error(chalk.red(`Error: Missing required configuration: ${missingStr}`));
|
|
124
|
-
console.error();
|
|
125
|
-
console.error(chalk.yellow('To fix this, run the configure command:'));
|
|
126
|
-
console.error(chalk.dim('tf configure --api-key YOUR_API_KEY --workspace-id YOUR_WORKSPACE_ID'));
|
|
127
|
-
console.error();
|
|
128
|
-
console.error(chalk.yellow('Or set environment variables:'));
|
|
129
|
-
if (!config.apiKey) {
|
|
130
|
-
console.error(chalk.dim('export TF_API_KEY=\'your-api-key\''));
|
|
131
|
-
}
|
|
132
|
-
if (!config.workspaceId) {
|
|
133
|
-
console.error(chalk.dim('export TF_WORKSPACE_ID=\'your-workspace-id\''));
|
|
134
|
-
}
|
|
135
|
-
console.error();
|
|
136
|
-
console.error(chalk.yellow('Or create a config file at ~/.toothfairy/config.yml:'));
|
|
137
|
-
console.error(chalk.dim('api_key: your-api-key'));
|
|
138
|
-
console.error(chalk.dim('workspace_id: your-workspace-id'));
|
|
139
|
-
|
|
140
|
-
throw new Error(`Configuration incomplete: missing ${missingStr}`);
|
|
197
|
+
if (!config.userId) {
|
|
198
|
+
console.error(chalk.dim('export TF_USER_ID=\'your-user-id\''));
|
|
141
199
|
}
|
|
200
|
+
console.error();
|
|
201
|
+
console.error(
|
|
202
|
+
chalk.yellow('Or create a config file at ~/.toothfairy/config.yml:')
|
|
203
|
+
);
|
|
204
|
+
console.error(chalk.dim('api_key: your-api-key'));
|
|
205
|
+
console.error(chalk.dim('workspace_id: your-workspace-id'));
|
|
206
|
+
console.error(chalk.dim('user_id: your-user-id'));
|
|
207
|
+
|
|
208
|
+
throw new Error(`Configuration incomplete: missing ${missingStr}`);
|
|
209
|
+
}
|
|
142
210
|
}
|
|
143
211
|
|
|
144
212
|
module.exports = {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
213
|
+
ToothFairyConfig,
|
|
214
|
+
loadConfig,
|
|
215
|
+
saveConfig,
|
|
216
|
+
getConfigPath,
|
|
217
|
+
validateConfiguration,
|
|
218
|
+
validateDocumentConfiguration,
|
|
219
|
+
};
|