mindsim 0.1.1 → 0.1.3
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 +92 -2
- package/dist/auth.d.ts +0 -3
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +184 -93
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +92 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -12
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/mind-topics.d.ts +11 -0
- package/dist/resources/mind-topics.d.ts.map +1 -0
- package/dist/resources/mind-topics.js +18 -0
- package/dist/resources/mind-topics.js.map +1 -0
- package/dist/resources/snapshots.d.ts +29 -1
- package/dist/resources/snapshots.d.ts.map +1 -1
- package/dist/resources/snapshots.js +30 -0
- package/dist/resources/snapshots.js.map +1 -1
- package/dist/resources/users.d.ts +14 -0
- package/dist/resources/users.d.ts.map +1 -0
- package/dist/resources/users.js +20 -0
- package/dist/resources/users.js.map +1 -0
- package/dist/types.d.ts +78 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/auth.ts +261 -105
- package/src/cli.ts +93 -2
- package/src/config.ts +11 -11
- package/src/index.ts +6 -0
- package/src/resources/mind-topics.ts +16 -0
- package/src/resources/snapshots.ts +60 -0
- package/src/resources/users.ts +16 -0
- package/src/types.ts +88 -7
- package/tests/config.test.ts +20 -15
- package/tests/resources/mind-topics.test.ts +69 -0
- package/tests/resources/snapshots.test.ts +46 -0
- package/tests/resources/users.test.ts +54 -0
- package/tests/version.test.ts +1 -1
package/src/auth.ts
CHANGED
|
@@ -1,131 +1,287 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import readline from "node:readline";
|
|
2
|
+
import axios from "axios";
|
|
3
3
|
import open from "open";
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
4
|
+
import { getDeviceAuthConfig, saveApiKey } from "./config";
|
|
5
|
+
import type {
|
|
6
|
+
DeviceAuthResponse,
|
|
7
|
+
SdkKeyDetailResponse,
|
|
8
|
+
SdkKeyInfo,
|
|
9
|
+
SdkKeysResponse,
|
|
10
|
+
TokenResponse,
|
|
11
|
+
} from "./types";
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
const authConfig = getAuthConfig();
|
|
13
|
+
const HTTP_TIMEOUT = 30000;
|
|
9
14
|
|
|
10
|
-
/**
|
|
11
|
-
* Starts the login flow
|
|
12
|
-
*/
|
|
13
15
|
export async function login(): Promise<void> {
|
|
14
|
-
|
|
16
|
+
const authConfig = getDeviceAuthConfig();
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
const apiKey = await listenForCallback();
|
|
18
|
+
console.log("Initiating authentication...\n");
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
const deviceAuth = await requestDeviceCode(authConfig);
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
console.log("To authenticate, visit the following URL:");
|
|
23
|
+
console.log(`\n ${deviceAuth.verification_uri_complete}\n`);
|
|
24
|
+
console.log(`Your code: ${deviceAuth.user_code}\n`);
|
|
25
|
+
|
|
26
|
+
await open(deviceAuth.verification_uri_complete);
|
|
27
|
+
|
|
28
|
+
console.log("Waiting for authentication...");
|
|
29
|
+
|
|
30
|
+
const tokenResponse = await pollForToken(
|
|
31
|
+
authConfig,
|
|
32
|
+
deviceAuth.device_code,
|
|
33
|
+
deviceAuth.interval,
|
|
34
|
+
deviceAuth.expires_in,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
console.log("\nAuthentication successful!");
|
|
38
|
+
|
|
39
|
+
console.log("Fetching your API keys...\n");
|
|
40
|
+
const keysResponse = await fetchUserKeys(authConfig, tokenResponse.access_token);
|
|
41
|
+
|
|
42
|
+
if (keysResponse.keys.length === 0) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"No API keys found. Please visit https://app.workflows.com and go to the Developer Portal to create an app and API key.",
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let selectedKeyId: string;
|
|
49
|
+
if (keysResponse.keys.length === 1) {
|
|
50
|
+
const onlyKey = keysResponse.keys[0] as SdkKeyInfo;
|
|
51
|
+
selectedKeyId = onlyKey.id;
|
|
52
|
+
console.log(`Auto-selecting your only key: ${onlyKey.name}`);
|
|
53
|
+
} else {
|
|
54
|
+
selectedKeyId = await selectKey(keysResponse.keys);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const keyDetail = await fetchKeySecret(authConfig, tokenResponse.access_token, selectedKeyId);
|
|
58
|
+
|
|
59
|
+
saveApiKey(keyDetail.key.key);
|
|
60
|
+
|
|
61
|
+
console.log("\n✅ Successfully logged in! Credentials saved.");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface AuthConfig {
|
|
65
|
+
deviceAuthUrl: string;
|
|
66
|
+
tokenUrl: string;
|
|
67
|
+
clientId: string;
|
|
68
|
+
sdkKeysApiUrl: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function requestDeviceCode(authConfig: AuthConfig): Promise<DeviceAuthResponse> {
|
|
22
72
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
73
|
+
const response = await axios.post<DeviceAuthResponse>(
|
|
74
|
+
authConfig.deviceAuthUrl,
|
|
75
|
+
new URLSearchParams({
|
|
76
|
+
client_id: authConfig.clientId,
|
|
77
|
+
}).toString(),
|
|
78
|
+
{
|
|
79
|
+
headers: {
|
|
80
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
81
|
+
},
|
|
82
|
+
timeout: HTTP_TIMEOUT,
|
|
83
|
+
},
|
|
84
|
+
);
|
|
25
85
|
|
|
26
|
-
|
|
86
|
+
return response.data;
|
|
27
87
|
} catch (error) {
|
|
28
|
-
|
|
29
|
-
|
|
88
|
+
if (axios.isAxiosError(error)) {
|
|
89
|
+
if (error.code === "ECONNABORTED") {
|
|
90
|
+
throw new Error("Device authorization request timed out. Please try again.");
|
|
91
|
+
}
|
|
92
|
+
if (error.response) {
|
|
93
|
+
const data = error.response.data as { error?: string; error_description?: string };
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Device authorization failed: ${data.error_description || data.error || "Unknown error"}`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
30
100
|
}
|
|
31
101
|
}
|
|
32
102
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
103
|
+
async function pollForToken(
|
|
104
|
+
authConfig: AuthConfig,
|
|
105
|
+
deviceCode: string,
|
|
106
|
+
interval: number,
|
|
107
|
+
expiresIn: number,
|
|
108
|
+
): Promise<TokenResponse> {
|
|
109
|
+
const startTime = Date.now();
|
|
110
|
+
const expiresAtMs = startTime + expiresIn * 1000;
|
|
111
|
+
let currentInterval = interval;
|
|
112
|
+
|
|
113
|
+
while (Date.now() < expiresAtMs) {
|
|
114
|
+
await sleep(currentInterval * 1000);
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const response = await axios.post<TokenResponse>(
|
|
118
|
+
authConfig.tokenUrl,
|
|
119
|
+
new URLSearchParams({
|
|
120
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
121
|
+
device_code: deviceCode,
|
|
122
|
+
client_id: authConfig.clientId,
|
|
123
|
+
}).toString(),
|
|
124
|
+
{
|
|
125
|
+
headers: {
|
|
126
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
127
|
+
},
|
|
128
|
+
timeout: HTTP_TIMEOUT,
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return response.data;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (axios.isAxiosError(error) && error.response) {
|
|
135
|
+
const errorData = error.response.data as { error?: string };
|
|
136
|
+
const errorCode = errorData?.error;
|
|
137
|
+
|
|
138
|
+
if (errorCode === "authorization_pending") {
|
|
139
|
+
continue;
|
|
59
140
|
}
|
|
60
141
|
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
req.on("data", (chunk) => {
|
|
65
|
-
body += chunk.toString(); // Collect data chunks
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
req.on("end", () => {
|
|
69
|
-
let authResponseData: AuthResponse | null = null;
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
authResponseData = JSON.parse(body);
|
|
73
|
-
} catch (err) {
|
|
74
|
-
console.error(`Got an invalid response: ${body}. Reason: ${err}`);
|
|
75
|
-
reject(err);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (!authResponseData) {
|
|
79
|
-
res.end("No api key found");
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const apiKey = authResponseData?.apiKey?.key;
|
|
84
|
-
|
|
85
|
-
if (!apiKey) {
|
|
86
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
87
|
-
res.end(`<h1>Authentication Error</h1><p>API Key Not Found</p>`);
|
|
88
|
-
server.close();
|
|
89
|
-
reject(new Error("API Key Not Found"));
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (apiKey) {
|
|
94
|
-
// Send a nice "Close this window" message to the user
|
|
95
|
-
res.writeHead(200, { "Content-Type": "text/html", ...headers });
|
|
96
|
-
res.end(`
|
|
97
|
-
<h1>Authentication Successful</h1>
|
|
98
|
-
<p>You can close this window and return to your terminal.</p>
|
|
99
|
-
<script>window.close()</script>
|
|
100
|
-
`);
|
|
101
|
-
|
|
102
|
-
server.close();
|
|
103
|
-
resolve(apiKey);
|
|
104
|
-
} else {
|
|
105
|
-
res.end("No code found");
|
|
106
|
-
}
|
|
107
|
-
});
|
|
142
|
+
if (errorCode === "slow_down") {
|
|
143
|
+
currentInterval += 1;
|
|
144
|
+
continue;
|
|
108
145
|
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
146
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
147
|
+
if (errorCode === "expired_token") {
|
|
148
|
+
throw new Error("Authentication timed out. Please try again.");
|
|
149
|
+
}
|
|
116
150
|
|
|
117
|
-
|
|
151
|
+
if (errorCode === "access_denied") {
|
|
152
|
+
throw new Error("Authentication was denied. Please try again.");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
118
155
|
|
|
119
|
-
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
120
159
|
|
|
121
|
-
|
|
122
|
-
|
|
160
|
+
throw new Error("Authentication timed out. Please try again.");
|
|
161
|
+
}
|
|
123
162
|
|
|
124
|
-
|
|
163
|
+
async function fetchUserKeys(
|
|
164
|
+
authConfig: AuthConfig,
|
|
165
|
+
accessToken: string,
|
|
166
|
+
): Promise<SdkKeysResponse> {
|
|
167
|
+
try {
|
|
168
|
+
const response = await axios.get<SdkKeysResponse>(authConfig.sdkKeysApiUrl, {
|
|
169
|
+
headers: {
|
|
170
|
+
Authorization: `Bearer ${accessToken}`,
|
|
171
|
+
},
|
|
172
|
+
timeout: HTTP_TIMEOUT,
|
|
125
173
|
});
|
|
126
174
|
|
|
127
|
-
|
|
128
|
-
|
|
175
|
+
return response.data;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (axios.isAxiosError(error)) {
|
|
178
|
+
if (error.code === "ECONNABORTED") {
|
|
179
|
+
throw new Error("Request to fetch API keys timed out. Please try again.");
|
|
180
|
+
}
|
|
181
|
+
if (error.response) {
|
|
182
|
+
const status = error.response.status;
|
|
183
|
+
const data = error.response.data as { error?: string; code?: string };
|
|
184
|
+
|
|
185
|
+
if (status === 401) {
|
|
186
|
+
throw new Error(`Authentication failed: ${data.error || "Invalid or expired token"}`);
|
|
187
|
+
}
|
|
188
|
+
if (status === 404) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`User not found: ${data.error || "Your account may not be set up correctly"}`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
throw new Error(`Failed to fetch API keys: ${data.error || "Unknown error"}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function fetchKeySecret(
|
|
202
|
+
authConfig: AuthConfig,
|
|
203
|
+
accessToken: string,
|
|
204
|
+
keyId: string,
|
|
205
|
+
): Promise<SdkKeyDetailResponse> {
|
|
206
|
+
try {
|
|
207
|
+
const response = await axios.get<SdkKeyDetailResponse>(`${authConfig.sdkKeysApiUrl}/${keyId}`, {
|
|
208
|
+
headers: {
|
|
209
|
+
Authorization: `Bearer ${accessToken}`,
|
|
210
|
+
},
|
|
211
|
+
timeout: HTTP_TIMEOUT,
|
|
129
212
|
});
|
|
213
|
+
|
|
214
|
+
return response.data;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (axios.isAxiosError(error)) {
|
|
217
|
+
if (error.code === "ECONNABORTED") {
|
|
218
|
+
throw new Error("Request to fetch API key details timed out. Please try again.");
|
|
219
|
+
}
|
|
220
|
+
if (error.response) {
|
|
221
|
+
const status = error.response.status;
|
|
222
|
+
const data = error.response.data as { error?: string; code?: string };
|
|
223
|
+
|
|
224
|
+
if (status === 401) {
|
|
225
|
+
throw new Error(`Authentication failed: ${data.error || "Invalid or expired token"}`);
|
|
226
|
+
}
|
|
227
|
+
if (status === 403) {
|
|
228
|
+
throw new Error(`Access denied: ${data.error || "You do not have access to this key"}`);
|
|
229
|
+
}
|
|
230
|
+
if (status === 404) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`Key not found: ${data.error || "The selected key may have been deleted"}`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
throw new Error(`Failed to fetch API key details: ${data.error || "Unknown error"}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function selectKey(keys: SdkKeyInfo[]): Promise<string> {
|
|
244
|
+
if (!process.stdin.isTTY) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
"Multiple API keys found but running in non-interactive mode. Please set MINDSIM_API_KEY environment variable directly.",
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log("Select an API key:\n");
|
|
251
|
+
|
|
252
|
+
for (const [index, key] of keys.entries()) {
|
|
253
|
+
const appName = key.app?.name || "No app";
|
|
254
|
+
console.log(` ${index + 1}. ${key.name}`);
|
|
255
|
+
console.log(` Tier: ${key.tier} | App: ${appName}`);
|
|
256
|
+
console.log();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const rl = readline.createInterface({
|
|
260
|
+
input: process.stdin,
|
|
261
|
+
output: process.stdout,
|
|
130
262
|
});
|
|
263
|
+
|
|
264
|
+
return new Promise((resolve) => {
|
|
265
|
+
const askQuestion = () => {
|
|
266
|
+
rl.question(`Enter selection (1-${keys.length}): `, (answer) => {
|
|
267
|
+
const selection = Number.parseInt(answer, 10);
|
|
268
|
+
|
|
269
|
+
if (Number.isNaN(selection) || selection < 1 || selection > keys.length) {
|
|
270
|
+
console.log(`Please enter a number between 1 and ${keys.length}`);
|
|
271
|
+
askQuestion();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const selectedKey = keys[selection - 1] as SdkKeyInfo;
|
|
276
|
+
rl.close();
|
|
277
|
+
resolve(selectedKey.id);
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
askQuestion();
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function sleep(ms: number): Promise<void> {
|
|
286
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
131
287
|
}
|
package/src/cli.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { Command
|
|
5
|
+
import { Command } from "commander";
|
|
6
6
|
import { login } from "./auth";
|
|
7
7
|
import { MindSim } from "./index";
|
|
8
8
|
import { checkForUpdates, getPackageVersion, updateSdk } from "./version";
|
|
@@ -15,7 +15,7 @@ const program = new Command();
|
|
|
15
15
|
const getSDK = () => {
|
|
16
16
|
try {
|
|
17
17
|
return new MindSim();
|
|
18
|
-
} catch
|
|
18
|
+
} catch {
|
|
19
19
|
console.error("❌ Authentication required.");
|
|
20
20
|
console.error("Please run 'mindsim auth' or set MINDSIM_API_KEY environment variable.");
|
|
21
21
|
process.exit(1);
|
|
@@ -213,6 +213,26 @@ const main = async () => {
|
|
|
213
213
|
}
|
|
214
214
|
});
|
|
215
215
|
|
|
216
|
+
snapshots
|
|
217
|
+
.command("get")
|
|
218
|
+
.description("Get snapshot detail")
|
|
219
|
+
.argument("<mindId>", "ID of the mind")
|
|
220
|
+
.argument("<snapshotId>", "ID of the snapshot")
|
|
221
|
+
.option("--transcript", "Include transcript")
|
|
222
|
+
.option("--psychometrics", "Include psychometrics")
|
|
223
|
+
.action(async (mindId, snapshotId, options) => {
|
|
224
|
+
try {
|
|
225
|
+
const client = getSDK();
|
|
226
|
+
const result = await client.snapshots.getDetail(mindId, snapshotId, {
|
|
227
|
+
includeTranscript: options.transcript,
|
|
228
|
+
includePsychometrics: options.psychometrics,
|
|
229
|
+
});
|
|
230
|
+
printOutput(result);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
handleError(err);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
216
236
|
snapshots
|
|
217
237
|
.command("create")
|
|
218
238
|
.description("Upload a file to create a snapshot")
|
|
@@ -260,6 +280,51 @@ const main = async () => {
|
|
|
260
280
|
}
|
|
261
281
|
});
|
|
262
282
|
|
|
283
|
+
snapshots
|
|
284
|
+
.command("delete")
|
|
285
|
+
.description("Delete a snapshot")
|
|
286
|
+
.argument("<mindId>", "ID of the mind")
|
|
287
|
+
.argument("<snapshotId>", "ID of the snapshot")
|
|
288
|
+
.action(async (mindId, snapshotId) => {
|
|
289
|
+
try {
|
|
290
|
+
const client = getSDK();
|
|
291
|
+
const result = await client.snapshots.delete(mindId, snapshotId);
|
|
292
|
+
printOutput(result);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
handleError(err);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
snapshots
|
|
299
|
+
.command("link")
|
|
300
|
+
.description("Link snapshot to digital twin")
|
|
301
|
+
.argument("<mindId>", "ID of the mind")
|
|
302
|
+
.argument("<snapshotId>", "ID of the snapshot")
|
|
303
|
+
.action(async (mindId, snapshotId) => {
|
|
304
|
+
try {
|
|
305
|
+
const client = getSDK();
|
|
306
|
+
const result = await client.snapshots.link(mindId, snapshotId);
|
|
307
|
+
printOutput(result);
|
|
308
|
+
} catch (err) {
|
|
309
|
+
handleError(err);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
snapshots
|
|
314
|
+
.command("unlink")
|
|
315
|
+
.description("Unlink snapshot from digital twin")
|
|
316
|
+
.argument("<mindId>", "ID of the mind")
|
|
317
|
+
.argument("<snapshotId>", "ID of the snapshot")
|
|
318
|
+
.action(async (mindId, snapshotId) => {
|
|
319
|
+
try {
|
|
320
|
+
const client = getSDK();
|
|
321
|
+
const result = await client.snapshots.unlink(mindId, snapshotId);
|
|
322
|
+
printOutput(result);
|
|
323
|
+
} catch (err) {
|
|
324
|
+
handleError(err);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
263
328
|
// ==========================================
|
|
264
329
|
// SIMULATIONS RESOURCES
|
|
265
330
|
// ==========================================
|
|
@@ -399,6 +464,32 @@ const main = async () => {
|
|
|
399
464
|
}
|
|
400
465
|
});
|
|
401
466
|
|
|
467
|
+
psychometrics
|
|
468
|
+
.command("topics")
|
|
469
|
+
.description("Get topic analysis")
|
|
470
|
+
.argument("<snapshotId>")
|
|
471
|
+
.action(async (sid) => {
|
|
472
|
+
try {
|
|
473
|
+
const client = getSDK();
|
|
474
|
+
printOutput(await client.mindTopics.get(sid));
|
|
475
|
+
} catch (e) {
|
|
476
|
+
handleError(e);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// USERS
|
|
481
|
+
program
|
|
482
|
+
.command("users")
|
|
483
|
+
.description("List organization users")
|
|
484
|
+
.action(async () => {
|
|
485
|
+
try {
|
|
486
|
+
const client = getSDK();
|
|
487
|
+
printOutput(await client.users.list());
|
|
488
|
+
} catch (e) {
|
|
489
|
+
handleError(e);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
402
493
|
program.parse(process.argv);
|
|
403
494
|
};
|
|
404
495
|
|
package/src/config.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import type {
|
|
4
|
+
import type { DeviceAuthConfig } from "./types";
|
|
5
5
|
|
|
6
|
-
const API_BASE_URL = "https://
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
6
|
+
const API_BASE_URL = "https://api.reasoner.com/api/mindsim";
|
|
7
|
+
const WORKOS_DEVICE_AUTH_URL = "https://auth.reasoner.com/user_management/authorize/device";
|
|
8
|
+
const WORKOS_TOKEN_URL = "https://auth.reasoner.com/user_management/authenticate";
|
|
9
|
+
const WORKOS_CLIENT_ID = "client_01GPECHM1J9DMY7WQNKTJ195P6";
|
|
10
|
+
const SDK_KEYS_API_URL = "https://api.reasoner.com/api/sdk/keys";
|
|
11
11
|
|
|
12
12
|
export function getConfigDir() {
|
|
13
13
|
return path.join(os.homedir(), ".mindsim");
|
|
@@ -46,12 +46,12 @@ export const saveApiKey = (apiKey: string): void => {
|
|
|
46
46
|
fs.writeFileSync(configFile, JSON.stringify({ apiKey }, null, 2));
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
export const
|
|
49
|
+
export const getDeviceAuthConfig = (): DeviceAuthConfig => {
|
|
50
50
|
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
deviceAuthUrl: process.env.MINDSIM_WORKOS_DEVICE_AUTH_URL || WORKOS_DEVICE_AUTH_URL,
|
|
52
|
+
tokenUrl: process.env.MINDSIM_WORKOS_TOKEN_URL || WORKOS_TOKEN_URL,
|
|
53
|
+
clientId: process.env.MINDSIM_WORKOS_CLIENT_ID || WORKOS_CLIENT_ID,
|
|
54
|
+
sdkKeysApiUrl: process.env.MINDSIM_SDK_KEYS_API_URL || SDK_KEYS_API_URL,
|
|
55
55
|
};
|
|
56
56
|
};
|
|
57
57
|
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import axios, { type AxiosInstance } from "axios";
|
|
2
2
|
import { getApiBaseUrl, loadApiKey } from "./config";
|
|
3
3
|
import { ArtifactsResource } from "./resources/artifacts";
|
|
4
|
+
import { MindTopicsResource } from "./resources/mind-topics";
|
|
4
5
|
import { MindsResource } from "./resources/minds";
|
|
5
6
|
import { PsychometricsResource } from "./resources/psychometrics";
|
|
6
7
|
import { SimulationsResource } from "./resources/simulations";
|
|
7
8
|
import { SnapshotsResource } from "./resources/snapshots";
|
|
8
9
|
import { TagsResource } from "./resources/tags";
|
|
10
|
+
import { UsersResource } from "./resources/users";
|
|
9
11
|
import { checkForUpdates, getPackageVersion } from "./version";
|
|
10
12
|
|
|
11
13
|
export class MindSim {
|
|
@@ -13,10 +15,12 @@ export class MindSim {
|
|
|
13
15
|
|
|
14
16
|
public artifacts: ArtifactsResource;
|
|
15
17
|
public minds: MindsResource;
|
|
18
|
+
public mindTopics: MindTopicsResource;
|
|
16
19
|
public psychometrics: PsychometricsResource;
|
|
17
20
|
public snapshots: SnapshotsResource;
|
|
18
21
|
public simulations: SimulationsResource;
|
|
19
22
|
public tags: TagsResource;
|
|
23
|
+
public users: UsersResource;
|
|
20
24
|
|
|
21
25
|
constructor(apiKey?: string, options?: { apiBaseUrl?: string }) {
|
|
22
26
|
// 1. Trigger the auto-update check (Fire and forget, do not await)
|
|
@@ -48,10 +52,12 @@ export class MindSim {
|
|
|
48
52
|
|
|
49
53
|
this.artifacts = new ArtifactsResource(this.client);
|
|
50
54
|
this.minds = new MindsResource(this.client);
|
|
55
|
+
this.mindTopics = new MindTopicsResource(this.client);
|
|
51
56
|
this.psychometrics = new PsychometricsResource(this.client);
|
|
52
57
|
this.snapshots = new SnapshotsResource(this.client);
|
|
53
58
|
this.simulations = new SimulationsResource(this.client);
|
|
54
59
|
this.tags = new TagsResource(this.client);
|
|
60
|
+
this.users = new UsersResource(this.client);
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
63
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AxiosInstance } from "axios";
|
|
2
|
+
import type { GetMindTopicsResponse } from "../types";
|
|
3
|
+
|
|
4
|
+
export class MindTopicsResource {
|
|
5
|
+
constructor(private client: AxiosInstance) {}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get topic analysis for a specific snapshot
|
|
9
|
+
*/
|
|
10
|
+
async get(snapshotId: string): Promise<GetMindTopicsResponse> {
|
|
11
|
+
const response = await this.client.get<GetMindTopicsResponse>(
|
|
12
|
+
`/snapshots/${snapshotId}/mind-topics`,
|
|
13
|
+
);
|
|
14
|
+
return response.data;
|
|
15
|
+
}
|
|
16
|
+
}
|