mindsim 0.1.0

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 (79) hide show
  1. package/.github/workflows/publish.yml +32 -0
  2. package/.github/workflows/test.yml +28 -0
  3. package/.github/workflows/type-checks.yml +29 -0
  4. package/LICENSE +21 -0
  5. package/README.md +748 -0
  6. package/assets/mindsim-logo.svg +15 -0
  7. package/biome.jsonc +43 -0
  8. package/dist/auth.d.ts +5 -0
  9. package/dist/auth.d.ts.map +1 -0
  10. package/dist/auth.js +115 -0
  11. package/dist/auth.js.map +1 -0
  12. package/dist/cli.d.ts +3 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +36 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/config.d.ts +8 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +63 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/index.d.ts +20 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +71 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/resources/artifacts.d.ts +8 -0
  25. package/dist/resources/artifacts.d.ts.map +1 -0
  26. package/dist/resources/artifacts.js +17 -0
  27. package/dist/resources/artifacts.js.map +1 -0
  28. package/dist/resources/minds.d.ts +39 -0
  29. package/dist/resources/minds.d.ts.map +1 -0
  30. package/dist/resources/minds.js +85 -0
  31. package/dist/resources/minds.js.map +1 -0
  32. package/dist/resources/psychometrics.d.ts +11 -0
  33. package/dist/resources/psychometrics.d.ts.map +1 -0
  34. package/dist/resources/psychometrics.js +18 -0
  35. package/dist/resources/psychometrics.js.map +1 -0
  36. package/dist/resources/simulations.d.ts +26 -0
  37. package/dist/resources/simulations.d.ts.map +1 -0
  38. package/dist/resources/simulations.js +41 -0
  39. package/dist/resources/simulations.js.map +1 -0
  40. package/dist/resources/snapshots.d.ts +27 -0
  41. package/dist/resources/snapshots.d.ts.map +1 -0
  42. package/dist/resources/snapshots.js +101 -0
  43. package/dist/resources/snapshots.js.map +1 -0
  44. package/dist/resources/tags.d.ts +23 -0
  45. package/dist/resources/tags.d.ts.map +1 -0
  46. package/dist/resources/tags.js +32 -0
  47. package/dist/resources/tags.js.map +1 -0
  48. package/dist/types.d.ts +161 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/dist/types.js +3 -0
  51. package/dist/types.js.map +1 -0
  52. package/dist/version.d.ts +15 -0
  53. package/dist/version.d.ts.map +1 -0
  54. package/dist/version.js +105 -0
  55. package/dist/version.js.map +1 -0
  56. package/package.json +55 -0
  57. package/src/auth.ts +131 -0
  58. package/src/cli.ts +41 -0
  59. package/src/config.ts +60 -0
  60. package/src/index.ts +59 -0
  61. package/src/resources/artifacts.ts +13 -0
  62. package/src/resources/minds.ts +98 -0
  63. package/src/resources/psychometrics.ts +16 -0
  64. package/src/resources/simulations.ts +49 -0
  65. package/src/resources/snapshots.ts +126 -0
  66. package/src/resources/tags.ts +30 -0
  67. package/src/types.ts +185 -0
  68. package/src/version.ts +111 -0
  69. package/tests/auth.test.ts +41 -0
  70. package/tests/config.test.ts +129 -0
  71. package/tests/resources/minds.test.ts +119 -0
  72. package/tests/resources/psychometrics.test.ts +38 -0
  73. package/tests/resources/simulation.test.ts +94 -0
  74. package/tests/resources/snapshots.test.ts +135 -0
  75. package/tests/resources/tags.test.ts +87 -0
  76. package/tests/use-cases/quickstart.test.ts +84 -0
  77. package/tests/version.test.ts +221 -0
  78. package/tsconfig.json +29 -0
  79. package/vitest.config.ts +12 -0
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "mindsim",
3
+ "version": "0.1.0",
4
+ "description": "The official MindSim typescript SDK allows you to programmatically create digital minds, populate them with conversational data, and run powerful simulations to get an accurate preview of how the person will think, feel, say, and act in any scenario.",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "mindsim": "./dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node ./dist/cli.js",
12
+ "build": "tsc",
13
+ "watch": "tsc --watch",
14
+ "prepare": "npm run build",
15
+ "lint": "biome check --write .",
16
+ "lint:check": "biome check .",
17
+ "format": "biome format --write .",
18
+ "format:check": "biome format .",
19
+ "type-check": "tsc --noEmit",
20
+ "test": "npm run test:unit",
21
+ "test:unit": "vitest run",
22
+ "test:unit:watch": "vitest --watch"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/mindsimulator/mindsim-sdk-typescript.git"
27
+ },
28
+ "keywords": [
29
+ "mindsim",
30
+ "digital mind",
31
+ "simulation",
32
+ "psychometrics",
33
+ "sdk",
34
+ "typescript"
35
+ ],
36
+ "author": "reasoner-developer",
37
+ "license": "MIT",
38
+ "bugs": {
39
+ "url": "https://github.com/mindsimulator/mindsim-sdk-typescript/issues"
40
+ },
41
+ "homepage": "https://github.com/mindsimulator/mindsim-sdk-typescript#readme",
42
+ "devDependencies": {
43
+ "@biomejs/biome": "2.3.6",
44
+ "@types/node": "^24.10.1",
45
+ "@types/semver": "^7.5.8",
46
+ "typescript": "^5.9.3",
47
+ "vitest": "^3.2.4"
48
+ },
49
+ "dependencies": {
50
+ "axios": "^1.13.2",
51
+ "commander": "^14.0.2",
52
+ "open": "^11.0.0",
53
+ "semver": "^7.6.0"
54
+ }
55
+ }
package/src/auth.ts ADDED
@@ -0,0 +1,131 @@
1
+ import http from "node:http";
2
+ import url from "node:url";
3
+ import open from "open";
4
+ import { getAuthConfig, saveApiKey } from "./config";
5
+ import type { AuthResponse } from "./types";
6
+
7
+ // CONFIGURATION
8
+ const authConfig = getAuthConfig();
9
+
10
+ /**
11
+ * Starts the login flow
12
+ */
13
+ export async function login(): Promise<void> {
14
+ console.log("Initiating authentication...");
15
+
16
+ // 1. Create a Promise that resolves when the local server receives the code
17
+ const apiKey = await listenForCallback();
18
+
19
+ console.log("Authorization succeeded.");
20
+
21
+ // 2. Exchange authorization code for access token
22
+ try {
23
+ // 3. Save credentials to disk
24
+ saveApiKey(apiKey);
25
+
26
+ console.log("✅ Successfully logged in! Credentials saved.");
27
+ } catch (error) {
28
+ console.error("❌ Authentication failed:", error);
29
+ process.exit(1);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Spins up a temporary local server to catch the redirect
35
+ */
36
+ function listenForCallback(): Promise<string> {
37
+ const callbackPath = new URL(authConfig.authRedirectPath).pathname;
38
+
39
+ return new Promise((resolve, reject) => {
40
+ const server = http.createServer(async (req, res) => {
41
+ if (!req.url) return;
42
+
43
+ const parsedUrl = url.parse(req.url, true);
44
+
45
+ const headers = {
46
+ "Access-Control-Allow-Origin": new URL(authConfig.authServerUrl).origin,
47
+ "Access-Control-Allow-Methods": "OPTIONS, POST, GET",
48
+ "Access-Control-Max-Age": 2592000, // 30 days
49
+ "Access-Control-Allow-Headers": "Content-Type,Authorization",
50
+ "Access-Control-Allow-Credentials": "true",
51
+ };
52
+
53
+ // Check if the request is for the callback path
54
+ if (parsedUrl.pathname === callbackPath) {
55
+ if (req.method === "OPTIONS") {
56
+ res.writeHead(204, headers);
57
+ res.end();
58
+ return;
59
+ }
60
+
61
+ if (req.method === "POST") {
62
+ let body = "";
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
+ });
108
+ }
109
+ }
110
+ });
111
+
112
+ server.listen(authConfig.listenPort, async () => {
113
+ const state = JSON.stringify({
114
+ redirect: `${authConfig.authServerLandingPath}?initCallbackUrl=${encodeURIComponent(authConfig.authRedirectPath)}`,
115
+ });
116
+
117
+ const encodedState = btoa(state);
118
+
119
+ const loginUrl = `${authConfig.authServerUrl}?state=${encodedState}`;
120
+
121
+ console.log(`Listening on port ${authConfig.listenPort}...`);
122
+ console.log(`Opening browser to: ${loginUrl}`);
123
+
124
+ await open(loginUrl);
125
+ });
126
+
127
+ server.on("error", (err) => {
128
+ reject(err);
129
+ });
130
+ });
131
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import { login } from "./auth";
5
+ import { checkForUpdates, getPackageVersion, updateSdk } from "./version";
6
+
7
+ const program = new Command();
8
+
9
+ const main = async () => {
10
+ program.name("mindsim").description("CLI for Mindsim SDK").version(getPackageVersion()); // Use the reusable extractor
11
+
12
+ program
13
+ .command("version")
14
+ .description("Output your current MindSim CLI version and check for updates")
15
+ .action(async () => {
16
+ // When explicitly asking for version, be verbose about update status
17
+ console.log(getPackageVersion());
18
+ await checkForUpdates(true);
19
+ });
20
+
21
+ // The Auth Command
22
+ program
23
+ .command("auth")
24
+ .description("Login to Mindsim via browser")
25
+ .action(async () => {
26
+ await checkForUpdates(false);
27
+ await login();
28
+ });
29
+
30
+ // NEW: The Update Command
31
+ program
32
+ .command("update")
33
+ .description("Auto-update the MindSim SDK to the latest version")
34
+ .action(async () => {
35
+ await updateSdk();
36
+ });
37
+
38
+ program.parse(process.argv);
39
+ };
40
+
41
+ main();
package/src/config.ts ADDED
@@ -0,0 +1,60 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import type { AuthConfig } from "./types";
5
+
6
+ const API_BASE_URL = "https://app.mindsim.com/api/public/v1";
7
+ const AUTH_SERVER_URL = "https://app.mindsim.com/sdk/authorize";
8
+ const AUTH_SERVER_LANDING_PATH = "/sdk-success";
9
+ const AUTH_REDIRECT_URI = "http://localhost:4242/callback";
10
+ const AUTH_PORT = "4242";
11
+
12
+ export function getConfigDir() {
13
+ return path.join(os.homedir(), ".mindsim");
14
+ }
15
+
16
+ export function getConfigFile() {
17
+ return path.join(getConfigDir(), "config");
18
+ }
19
+
20
+ export const loadApiKey = (): string | null => {
21
+ try {
22
+ if (process.env.MINDSIM_API_KEY) {
23
+ return process.env.MINDSIM_API_KEY;
24
+ }
25
+
26
+ const configFile = getConfigFile();
27
+
28
+ if (fs.existsSync(configFile)) {
29
+ const config = JSON.parse(fs.readFileSync(configFile, "utf-8"));
30
+ return config.apiKey || null;
31
+ }
32
+ } catch (error) {
33
+ // TODO: define logging interface so users can control where they are written to
34
+ console.error(error);
35
+ }
36
+ return null;
37
+ };
38
+
39
+ export const saveApiKey = (apiKey: string): void => {
40
+ const configDir = getConfigDir();
41
+ const configFile = getConfigFile();
42
+
43
+ if (!fs.existsSync(configDir)) {
44
+ fs.mkdirSync(configDir, { recursive: true });
45
+ }
46
+ fs.writeFileSync(configFile, JSON.stringify({ apiKey }, null, 2));
47
+ };
48
+
49
+ export const getAuthConfig = (): AuthConfig => {
50
+ return {
51
+ authServerUrl: process.env.MINDSIM_AUTH_SERVER_URL || AUTH_SERVER_URL,
52
+ authServerLandingPath: AUTH_SERVER_LANDING_PATH,
53
+ authRedirectPath: process.env.MINDSIM_AUTH_REDIRECT_URI || AUTH_REDIRECT_URI,
54
+ listenPort: process.env.MINDSIM_AUTH_PORT || AUTH_PORT,
55
+ };
56
+ };
57
+
58
+ export const getApiBaseUrl = () => {
59
+ return process.env.MIND_SIM_API_BASE_URL || API_BASE_URL;
60
+ };
package/src/index.ts ADDED
@@ -0,0 +1,59 @@
1
+ import axios, { type AxiosInstance } from "axios";
2
+ import { getApiBaseUrl, loadApiKey } from "./config";
3
+ import { ArtifactsResource } from "./resources/artifacts";
4
+ import { MindsResource } from "./resources/minds";
5
+ import { PsychometricsResource } from "./resources/psychometrics";
6
+ import { SimulationsResource } from "./resources/simulations";
7
+ import { SnapshotsResource } from "./resources/snapshots";
8
+ import { TagsResource } from "./resources/tags";
9
+ import { checkForUpdates, getPackageVersion } from "./version";
10
+
11
+ export class MindSim {
12
+ private client: AxiosInstance;
13
+
14
+ public artifacts: ArtifactsResource;
15
+ public minds: MindsResource;
16
+ public psychometrics: PsychometricsResource;
17
+ public snapshots: SnapshotsResource;
18
+ public simulations: SimulationsResource;
19
+ public tags: TagsResource;
20
+
21
+ constructor(apiKey?: string, options?: { apiBaseUrl?: string }) {
22
+ // 1. Trigger the auto-update check (Fire and forget, do not await)
23
+ checkForUpdates().catch(() => {
24
+ // Catching here ensures no unhandled promise rejections in the user's console
25
+ // if the check fails completely.
26
+ });
27
+
28
+ const token = apiKey || loadApiKey();
29
+ const sdkVersion = getPackageVersion();
30
+ const apiBaseUrl = options?.apiBaseUrl || getApiBaseUrl();
31
+
32
+ if (!token) {
33
+ throw new Error(
34
+ "API Key not found. Please run `mindsim auth` or pass the key to the constructor.",
35
+ );
36
+ }
37
+
38
+ this.client = axios.create({
39
+ baseURL: apiBaseUrl,
40
+ headers: {
41
+ Authorization: `Bearer ${token}`,
42
+ "Content-Type": "application/json",
43
+ "x-reasoner-source": "sdk",
44
+ "x-reasoner-sdk": "mindsim/mindsim-sdk-typescript",
45
+ "x-reasoner-sdk-version": sdkVersion,
46
+ },
47
+ });
48
+
49
+ this.artifacts = new ArtifactsResource(this.client);
50
+ this.minds = new MindsResource(this.client);
51
+ this.psychometrics = new PsychometricsResource(this.client);
52
+ this.snapshots = new SnapshotsResource(this.client);
53
+ this.simulations = new SimulationsResource(this.client);
54
+ this.tags = new TagsResource(this.client);
55
+ }
56
+ }
57
+
58
+ // Export types for consumer use
59
+ export * from "./types";
@@ -0,0 +1,13 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type { GetSignedUrlParams, GetSignedUrlResponse } from "../types";
3
+
4
+ export class ArtifactsResource {
5
+ constructor(private client: AxiosInstance) {}
6
+
7
+ async getSignedUrl(mindId: string, params?: GetSignedUrlParams): Promise<GetSignedUrlResponse> {
8
+ const response = await this.client.get<GetSignedUrlResponse>(`/minds/${mindId}/signed-url`, {
9
+ params,
10
+ });
11
+ return response.data;
12
+ }
13
+ }
@@ -0,0 +1,98 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type {
3
+ CreateMindRequest,
4
+ ListMindsParams,
5
+ Mind,
6
+ SearchMindsParams,
7
+ UpdateMindRequest,
8
+ } from "../types";
9
+
10
+ export class MindsResource {
11
+ constructor(private client: AxiosInstance) {}
12
+
13
+ /**
14
+ * List all minds
15
+ */
16
+ async list(params: ListMindsParams): Promise<Mind[]> {
17
+ const query: { [key: string]: string } = {};
18
+
19
+ if (params?.tags && params.tags?.length > 0) {
20
+ query.tagNames = params.tags.join(",");
21
+ }
22
+
23
+ const response = await this.client.get<{ minds: Mind[] }>("/minds", {
24
+ params: query,
25
+ });
26
+ return response.data.minds;
27
+ }
28
+
29
+ /**
30
+ * Create a new mind
31
+ */
32
+ async create(data: CreateMindRequest): Promise<Mind> {
33
+ let createTags: string | undefined;
34
+
35
+ if (data?.tags && data?.tags?.length > 0) {
36
+ createTags = data.tags.join(",");
37
+ }
38
+ const response = await this.client.post<{ mind: Mind }>("/minds", {
39
+ ...data,
40
+ tags: createTags,
41
+ });
42
+ return response.data.mind;
43
+ }
44
+
45
+ /**
46
+ * Get a specific mind by ID
47
+ */
48
+ async get(mindId: string): Promise<Mind> {
49
+ const response = await this.client.get<{ id: string } & Mind>(`/minds/${mindId}`);
50
+ return response.data;
51
+ }
52
+
53
+ /**
54
+ * Update a specific mind by ID
55
+ */
56
+ async update(mindId: string, data: UpdateMindRequest): Promise<Mind> {
57
+ let updateTags: string | undefined;
58
+
59
+ // The API expects tags as a comma-separated string
60
+ if (data?.tags) {
61
+ updateTags = data.tags.join(",");
62
+ }
63
+
64
+ const response = await this.client.put<{ mind: Mind }>(`/minds/${mindId}`, {
65
+ ...data,
66
+ tags: updateTags,
67
+ });
68
+ return response.data.mind;
69
+ }
70
+
71
+ /**
72
+ * Delete a mind by ID
73
+ */
74
+ async delete(mindId: string): Promise<{ success: boolean }> {
75
+ const response = await this.client.delete<{ success: boolean }>(`/minds/${mindId}`);
76
+ return response.data;
77
+ }
78
+
79
+ /**
80
+ * Search minds
81
+ */
82
+ async search(params: SearchMindsParams): Promise<Mind[]> {
83
+ const response = await this.client.get<{ minds: Mind[] }>("/minds/search", {
84
+ params,
85
+ });
86
+ return response.data.minds;
87
+ }
88
+
89
+ /**
90
+ * Set tags for a mind (Replace existing)
91
+ */
92
+ async setTags(mindId: string, data: { tags: string[] }): Promise<Mind> {
93
+ const response = await this.client.put<{ mind: Mind }>(`/minds/${mindId}/tags`, {
94
+ tagNames: data.tags,
95
+ });
96
+ return response.data.mind;
97
+ }
98
+ }
@@ -0,0 +1,16 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type { GetPsychometricsResponse } from "../types";
3
+
4
+ export class PsychometricsResource {
5
+ constructor(private client: AxiosInstance) {}
6
+
7
+ /**
8
+ * Get psychometrics for a specific snapshot
9
+ */
10
+ async get(snapshotId: string): Promise<GetPsychometricsResponse> {
11
+ const response = await this.client.get<GetPsychometricsResponse>(
12
+ `/snapshots/${snapshotId}/psychometrics`,
13
+ );
14
+ return response.data;
15
+ }
16
+ }
@@ -0,0 +1,49 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type {
3
+ GetSimulationResponse,
4
+ ListSimulationsParams,
5
+ ListSimulationsResponse,
6
+ RunSimulationRequest,
7
+ SimulationResponse,
8
+ } from "../types";
9
+
10
+ export class SimulationsResource {
11
+ constructor(private client: AxiosInstance) {}
12
+
13
+ /**
14
+ * Run simulation
15
+ */
16
+ async run(data: RunSimulationRequest): Promise<SimulationResponse> {
17
+ const response = await this.client.post<SimulationResponse>("/simulate", data);
18
+ return response.data;
19
+ }
20
+
21
+ /**
22
+ * List historical simulations
23
+ */
24
+ async list(params?: ListSimulationsParams): Promise<ListSimulationsResponse> {
25
+ const response = await this.client.get<ListSimulationsResponse>("/simulations", {
26
+ params,
27
+ });
28
+ return response.data;
29
+ }
30
+
31
+ /**
32
+ * Get a specific simulation by ID
33
+ */
34
+ async get(simulationId: string): Promise<GetSimulationResponse> {
35
+ const response = await this.client.get<GetSimulationResponse>(`/simulations/${simulationId}`);
36
+ return response.data;
37
+ }
38
+
39
+ /**
40
+ * Delete a specific simulation by ID
41
+ */
42
+ async delete(simulationId: string): Promise<{ message: string; simulationId: string }> {
43
+ const response = await this.client.delete<{
44
+ message: string;
45
+ simulationId: string;
46
+ }>(`/simulations/${simulationId}`);
47
+ return response.data;
48
+ }
49
+ }
@@ -0,0 +1,126 @@
1
+ import path from "node:path"; // Import path
2
+ import axios, { type AxiosInstance } from "axios";
3
+ import FormData from "form-data";
4
+ import type {
5
+ CreateSnapshotFromFileParams,
6
+ CreateSnapshotParams,
7
+ CreateSnapshotResponse,
8
+ GetSignedUrlResponse,
9
+ ListSnapshotsResponse,
10
+ SnapshotStatus,
11
+ } from "../types";
12
+
13
+ export class SnapshotsResource {
14
+ constructor(private client: AxiosInstance) {}
15
+
16
+ /**
17
+ * Helper to infer content type from file extension
18
+ */
19
+ private getContentType(fileName: string): string {
20
+ const ext = path.extname(fileName).toLowerCase();
21
+ const mimeTypes: Record<string, string> = {
22
+ ".html": "text/html",
23
+ ".js": "text/javascript",
24
+ ".css": "text/css",
25
+ ".json": "application/json",
26
+ ".pdf": "application/pdf",
27
+ ".txt": "text/plain",
28
+ ".vtt": "text/vtt",
29
+ ".md": "text/markdown",
30
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
31
+ };
32
+
33
+ return mimeTypes[ext] || "application/octet-stream";
34
+ }
35
+
36
+ /**
37
+ * List all snapshots for a mind
38
+ */
39
+ async list(mindId: string): Promise<ListSnapshotsResponse> {
40
+ const response = await this.client.get<ListSnapshotsResponse>(`/minds/${mindId}/snapshots`);
41
+ return response.data;
42
+ }
43
+
44
+ /**
45
+ * Create a snapshot via a signed url. Recommended for large files.
46
+ */
47
+ async create(mindId: string, params: CreateSnapshotParams): Promise<CreateSnapshotResponse> {
48
+ const finalContentType = params.contentType || this.getContentType(params.fileName);
49
+
50
+ const signedUrlRes = await this.client.get<GetSignedUrlResponse>(
51
+ `/minds/${mindId}/signed-url`,
52
+ {
53
+ params: {
54
+ fileName: params.fileName,
55
+ contentType: finalContentType,
56
+ },
57
+ },
58
+ );
59
+
60
+ const { signedUrl, artifactId } = signedUrlRes.data;
61
+
62
+ await axios.put(signedUrl, params.file, {
63
+ headers: {
64
+ "Content-Type": finalContentType,
65
+ },
66
+ maxBodyLength: Infinity,
67
+ maxContentLength: Infinity,
68
+ });
69
+
70
+ const form = new FormData();
71
+ form.append("artifactId", artifactId);
72
+
73
+ if (params.mindsetDate) {
74
+ form.append("mindsetDate", params.mindsetDate);
75
+ }
76
+
77
+ const snapshotRes = await this.client.post<CreateSnapshotResponse>(
78
+ `/minds/${mindId}/snapshots`,
79
+ form,
80
+ {
81
+ headers: {
82
+ ...form.getHeaders(),
83
+ },
84
+ },
85
+ );
86
+
87
+ return snapshotRes.data;
88
+ }
89
+
90
+ /**
91
+ * Create snapshot from uploaded file
92
+ */
93
+ async createFromFile(mindId: string, params: CreateSnapshotFromFileParams) {
94
+ const form = new FormData();
95
+
96
+ if (params.file) {
97
+ form.append("file", params.file, params.fileName || "upload.txt");
98
+ }
99
+
100
+ if (params.artifactId) {
101
+ form.append("artifactId", params.artifactId);
102
+ }
103
+
104
+ if (params.mindsetDate) {
105
+ form.append("mindsetDate", params.mindsetDate);
106
+ }
107
+
108
+ const response = await this.client.post(`/minds/${mindId}/snapshots`, form, {
109
+ headers: {
110
+ ...form.getHeaders(), // Crucial for multipart/form-data in Node
111
+ },
112
+ });
113
+
114
+ return response.data;
115
+ }
116
+
117
+ /**
118
+ * Check snapshot status
119
+ */
120
+ async getStatus(mindId: string, snapshotId: string): Promise<SnapshotStatus> {
121
+ const response = await this.client.get<SnapshotStatus>(
122
+ `/minds/${mindId}/snapshots/${snapshotId}/status`,
123
+ );
124
+ return response.data;
125
+ }
126
+ }
@@ -0,0 +1,30 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type { Tag } from "../types";
3
+
4
+ export class TagsResource {
5
+ constructor(private client: AxiosInstance) {}
6
+
7
+ /**
8
+ * List all tags
9
+ */
10
+ async list(): Promise<Tag[]> {
11
+ const response = await this.client.get<{ tags: Tag[] }>("/tags");
12
+ return response.data.tags;
13
+ }
14
+
15
+ /**
16
+ * Update tag
17
+ */
18
+ async update(tagId: string, data: { name: string }): Promise<Tag> {
19
+ const response = await this.client.put<{ tag: Tag }>(`/tags/${tagId}`, data);
20
+ return response.data.tag;
21
+ }
22
+
23
+ /**
24
+ * Delete tag
25
+ */
26
+ async delete(tagId: string): Promise<{ deleted: boolean }> {
27
+ const response = await this.client.delete<{ deleted: boolean }>(`/tags/${tagId}`);
28
+ return response.data;
29
+ }
30
+ }