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.
Files changed (43) hide show
  1. package/README.md +92 -2
  2. package/dist/auth.d.ts +0 -3
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +184 -93
  5. package/dist/auth.js.map +1 -1
  6. package/dist/cli.js +92 -1
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts +2 -2
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +12 -12
  11. package/dist/config.js.map +1 -1
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +6 -0
  15. package/dist/index.js.map +1 -1
  16. package/dist/resources/mind-topics.d.ts +11 -0
  17. package/dist/resources/mind-topics.d.ts.map +1 -0
  18. package/dist/resources/mind-topics.js +18 -0
  19. package/dist/resources/mind-topics.js.map +1 -0
  20. package/dist/resources/snapshots.d.ts +29 -1
  21. package/dist/resources/snapshots.d.ts.map +1 -1
  22. package/dist/resources/snapshots.js +30 -0
  23. package/dist/resources/snapshots.js.map +1 -1
  24. package/dist/resources/users.d.ts +14 -0
  25. package/dist/resources/users.d.ts.map +1 -0
  26. package/dist/resources/users.js +20 -0
  27. package/dist/resources/users.js.map +1 -0
  28. package/dist/types.d.ts +78 -8
  29. package/dist/types.d.ts.map +1 -1
  30. package/package.json +1 -1
  31. package/src/auth.ts +261 -105
  32. package/src/cli.ts +93 -2
  33. package/src/config.ts +11 -11
  34. package/src/index.ts +6 -0
  35. package/src/resources/mind-topics.ts +16 -0
  36. package/src/resources/snapshots.ts +60 -0
  37. package/src/resources/users.ts +16 -0
  38. package/src/types.ts +88 -7
  39. package/tests/config.test.ts +20 -15
  40. package/tests/resources/mind-topics.test.ts +69 -0
  41. package/tests/resources/snapshots.test.ts +46 -0
  42. package/tests/resources/users.test.ts +54 -0
  43. package/tests/version.test.ts +1 -1
@@ -5,8 +5,10 @@ import type {
5
5
  CreateSnapshotFromFileParams,
6
6
  CreateSnapshotParams,
7
7
  CreateSnapshotResponse,
8
+ DigitalTwinMindAssessment,
8
9
  GetSignedUrlResponse,
9
10
  ListSnapshotsResponse,
11
+ SnapshotDetailResponse,
10
12
  SnapshotStatus,
11
13
  } from "../types";
12
14
 
@@ -41,6 +43,36 @@ export class SnapshotsResource {
41
43
  return response.data;
42
44
  }
43
45
 
46
+ /**
47
+ * Get details of a specific snapshot, optionally including transcripts and psychometrics
48
+ */
49
+ async getDetail(
50
+ mindId: string,
51
+ snapshotId: string,
52
+ options?: { includeTranscript?: boolean; includePsychometrics?: boolean },
53
+ ): Promise<SnapshotDetailResponse> {
54
+ const response = await this.client.get<SnapshotDetailResponse>(
55
+ `/minds/${mindId}/snapshots/${snapshotId}`,
56
+ {
57
+ params: options,
58
+ },
59
+ );
60
+ return response.data;
61
+ }
62
+
63
+ /**
64
+ * Delete a snapshot
65
+ */
66
+ async delete(
67
+ mindId: string,
68
+ snapshotId: string,
69
+ ): Promise<{ message: string; snapshotId: string }> {
70
+ const response = await this.client.delete<{ message: string; snapshotId: string }>(
71
+ `/minds/${mindId}/snapshots/${snapshotId}`,
72
+ );
73
+ return response.data;
74
+ }
75
+
44
76
  /**
45
77
  * Create a snapshot via a signed url. Recommended for large files.
46
78
  */
@@ -123,4 +155,32 @@ export class SnapshotsResource {
123
155
  );
124
156
  return response.data;
125
157
  }
158
+
159
+ /**
160
+ * Link a snapshot to the Digital Twin
161
+ */
162
+ async link(
163
+ mindId: string,
164
+ snapshotId: string,
165
+ ): Promise<{ message: string; snapshot: DigitalTwinMindAssessment }> {
166
+ const response = await this.client.post<{
167
+ message: string;
168
+ snapshot: DigitalTwinMindAssessment;
169
+ }>(`/minds/${mindId}/snapshots/${snapshotId}/link`);
170
+ return response.data;
171
+ }
172
+
173
+ /**
174
+ * Unlink a snapshot from the Digital Twin
175
+ */
176
+ async unlink(
177
+ mindId: string,
178
+ snapshotId: string,
179
+ ): Promise<{ message: string; snapshot: DigitalTwinMindAssessment[] }> {
180
+ const response = await this.client.post<{
181
+ message: string;
182
+ snapshot: DigitalTwinMindAssessment[];
183
+ }>(`/minds/${mindId}/snapshots/${snapshotId}/unlink`);
184
+ return response.data;
185
+ }
126
186
  }
@@ -0,0 +1,16 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type { ListUsersResponse } from "../types";
3
+
4
+ export class UsersResource {
5
+ constructor(private client: AxiosInstance) {}
6
+
7
+ /**
8
+ * List users in the organization
9
+ */
10
+ async list(params?: { limit?: number; offset?: number }): Promise<ListUsersResponse> {
11
+ const response = await this.client.get<ListUsersResponse>("/organizations/users", {
12
+ params,
13
+ });
14
+ return response.data;
15
+ }
16
+ }
package/src/types.ts CHANGED
@@ -1,13 +1,56 @@
1
- export interface AuthConfig {
2
- authServerUrl: string;
3
- authServerLandingPath: string;
4
- authRedirectPath: string;
5
- listenPort: string;
1
+ export interface DeviceAuthConfig {
2
+ deviceAuthUrl: string;
3
+ tokenUrl: string;
4
+ clientId: string;
5
+ sdkKeysApiUrl: string;
6
6
  }
7
7
 
8
- export interface AuthResponse {
9
- apiKey?: {
8
+ export interface DeviceAuthResponse {
9
+ device_code: string;
10
+ user_code: string;
11
+ verification_uri: string;
12
+ verification_uri_complete: string;
13
+ expires_in: number;
14
+ interval: number;
15
+ }
16
+
17
+ export interface TokenResponse {
18
+ access_token: string;
19
+ refresh_token?: string;
20
+ user?: Record<string, unknown>;
21
+ }
22
+
23
+ export interface SdkKeysResponse {
24
+ success: boolean;
25
+ user: {
10
26
  id: string;
27
+ workosUserId: string;
28
+ email: string;
29
+ firstName: string | null;
30
+ lastName: string | null;
31
+ };
32
+ keys: SdkKeyInfo[];
33
+ }
34
+
35
+ export interface SdkKeyInfo {
36
+ id: string;
37
+ name: string;
38
+ description: string | null;
39
+ tier: "development" | "production";
40
+ lastUsedAt: string | null;
41
+ createdAt: string;
42
+ app: {
43
+ id: string;
44
+ name: string;
45
+ description: string | null;
46
+ mindsimOrgId: string;
47
+ mindsimOrgName: string | null;
48
+ } | null;
49
+ }
50
+
51
+ export interface SdkKeyDetailResponse {
52
+ success: boolean;
53
+ key: SdkKeyInfo & {
11
54
  key: string;
12
55
  };
13
56
  }
@@ -34,6 +77,7 @@ export interface Mind {
34
77
  createdAt?: string;
35
78
  updatedAt?: string;
36
79
  tags: Tag[];
80
+ snapshotCount?: number;
37
81
  }
38
82
 
39
83
  export interface CreateMindRequest {
@@ -66,6 +110,8 @@ export interface SimulationScenario {
66
110
  export interface RunSimulationRequest {
67
111
  mindId: string;
68
112
  scenario: SimulationScenario;
113
+ conversationId?: string;
114
+ stream?: boolean;
69
115
  runInBackground?: boolean;
70
116
  }
71
117
 
@@ -160,11 +206,24 @@ export interface MindAssessment {
160
206
  findingCount?: number | null;
161
207
  }
162
208
 
209
+ export interface SnapshotDetailResponse extends MindAssessment {
210
+ transcripts?: any[];
211
+ psychometrics?: any;
212
+ }
213
+
163
214
  export interface ListSnapshotsResponse {
164
215
  snapshots: MindAssessment[];
165
216
  count: number;
166
217
  }
167
218
 
219
+ export interface DigitalTwinMindAssessment {
220
+ id: string;
221
+ digitalTwinId: string;
222
+ mindAssessmentId: string;
223
+ createdAt: string;
224
+ updatedAt: string;
225
+ }
226
+
168
227
  // -- Psychometrics --
169
228
 
170
229
  export interface PersonDetails {
@@ -183,3 +242,25 @@ export interface GetPsychometricsResponse {
183
242
  topicsSentimentSummary?: Record<string, any> | null;
184
243
  mindReasonerStopError?: any | null;
185
244
  }
245
+
246
+ export interface GetMindTopicsResponse {
247
+ personDetails: PersonDetails;
248
+ topicsSentimentSummary?: Record<string, any> | null;
249
+ mindReasonerStopError?: any | null;
250
+ }
251
+
252
+ // -- Users --
253
+
254
+ export interface User {
255
+ id: string;
256
+ name: string;
257
+ email: string;
258
+ imageUrl?: string | null;
259
+ createdAt?: string;
260
+ updatedAt?: string;
261
+ }
262
+
263
+ export interface ListUsersResponse {
264
+ users: User[];
265
+ count: number;
266
+ }
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { afterEach, beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
5
- import { getApiBaseUrl, getAuthConfig, loadApiKey, saveApiKey } from "../src/config";
5
+ import { getApiBaseUrl, getDeviceAuthConfig, loadApiKey, saveApiKey } from "../src/config";
6
6
 
7
7
  // Mock Node built-ins
8
8
  vi.mock("node:fs");
@@ -96,29 +96,34 @@ describe("Config Module", () => {
96
96
  });
97
97
  });
98
98
 
99
- describe("getAuthConfig", () => {
99
+ describe("getDeviceAuthConfig", () => {
100
100
  it("should return default values when env vars are not set", () => {
101
- const config = getAuthConfig();
102
- expect(config.authServerUrl).toBe("https://app.mindsim.com/sdk/authorize");
103
- expect(config.authRedirectPath).toBe("http://localhost:4242/callback");
104
- expect(config.listenPort).toBe("4242");
101
+ const config = getDeviceAuthConfig();
102
+ expect(config.deviceAuthUrl).toBe(
103
+ "https://auth.reasoner.com/user_management/authorize/device",
104
+ );
105
+ expect(config.tokenUrl).toBe("https://auth.reasoner.com/user_management/authenticate");
106
+ expect(config.clientId).toBe("client_01GPECHM1J9DMY7WQNKTJ195P6");
107
+ expect(config.sdkKeysApiUrl).toBe("https://api.reasoner.com/api/sdk/keys");
105
108
  });
106
109
 
107
110
  it("should return env overrides when set", () => {
108
- vi.stubEnv("MINDSIM_AUTH_SERVER_URL", "https://dev.mindsim.com/auth");
109
- vi.stubEnv("MINDSIM_AUTH_REDIRECT_URI", "http://custom-callback");
110
- vi.stubEnv("MINDSIM_AUTH_PORT", "8080");
111
-
112
- const config = getAuthConfig();
113
- expect(config.authServerUrl).toBe("https://dev.mindsim.com/auth");
114
- expect(config.authRedirectPath).toBe("http://custom-callback");
115
- expect(config.listenPort).toBe("8080");
111
+ vi.stubEnv("MINDSIM_WORKOS_DEVICE_AUTH_URL", "https://custom-auth.example.com/device");
112
+ vi.stubEnv("MINDSIM_WORKOS_TOKEN_URL", "https://custom-auth.example.com/token");
113
+ vi.stubEnv("MINDSIM_WORKOS_CLIENT_ID", "custom_client_id");
114
+ vi.stubEnv("MINDSIM_SDK_KEYS_API_URL", "https://custom-api.example.com/keys");
115
+
116
+ const config = getDeviceAuthConfig();
117
+ expect(config.deviceAuthUrl).toBe("https://custom-auth.example.com/device");
118
+ expect(config.tokenUrl).toBe("https://custom-auth.example.com/token");
119
+ expect(config.clientId).toBe("custom_client_id");
120
+ expect(config.sdkKeysApiUrl).toBe("https://custom-api.example.com/keys");
116
121
  });
117
122
  });
118
123
 
119
124
  describe("getApiBaseUrl", () => {
120
125
  it("should return default URL", () => {
121
- expect(getApiBaseUrl()).toBe("https://app.mindsim.com/api/public/v1");
126
+ expect(getApiBaseUrl()).toBe("https://api.reasoner.com/api/mindsim");
122
127
  });
123
128
 
124
129
  it("should return override from environment", () => {
@@ -0,0 +1,69 @@
1
+ import axios from "axios";
2
+ import { beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
3
+ import { MindSim } from "../../src/index";
4
+ import type { GetMindTopicsResponse } from "../../src/types";
5
+
6
+ vi.mock("axios");
7
+ const mockedAxios = axios as Mocked<typeof axios>;
8
+
9
+ describe("MindSim Mind Topics Resource", () => {
10
+ let mindsim: MindSim;
11
+ const SNAPSHOT_ID = "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11";
12
+
13
+ const mockClient = {
14
+ get: vi.fn(),
15
+ defaults: { headers: { common: {} } },
16
+ interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() } },
17
+ };
18
+
19
+ beforeEach(() => {
20
+ mockedAxios.create.mockReturnValue(mockClient as any);
21
+ mindsim = new MindSim("test-api-key");
22
+ });
23
+
24
+ it("get() should retrieve topic analysis for a snapshot", async () => {
25
+ const mockResponse: GetMindTopicsResponse = {
26
+ personDetails: {
27
+ id: "p1",
28
+ name: "Test Subject",
29
+ mindDate: "2023-01-01",
30
+ mindAssesssmentId: SNAPSHOT_ID,
31
+ },
32
+ topicsSentimentSummary: {
33
+ topics: [
34
+ { name: "Coding", sentiment: "Positive" },
35
+ { name: "Meetings", sentiment: "Negative" },
36
+ ],
37
+ },
38
+ mindReasonerStopError: null,
39
+ };
40
+
41
+ mockClient.get.mockResolvedValue({ data: mockResponse });
42
+
43
+ const result = await mindsim.mindTopics.get(SNAPSHOT_ID);
44
+
45
+ expect(mockClient.get).toHaveBeenCalledWith(`/snapshots/${SNAPSHOT_ID}/mind-topics`);
46
+ expect(result.personDetails.name).toBe("Test Subject");
47
+ expect(result.topicsSentimentSummary?.topics).toHaveLength(2);
48
+ });
49
+
50
+ it("get() should handle missing optional fields gracefully", async () => {
51
+ // API might return minimal data if processing isn't fully complete or data is sparse
52
+ const mockResponse: GetMindTopicsResponse = {
53
+ personDetails: {
54
+ id: "p1",
55
+ name: "Test Subject",
56
+ mindDate: "2023-01-01",
57
+ mindAssesssmentId: SNAPSHOT_ID,
58
+ },
59
+ topicsSentimentSummary: null,
60
+ mindReasonerStopError: null,
61
+ };
62
+
63
+ mockClient.get.mockResolvedValue({ data: mockResponse });
64
+
65
+ const result = await mindsim.mindTopics.get(SNAPSHOT_ID);
66
+
67
+ expect(result.topicsSentimentSummary).toBeNull();
68
+ });
69
+ });
@@ -17,6 +17,7 @@ describe("MindSim Snapshots Resource", () => {
17
17
  get: vi.fn(),
18
18
  post: vi.fn(),
19
19
  put: vi.fn(),
20
+ delete: vi.fn(), // Added delete mock
20
21
  defaults: { headers: { common: {} } },
21
22
  interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() } },
22
23
  };
@@ -26,6 +27,7 @@ describe("MindSim Snapshots Resource", () => {
26
27
  mindsim = new MindSim("fake-key");
27
28
  });
28
29
 
30
+ // ... (Existing list, create, createFromFile, getStatus tests remain unchanged) ...
29
31
  it("list() should retrieve snapshots for a mind", async () => {
30
32
  const mockList = {
31
33
  snapshots: [{ id: ASSESSMENT_ID, status: "completed" }],
@@ -132,4 +134,48 @@ describe("MindSim Snapshots Resource", () => {
132
134
  `/minds/${MIND_ID}/snapshots/${ASSESSMENT_ID}/status`,
133
135
  );
134
136
  });
137
+
138
+ // NEW TESTS
139
+
140
+ it("getDetail() should retrieve details with query params", async () => {
141
+ mockApiClient.get.mockResolvedValue({
142
+ data: { id: ASSESSMENT_ID, status: "completed", transcripts: ["foo"] },
143
+ });
144
+
145
+ const result = await mindsim.snapshots.getDetail(MIND_ID, ASSESSMENT_ID, {
146
+ includeTranscript: true,
147
+ });
148
+
149
+ expect(mockApiClient.get).toHaveBeenCalledWith(`/minds/${MIND_ID}/snapshots/${ASSESSMENT_ID}`, {
150
+ params: { includeTranscript: true },
151
+ });
152
+ expect(result.transcripts).toHaveLength(1);
153
+ });
154
+
155
+ it("delete() should remove snapshot", async () => {
156
+ mockApiClient.delete.mockResolvedValue({
157
+ data: { message: "deleted", snapshotId: ASSESSMENT_ID },
158
+ });
159
+
160
+ await mindsim.snapshots.delete(MIND_ID, ASSESSMENT_ID);
161
+ expect(mockApiClient.delete).toHaveBeenCalledWith(
162
+ `/minds/${MIND_ID}/snapshots/${ASSESSMENT_ID}`,
163
+ );
164
+ });
165
+
166
+ it("link() should post to link endpoint", async () => {
167
+ mockApiClient.post.mockResolvedValue({ data: { message: "linked" } });
168
+ await mindsim.snapshots.link(MIND_ID, ASSESSMENT_ID);
169
+ expect(mockApiClient.post).toHaveBeenCalledWith(
170
+ `/minds/${MIND_ID}/snapshots/${ASSESSMENT_ID}/link`,
171
+ );
172
+ });
173
+
174
+ it("unlink() should post to unlink endpoint", async () => {
175
+ mockApiClient.post.mockResolvedValue({ data: { message: "unlinked" } });
176
+ await mindsim.snapshots.unlink(MIND_ID, ASSESSMENT_ID);
177
+ expect(mockApiClient.post).toHaveBeenCalledWith(
178
+ `/minds/${MIND_ID}/snapshots/${ASSESSMENT_ID}/unlink`,
179
+ );
180
+ });
135
181
  });
@@ -0,0 +1,54 @@
1
+ import axios from "axios";
2
+ import { beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
3
+ import { MindSim } from "../../src/index";
4
+ import type { ListUsersResponse } from "../../src/types";
5
+
6
+ vi.mock("axios");
7
+ const mockedAxios = axios as Mocked<typeof axios>;
8
+
9
+ describe("MindSim Users Resource", () => {
10
+ let mindsim: MindSim;
11
+
12
+ const mockClient = {
13
+ get: vi.fn(),
14
+ defaults: { headers: { common: {} } },
15
+ interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() } },
16
+ };
17
+
18
+ beforeEach(() => {
19
+ mockedAxios.create.mockReturnValue(mockClient as any);
20
+ mindsim = new MindSim("test-api-key");
21
+ });
22
+
23
+ it("list() should call /organizations/users endpoint", async () => {
24
+ const mockResponse: ListUsersResponse = {
25
+ users: [
26
+ { id: "u1", name: "Alice", email: "alice@test.com" },
27
+ { id: "u2", name: "Bob", email: "bob@test.com" },
28
+ ],
29
+ count: 2,
30
+ };
31
+
32
+ mockClient.get.mockResolvedValue({ data: mockResponse });
33
+
34
+ const result = await mindsim.users.list();
35
+
36
+ expect(mockClient.get).toHaveBeenCalledWith("/organizations/users", {
37
+ params: undefined,
38
+ });
39
+ expect(result.count).toBe(2);
40
+ expect(result.users[0].name).toBe("Alice");
41
+ });
42
+
43
+ it("list() should pass pagination parameters", async () => {
44
+ const mockResponse: ListUsersResponse = { users: [], count: 0 };
45
+ mockClient.get.mockResolvedValue({ data: mockResponse });
46
+
47
+ const params = { limit: 10, offset: 5 };
48
+ await mindsim.users.list(params);
49
+
50
+ expect(mockClient.get).toHaveBeenCalledWith("/organizations/users", {
51
+ params: params,
52
+ });
53
+ });
54
+ });
@@ -91,7 +91,7 @@ describe("Version Module", () => {
91
91
  mockedFs.existsSync.mockReturnValue(true);
92
92
  mockedFs.readFileSync.mockReturnValue("INVALID_JSON");
93
93
 
94
- const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
94
+ vi.spyOn(console, "warn").mockImplementation(() => {});
95
95
 
96
96
  const version = getPackageVersion();
97
97
  expect(version).toBe("0.0.0");