onshape 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.
package/bin/onshape.js ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { main } = require("../dist/cli");
5
+
6
+ main(process.argv.slice(2)).catch((error) => {
7
+ const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
8
+ console.log(JSON.stringify({ ok: false, error: message, detail: null }, null, 2));
9
+ process.exit(1);
10
+ });
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OnshapeClient = exports.HttpError = void 0;
4
+ const node_url_1 = require("node:url");
5
+ class HttpError extends Error {
6
+ status;
7
+ detail;
8
+ constructor(status, detail) {
9
+ super(`HTTP ${status}`);
10
+ this.name = "HttpError";
11
+ this.status = status;
12
+ this.detail = detail;
13
+ }
14
+ }
15
+ exports.HttpError = HttpError;
16
+ class OnshapeClient {
17
+ creds;
18
+ constructor(creds) {
19
+ this.creds = creds;
20
+ }
21
+ async get(path, params) {
22
+ const url = new URL(path, this.creds.baseUrl);
23
+ if (params) {
24
+ const search = new node_url_1.URLSearchParams();
25
+ for (const [key, value] of Object.entries(params)) {
26
+ if (value !== undefined)
27
+ search.set(key, String(value));
28
+ }
29
+ url.search = search.toString();
30
+ }
31
+ return this.requestJson(url);
32
+ }
33
+ async post(path, data) {
34
+ const response = await this.fetchWithAuthRedirects(new URL(path, this.creds.baseUrl), {
35
+ Accept: "application/json;charset=UTF-8; qs=0.09",
36
+ "Content-Type": "application/json;charset=UTF-8; qs=0.09",
37
+ }, "POST", data === undefined ? undefined : JSON.stringify(data));
38
+ if (!response.ok) {
39
+ throw new HttpError(response.status, await responseDetail(response));
40
+ }
41
+ return responseJsonOrStatus(response, "ok");
42
+ }
43
+ async delete(path) {
44
+ const response = await this.fetchWithAuthRedirects(new URL(path, this.creds.baseUrl), {
45
+ Accept: "application/json;charset=UTF-8; qs=0.09",
46
+ }, "DELETE");
47
+ if (!response.ok) {
48
+ throw new HttpError(response.status, await responseDetail(response));
49
+ }
50
+ return responseJsonOrStatus(response, "deleted");
51
+ }
52
+ async requestJson(url) {
53
+ const response = await this.fetchWithAuthRedirects(url, {
54
+ Accept: "application/json;charset=UTF-8; qs=0.09",
55
+ });
56
+ if (!response.ok) {
57
+ throw new HttpError(response.status, await responseDetail(response));
58
+ }
59
+ return response.json();
60
+ }
61
+ async fetchWithAuthRedirects(url, headers, method = "GET", body) {
62
+ const auth = Buffer.from(`${this.creds.accessKey}:${this.creds.secretKey}`).toString("base64");
63
+ const requestHeaders = { ...headers, Authorization: `Basic ${auth}` };
64
+ let current = url;
65
+ for (let hop = 0; hop < 5; hop += 1) {
66
+ const response = await fetch(current, { method, body, headers: requestHeaders, redirect: "manual" });
67
+ if (![301, 302, 303, 307, 308].includes(response.status))
68
+ return response;
69
+ const location = response.headers.get("location");
70
+ if (!location)
71
+ return response;
72
+ current = new URL(location, current);
73
+ }
74
+ return fetch(current, { method, body, headers: requestHeaders, redirect: "manual" });
75
+ }
76
+ }
77
+ exports.OnshapeClient = OnshapeClient;
78
+ async function responseDetail(response) {
79
+ const text = await response.text();
80
+ try {
81
+ return JSON.parse(text);
82
+ }
83
+ catch {
84
+ return text.slice(0, 1000);
85
+ }
86
+ }
87
+ async function responseJsonOrStatus(response, key) {
88
+ const text = await response.text();
89
+ if (!text)
90
+ return { [key]: true, status: response.status };
91
+ try {
92
+ return JSON.parse(text);
93
+ }
94
+ catch {
95
+ return { [key]: true, status: response.status, text: text.slice(0, 500) };
96
+ }
97
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DocumentManager = void 0;
4
+ exports.normalizeDocumentList = normalizeDocumentList;
5
+ exports.normalizeDocument = normalizeDocument;
6
+ class DocumentManager {
7
+ client;
8
+ constructor(client) {
9
+ this.client = client;
10
+ }
11
+ async listDocuments(options) {
12
+ const response = await this.client.get("/api/v6/documents", {
13
+ sortColumn: options.sortBy ?? "modifiedAt",
14
+ sortOrder: options.sortOrder ?? "desc",
15
+ limit: options.limit ?? 20,
16
+ offset: options.offset ?? 0,
17
+ filter: options.filterType,
18
+ });
19
+ return normalizeDocumentList(response);
20
+ }
21
+ async searchDocuments(query, limit = 20, documentFilter = 0) {
22
+ const response = await this.client.get("/api/v6/documents", {
23
+ q: query,
24
+ limit,
25
+ filter: documentFilter || undefined,
26
+ });
27
+ return normalizeDocumentList(response);
28
+ }
29
+ async getDocument(documentId) {
30
+ return normalizeDocument(await this.client.get(`/api/v6/documents/${documentId}`));
31
+ }
32
+ async createDocument(name, isPublic = false, description) {
33
+ return this.client.post("/api/v6/documents", {
34
+ name,
35
+ isPublic,
36
+ ...(description ? { description } : {}),
37
+ });
38
+ }
39
+ async deleteDocument(documentId) {
40
+ return this.client.delete(`/api/v6/documents/${documentId}`);
41
+ }
42
+ async updateDocument(documentId, name, description) {
43
+ return this.client.post(`/api/v6/documents/${documentId}`, {
44
+ ...(name !== undefined ? { name } : {}),
45
+ ...(description !== undefined ? { description } : {}),
46
+ });
47
+ }
48
+ async getVersions(documentId) {
49
+ return this.client.get(`/api/v6/documents/d/${documentId}/versions`);
50
+ }
51
+ async createVersion(documentId, workspaceId, name, description) {
52
+ return this.client.post(`/api/v6/documents/d/${documentId}/versions`, {
53
+ documentId,
54
+ workspaceId,
55
+ name,
56
+ ...(description ? { description } : {}),
57
+ });
58
+ }
59
+ async getWorkspaces(documentId) {
60
+ const response = await this.client.get(`/api/v6/documents/d/${documentId}/workspaces`);
61
+ const items = Array.isArray(response) ? response : [];
62
+ return items.map(normalizeWorkspace);
63
+ }
64
+ async getElements(documentId, workspaceId, elementType) {
65
+ const response = await this.client.get(`/api/v6/documents/d/${documentId}/w/${workspaceId}/elements`);
66
+ const items = Array.isArray(response) ? response : [];
67
+ return items.map(normalizeElement).filter((element) => {
68
+ if (!elementType)
69
+ return true;
70
+ const normalizedElementType = String(element.element_type ?? "").replaceAll(" ", "").toUpperCase();
71
+ const normalizedFilter = elementType.replaceAll(" ", "").toUpperCase();
72
+ return normalizedElementType === normalizedFilter;
73
+ });
74
+ }
75
+ async findPartStudios(documentId, workspaceId, namePattern) {
76
+ const elements = await this.getElements(documentId, workspaceId, "PARTSTUDIO");
77
+ if (!namePattern)
78
+ return elements;
79
+ const pattern = namePattern.toLowerCase();
80
+ return elements.filter((element) => String(element.name ?? "").toLowerCase().includes(pattern));
81
+ }
82
+ async getDocumentSummary(documentId) {
83
+ const document = await this.getDocument(documentId);
84
+ const workspaces = await this.getWorkspaces(documentId);
85
+ const workspaceDetails = [];
86
+ for (const workspace of workspaces) {
87
+ const workspaceRecord = workspace;
88
+ const elements = await this.getElements(documentId, String(workspaceRecord.id));
89
+ workspaceDetails.push({ workspace, elements });
90
+ }
91
+ return { document, workspaces, workspace_details: workspaceDetails };
92
+ }
93
+ }
94
+ exports.DocumentManager = DocumentManager;
95
+ function normalizeDocumentList(response) {
96
+ const items = isRecord(response) && Array.isArray(response.items) ? response.items : [];
97
+ return items.map(normalizeDocument).filter(Boolean);
98
+ }
99
+ function normalizeDocument(response) {
100
+ if (!isRecord(response))
101
+ return null;
102
+ const owner = isRecord(response.owner) ? response.owner : {};
103
+ const thumbnail = isRecord(response.thumbnail) ? response.thumbnail.href : null;
104
+ return {
105
+ id: response.id ?? null,
106
+ name: response.name ?? null,
107
+ created_at: response.createdAt ?? null,
108
+ modified_at: response.modifiedAt ?? null,
109
+ owner_id: owner.id ?? "",
110
+ owner_name: owner.name ?? null,
111
+ public: response.public ?? false,
112
+ description: response.description ?? null,
113
+ thumbnail,
114
+ };
115
+ }
116
+ function normalizeWorkspace(response) {
117
+ const item = isRecord(response) ? response : {};
118
+ return {
119
+ id: item.id ?? null,
120
+ name: item.name ?? null,
121
+ is_main: item.isMain ?? false,
122
+ created_at: item.createdAt ?? null,
123
+ modified_at: item.modifiedAt ?? null,
124
+ };
125
+ }
126
+ function normalizeElement(response) {
127
+ const item = isRecord(response) ? response : {};
128
+ return {
129
+ id: item.id ?? null,
130
+ name: item.name ?? null,
131
+ element_type: item.type ?? "UNKNOWN",
132
+ data_type: item.dataType ?? null,
133
+ thumbnail: item.thumbnail ?? null,
134
+ };
135
+ }
136
+ function isRecord(value) {
137
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
138
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EdgeQuery = void 0;
4
+ const fsvalue_1 = require("./fsvalue");
5
+ const INCH_TO_METER = 0.0254;
6
+ class EdgeQuery {
7
+ client;
8
+ constructor(client) {
9
+ this.client = client;
10
+ }
11
+ async getEdges(documentId, workspaceId, elementId) {
12
+ const response = await this.client.get(`/api/v6/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/bodydetails`, {
13
+ includeTopology: "true",
14
+ });
15
+ const bodies = isRecord(response) && Array.isArray(response.bodies) ? response.bodies : [];
16
+ const edges = [];
17
+ for (const body of bodies) {
18
+ if (!isRecord(body))
19
+ continue;
20
+ const bodyId = body.id;
21
+ const bodyEdges = Array.isArray(body.edges) ? body.edges : [];
22
+ for (const edge of bodyEdges) {
23
+ if (!isRecord(edge))
24
+ continue;
25
+ const geom = isRecord(edge.geometry) ? edge.geometry : {};
26
+ let geometryType = geom.type ?? edge.geometryType;
27
+ if (!geometryType) {
28
+ const btType = String(geom.btType ?? "");
29
+ if (geom.radius !== undefined || btType.includes("Circle") || btType.includes("Arc")) {
30
+ geometryType = geom.startPoint !== geom.endPoint && geom.radius !== undefined ? "arc" : "circle";
31
+ }
32
+ else if (geom.startPoint !== undefined && geom.endPoint !== undefined) {
33
+ geometryType = "line";
34
+ }
35
+ else {
36
+ geometryType = "unknown";
37
+ }
38
+ }
39
+ const info = {
40
+ id: edge.id,
41
+ body: bodyId,
42
+ type: String(geometryType).toLowerCase(),
43
+ };
44
+ if (typeof geom.radius === "number")
45
+ info.radius = geom.radius / INCH_TO_METER;
46
+ edges.push(info);
47
+ }
48
+ }
49
+ return { edges, count: edges.length, bodyCount: bodies.length };
50
+ }
51
+ async findCircularEdges(documentId, workspaceId, elementId, radius, tolerance = 0.001) {
52
+ const result = await this.getEdges(documentId, workspaceId, elementId);
53
+ const edges = isRecord(result) && Array.isArray(result.edges) ? result.edges : [];
54
+ return edges.filter((edge) => {
55
+ if (!isRecord(edge) || typeof edge.radius !== "number")
56
+ return false;
57
+ return radius === undefined || Math.abs(edge.radius - radius) <= tolerance;
58
+ });
59
+ }
60
+ async findEdgesByFeature(documentId, workspaceId, elementId, featureId) {
61
+ const script = `
62
+ function(context is Context, queries) {
63
+ const feature = qCreatedBy(makeId("${featureId}"), EntityType.EDGE);
64
+ const edges = evaluateQuery(context, feature);
65
+
66
+ const edgeIds = [];
67
+ for (var edge in edges) {
68
+ try {
69
+ const detId = toString(qDeterministicIdQuery(edge));
70
+ edgeIds = append(edgeIds, detId);
71
+ } catch {}
72
+ }
73
+
74
+ return edgeIds;
75
+ }
76
+ `;
77
+ const response = await this.client.post(`/api/v6/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/featurescript`, {
78
+ script,
79
+ });
80
+ const decoded = (0, fsvalue_1.unwrapFeatureScriptResult)(response);
81
+ return Array.isArray(decoded) ? decoded : [];
82
+ }
83
+ }
84
+ exports.EdgeQuery = EdgeQuery;
85
+ function isRecord(value) {
86
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
87
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FeatureScriptError = void 0;
4
+ exports.featurescriptMessages = featurescriptMessages;
5
+ exports.decodeFsValue = decodeFsValue;
6
+ exports.unwrapFeatureScriptResult = unwrapFeatureScriptResult;
7
+ class FeatureScriptError extends Error {
8
+ notices;
9
+ constructor(notices) {
10
+ super(String(notices[0]?.message ?? "unknown FeatureScript error"));
11
+ this.notices = notices;
12
+ this.name = "FeatureScriptError";
13
+ }
14
+ }
15
+ exports.FeatureScriptError = FeatureScriptError;
16
+ function featurescriptMessages(response) {
17
+ if (!isRecord(response) || !Array.isArray(response.notices))
18
+ return [];
19
+ return response.notices
20
+ .filter((notice) => isRecord(notice) && Boolean(notice.message))
21
+ .map((notice) => ({ type: notice.type, message: notice.message }));
22
+ }
23
+ function decodeFsValue(value) {
24
+ if (!isRecord(value))
25
+ return value;
26
+ const btType = String(value.btType ?? "");
27
+ const payload = value.value === undefined && isRecord(value.message) ? value.message : value;
28
+ if (btType.includes("BTFSValueMap") && !btType.includes("Entry")) {
29
+ const out = {};
30
+ const entries = Array.isArray(payload.value) ? payload.value : [];
31
+ for (const entry of entries) {
32
+ if (!isRecord(entry))
33
+ continue;
34
+ out[String(decodeFsValue(entry.key))] = decodeFsValue(entry.value);
35
+ }
36
+ return out;
37
+ }
38
+ if (btType.includes("BTFSValueArray")) {
39
+ const items = Array.isArray(payload.value) ? payload.value : [];
40
+ return items.map(decodeFsValue);
41
+ }
42
+ return payload.value;
43
+ }
44
+ function unwrapFeatureScriptResult(response) {
45
+ if (!isRecord(response) || response.result === null || response.result === undefined)
46
+ return null;
47
+ return decodeFsValue(response.result);
48
+ }
49
+ function isRecord(value) {
50
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
51
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PartStudioManager = void 0;
4
+ exports.loadJson = loadJson;
5
+ const node_fs_1 = require("node:fs");
6
+ class PartStudioManager {
7
+ client;
8
+ constructor(client) {
9
+ this.client = client;
10
+ }
11
+ async getFeatures(documentId, workspaceId, elementId, configuration) {
12
+ return this.client.get(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/features`, {
13
+ configuration,
14
+ });
15
+ }
16
+ async getFeatureSpecs(documentId, workspaceId, elementId) {
17
+ return this.client.get(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/featurespecs`);
18
+ }
19
+ async getSketchInfo(documentId, workspaceId, elementId, sketchId) {
20
+ return this.client.get(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/sketches`, {
21
+ includeGeometry: "true",
22
+ sketchId,
23
+ });
24
+ }
25
+ async getBodyDetails(documentId, workspaceId, elementId) {
26
+ return this.client.get(`/api/v6/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/bodydetails`, {
27
+ includeTopology: "true",
28
+ });
29
+ }
30
+ async getParts(documentId, workspaceId, elementId) {
31
+ return this.client.get(`/api/v9/parts/d/${documentId}/w/${workspaceId}/e/${elementId}`);
32
+ }
33
+ async createPartStudio(documentId, workspaceId, name) {
34
+ return this.client.post(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}`, { name });
35
+ }
36
+ async deleteFeature(documentId, workspaceId, elementId, featureId) {
37
+ return this.client.delete(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/features/featureid/${featureId}`);
38
+ }
39
+ async addFeature(documentId, workspaceId, elementId, feature) {
40
+ return this.client.post(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/features`, feature);
41
+ }
42
+ async updateFeature(documentId, workspaceId, elementId, featureId, feature) {
43
+ return this.client.post(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/features/featureid/${featureId}`, feature);
44
+ }
45
+ async rollback(documentId, workspaceId, elementId, index) {
46
+ return this.client.post(`/api/v9/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/features/rollback`, {
47
+ rollbackIndex: index,
48
+ });
49
+ }
50
+ }
51
+ exports.PartStudioManager = PartStudioManager;
52
+ function loadJson(inline, file) {
53
+ const raw = file ? (0, node_fs_1.readFileSync)(file, "utf8") : inline;
54
+ if (!raw)
55
+ throw new Error("Expected JSON via --json or --json-file");
56
+ return JSON.parse(raw);
57
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,478 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.main = main;
37
+ const promises_1 = require("node:readline/promises");
38
+ const node_process_1 = require("node:process");
39
+ const credentials_1 = require("./credentials");
40
+ const client_1 = require("./api/client");
41
+ const documents_1 = require("./api/documents");
42
+ const edges_1 = require("./api/edges");
43
+ const partstudio_1 = require("./api/partstudio");
44
+ const output_1 = require("./output");
45
+ async function main(argv) {
46
+ try {
47
+ await run(argv);
48
+ }
49
+ catch (error) {
50
+ if (error instanceof credentials_1.CredentialError) {
51
+ (0, output_1.emitError)(error.message);
52
+ process.exitCode = 2;
53
+ return;
54
+ }
55
+ if (error instanceof output_1.CliError) {
56
+ (0, output_1.emitError)(error.message, error.detail);
57
+ process.exitCode = error.exitCode;
58
+ return;
59
+ }
60
+ if (error instanceof client_1.HttpError) {
61
+ (0, output_1.emitError)(`HTTP ${error.status}`, error.detail);
62
+ process.exitCode = 1;
63
+ return;
64
+ }
65
+ throw error;
66
+ }
67
+ }
68
+ async function run(argv) {
69
+ const parsed = parseArgs(argv);
70
+ if (parsed.options.help || parsed.options.h || !parsed.command) {
71
+ printHelp();
72
+ return;
73
+ }
74
+ switch (parsed.command) {
75
+ case "config":
76
+ await handleConfig(parsed);
77
+ return;
78
+ case "login":
79
+ await handleLogin(parsed.options);
80
+ return;
81
+ case "logout":
82
+ (0, output_1.emit)(new credentials_1.CredentialStore().clear());
83
+ return;
84
+ case "list-documents":
85
+ case "search-documents":
86
+ case "get-document":
87
+ case "get-document-summary":
88
+ case "create-document":
89
+ case "delete-document":
90
+ case "update-document":
91
+ case "get-elements":
92
+ case "find-part-studios":
93
+ case "get-workspaces":
94
+ case "list-versions":
95
+ case "create-version":
96
+ case "get-parts":
97
+ case "get-features":
98
+ case "get-feature-specs":
99
+ case "get-sketch-info":
100
+ case "get-body-details":
101
+ case "create-part-studio":
102
+ case "delete-feature":
103
+ case "delete-element":
104
+ case "add-feature":
105
+ case "update-feature":
106
+ case "rollback":
107
+ case "get-edges":
108
+ case "find-circular-edges":
109
+ case "find-edges-by-feature":
110
+ case "mass-properties":
111
+ await handleReadCommand(parsed);
112
+ return;
113
+ default:
114
+ throw new output_1.CliError(`Unknown command: ${parsed.command}`, null, 2);
115
+ }
116
+ }
117
+ function parseArgs(argv) {
118
+ const options = {};
119
+ const positionals = [];
120
+ let command = null;
121
+ for (let index = 0; index < argv.length; index += 1) {
122
+ const item = argv[index];
123
+ if (item.startsWith("--")) {
124
+ const eq = item.indexOf("=");
125
+ const rawKey = item.slice(2, eq === -1 ? undefined : eq);
126
+ const key = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
127
+ if (eq !== -1) {
128
+ options[key] = item.slice(eq + 1);
129
+ }
130
+ else if (argv[index + 1] && !argv[index + 1].startsWith("-")) {
131
+ options[key] = argv[index + 1];
132
+ index += 1;
133
+ }
134
+ else {
135
+ options[key] = true;
136
+ }
137
+ }
138
+ else if (item.startsWith("-") && item.length > 1) {
139
+ options[item.slice(1)] = true;
140
+ }
141
+ else if (!command) {
142
+ command = item;
143
+ }
144
+ else {
145
+ positionals.push(item);
146
+ }
147
+ }
148
+ return { command, positionals, options };
149
+ }
150
+ async function handleConfig(parsed) {
151
+ const action = parsed.positionals[0];
152
+ const store = new credentials_1.CredentialStore();
153
+ switch (action) {
154
+ case "path":
155
+ (0, output_1.emit)({ path: store.configPath() });
156
+ return;
157
+ case "show":
158
+ (0, output_1.emit)(store.describe());
159
+ return;
160
+ case "clear":
161
+ (0, output_1.emit)(store.clear());
162
+ return;
163
+ case "set": {
164
+ const accessKey = stringOption(parsed.options, "accessKey");
165
+ const secretKey = stringOption(parsed.options, "secretKey");
166
+ if (!accessKey || !secretKey) {
167
+ throw new output_1.CliError("config set requires --access-key and --secret-key in the Node CLI.", null, 2);
168
+ }
169
+ const creds = {
170
+ accessKey,
171
+ secretKey,
172
+ baseUrl: stringOption(parsed.options, "baseUrl") ?? credentials_1.DEFAULT_BASE_URL,
173
+ };
174
+ const result = store.save(creds, storeMode(parsed.options));
175
+ (0, output_1.emit)({ ...result, verified: null });
176
+ return;
177
+ }
178
+ default:
179
+ throw new output_1.CliError("Usage: onshape config set|show|path|clear", null, 2);
180
+ }
181
+ }
182
+ async function handleLogin(options) {
183
+ let accessKey = stringOption(options, "accessKey") ?? process.env.ONSHAPE_ACCESS_KEY;
184
+ let secretKey = stringOption(options, "secretKey") ?? process.env.ONSHAPE_SECRET_KEY;
185
+ const baseUrl = stringOption(options, "baseUrl") ?? process.env.ONSHAPE_BASE_URL ?? credentials_1.DEFAULT_BASE_URL;
186
+ if ((!accessKey || !secretKey) && !process.stdin.isTTY) {
187
+ throw new output_1.CliError("login cannot prompt in a non-interactive terminal. Pass --access-key and --secret-key.", null, 2);
188
+ }
189
+ if (!accessKey || !secretKey) {
190
+ if (!options.noBrowser) {
191
+ const open = await Promise.resolve().then(() => __importStar(require("open")));
192
+ await open.default("https://dev.onshape.com/keys");
193
+ }
194
+ const rl = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
195
+ try {
196
+ accessKey = accessKey ?? (await rl.question("Onshape access key: "));
197
+ secretKey = secretKey ?? (await rl.question("Onshape secret key: "));
198
+ }
199
+ finally {
200
+ rl.close();
201
+ }
202
+ }
203
+ if (!accessKey || !secretKey) {
204
+ throw new output_1.CliError("Both an access key and a secret key are required.", null, 2);
205
+ }
206
+ const creds = { accessKey, secretKey, baseUrl };
207
+ let verified = null;
208
+ let verifyError;
209
+ if (!options.noVerify) {
210
+ try {
211
+ await new client_1.OnshapeClient(creds).get("/api/v6/documents", { limit: 1 });
212
+ verified = true;
213
+ }
214
+ catch (error) {
215
+ verified = false;
216
+ verifyError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
217
+ }
218
+ }
219
+ const result = {
220
+ ...new credentials_1.CredentialStore().save(creds, storeMode(options)),
221
+ verified,
222
+ };
223
+ if (verifyError)
224
+ result.verifyError = verifyError;
225
+ (0, output_1.emit)(result);
226
+ }
227
+ async function handleReadCommand(parsed) {
228
+ const creds = resolveCredentials(parsed.options);
229
+ const client = new client_1.OnshapeClient(creds);
230
+ const docs = new documents_1.DocumentManager(client);
231
+ const partstudios = new partstudio_1.PartStudioManager(client);
232
+ const edges = new edges_1.EdgeQuery(client);
233
+ switch (parsed.command) {
234
+ case "list-documents": {
235
+ const filterMap = { all: undefined, owned: 1, created: 4, shared: 5 };
236
+ const filter = stringOption(parsed.options, "filter") ?? "all";
237
+ (0, output_1.emit)(await docs.listDocuments({
238
+ filterType: filterMap[filter],
239
+ limit: numberOption(parsed.options, "limit", 20),
240
+ sortBy: stringOption(parsed.options, "sortBy") ?? "modifiedAt",
241
+ sortOrder: stringOption(parsed.options, "sortOrder") ?? "desc",
242
+ }));
243
+ return;
244
+ }
245
+ case "search-documents":
246
+ (0, output_1.emit)(await docs.searchDocuments(parsed.positionals[0] ?? missing("query"), numberOption(parsed.options, "limit", 20)));
247
+ return;
248
+ case "get-document":
249
+ (0, output_1.emit)(await docs.getDocument(requiredOption(parsed.options, "doc")));
250
+ return;
251
+ case "get-document-summary":
252
+ (0, output_1.emit)(await docs.getDocumentSummary(requiredOption(parsed.options, "doc")));
253
+ return;
254
+ case "create-document":
255
+ (0, output_1.emit)(await docs.createDocument(requiredOption(parsed.options, "name"), Boolean(parsed.options.public), stringOption(parsed.options, "description")));
256
+ return;
257
+ case "delete-document":
258
+ (0, output_1.emit)(await docs.deleteDocument(requiredOption(parsed.options, "doc")));
259
+ return;
260
+ case "update-document":
261
+ (0, output_1.emit)(await docs.updateDocument(requiredOption(parsed.options, "doc"), stringOption(parsed.options, "name"), stringOption(parsed.options, "description")));
262
+ return;
263
+ case "get-elements": {
264
+ const { doc, ws } = docWorkspace(parsed.options);
265
+ (0, output_1.emit)(await docs.getElements(doc, ws, stringOption(parsed.options, "type")));
266
+ return;
267
+ }
268
+ case "find-part-studios": {
269
+ const { doc, ws } = docWorkspace(parsed.options);
270
+ (0, output_1.emit)(await docs.findPartStudios(doc, ws, stringOption(parsed.options, "name")));
271
+ return;
272
+ }
273
+ case "get-workspaces":
274
+ (0, output_1.emit)(await docs.getWorkspaces(requiredOption(parsed.options, "doc")));
275
+ return;
276
+ case "list-versions":
277
+ (0, output_1.emit)(await docs.getVersions(requiredOption(parsed.options, "doc")));
278
+ return;
279
+ case "create-version": {
280
+ const { doc, ws } = docWorkspace(parsed.options);
281
+ (0, output_1.emit)(await docs.createVersion(doc, ws, requiredOption(parsed.options, "name"), stringOption(parsed.options, "description")));
282
+ return;
283
+ }
284
+ case "get-features": {
285
+ const { doc, ws, elem } = dwe(parsed.options);
286
+ (0, output_1.emit)(await partstudios.getFeatures(doc, ws, elem, stringOption(parsed.options, "configuration")));
287
+ return;
288
+ }
289
+ case "get-feature-specs": {
290
+ const { doc, ws, elem } = dwe(parsed.options);
291
+ (0, output_1.emit)(await partstudios.getFeatureSpecs(doc, ws, elem));
292
+ return;
293
+ }
294
+ case "get-sketch-info": {
295
+ const { doc, ws, elem } = dwe(parsed.options);
296
+ (0, output_1.emit)(await partstudios.getSketchInfo(doc, ws, elem, stringOption(parsed.options, "sketch")));
297
+ return;
298
+ }
299
+ case "get-body-details": {
300
+ const { doc, ws, elem } = dwe(parsed.options);
301
+ (0, output_1.emit)(await partstudios.getBodyDetails(doc, ws, elem));
302
+ return;
303
+ }
304
+ case "get-parts": {
305
+ const { doc, ws, elem } = dwe(parsed.options);
306
+ (0, output_1.emit)(await partstudios.getParts(doc, ws, elem));
307
+ return;
308
+ }
309
+ case "create-part-studio": {
310
+ const { doc, ws } = docWorkspace(parsed.options);
311
+ (0, output_1.emit)(await partstudios.createPartStudio(doc, ws, requiredOption(parsed.options, "name")));
312
+ return;
313
+ }
314
+ case "delete-feature": {
315
+ const { doc, ws, elem } = dwe(parsed.options);
316
+ (0, output_1.emit)(await partstudios.deleteFeature(doc, ws, elem, requiredOption(parsed.options, "feature")));
317
+ return;
318
+ }
319
+ case "delete-element": {
320
+ const { doc, ws, elem } = dwe(parsed.options);
321
+ (0, output_1.emit)(await client.delete(`/api/v9/elements/d/${doc}/w/${ws}/e/${elem}`));
322
+ return;
323
+ }
324
+ case "add-feature": {
325
+ const { doc, ws, elem } = dwe(parsed.options);
326
+ const feature = requiredJson(parsed.options);
327
+ (0, output_1.emit)(await withFeatureId(partstudios.addFeature(doc, ws, elem, feature)));
328
+ return;
329
+ }
330
+ case "update-feature": {
331
+ const { doc, ws, elem } = dwe(parsed.options);
332
+ const feature = requiredJson(parsed.options);
333
+ (0, output_1.emit)(await partstudios.updateFeature(doc, ws, elem, requiredOption(parsed.options, "feature"), feature));
334
+ return;
335
+ }
336
+ case "rollback": {
337
+ const { doc, ws, elem } = dwe(parsed.options);
338
+ (0, output_1.emit)(await partstudios.rollback(doc, ws, elem, requiredNumberOption(parsed.options, "index")));
339
+ return;
340
+ }
341
+ case "get-edges": {
342
+ const { doc, ws, elem } = dwe(parsed.options);
343
+ (0, output_1.emit)(await edges.getEdges(doc, ws, elem));
344
+ return;
345
+ }
346
+ case "find-circular-edges": {
347
+ const { doc, ws, elem } = dwe(parsed.options);
348
+ (0, output_1.emit)(await edges.findCircularEdges(doc, ws, elem, optionalNumberOption(parsed.options, "radius")));
349
+ return;
350
+ }
351
+ case "find-edges-by-feature": {
352
+ const { doc, ws, elem } = dwe(parsed.options);
353
+ (0, output_1.emit)(await edges.findEdgesByFeature(doc, ws, elem, requiredOption(parsed.options, "feature")));
354
+ return;
355
+ }
356
+ case "mass-properties": {
357
+ const { doc, ws, elem } = dwe(parsed.options);
358
+ (0, output_1.emit)(await client.get(`/api/v6/partstudios/d/${doc}/w/${ws}/e/${elem}/massproperties`, {
359
+ configuration: stringOption(parsed.options, "configuration"),
360
+ }));
361
+ return;
362
+ }
363
+ }
364
+ }
365
+ async function withFeatureId(responsePromise) {
366
+ const response = await responsePromise;
367
+ const featureId = isRecord(response) && isRecord(response.feature) ? response.feature.featureId ?? null : null;
368
+ return { featureId, response };
369
+ }
370
+ function isRecord(value) {
371
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
372
+ }
373
+ function resolveCredentials(options) {
374
+ return new credentials_1.CredentialStore().resolve({
375
+ accessKey: stringOption(options, "accessKey"),
376
+ secretKey: stringOption(options, "secretKey"),
377
+ baseUrl: stringOption(options, "baseUrl"),
378
+ });
379
+ }
380
+ function dwe(options) {
381
+ return {
382
+ doc: stringOption(options, "doc") ?? process.env.ONSHAPE_DOC ?? missing("doc"),
383
+ ws: stringOption(options, "ws") ?? process.env.ONSHAPE_WS ?? missing("ws"),
384
+ elem: stringOption(options, "elem") ?? process.env.ONSHAPE_ELEM ?? missing("elem"),
385
+ };
386
+ }
387
+ function docWorkspace(options) {
388
+ return {
389
+ doc: stringOption(options, "doc") ?? process.env.ONSHAPE_DOC ?? missing("doc"),
390
+ ws: stringOption(options, "ws") ?? process.env.ONSHAPE_WS ?? missing("ws"),
391
+ };
392
+ }
393
+ function stringOption(options, key) {
394
+ const value = options[key];
395
+ return typeof value === "string" ? value : undefined;
396
+ }
397
+ function numberOption(options, key, fallback) {
398
+ const value = stringOption(options, key);
399
+ return value === undefined ? fallback : Number(value);
400
+ }
401
+ function optionalNumberOption(options, key) {
402
+ const value = stringOption(options, key);
403
+ if (value === undefined)
404
+ return undefined;
405
+ const number = Number(value);
406
+ if (!Number.isFinite(number))
407
+ throw new output_1.CliError(`--${key} must be a number`, null, 2);
408
+ return number;
409
+ }
410
+ function requiredNumberOption(options, key) {
411
+ const value = stringOption(options, key);
412
+ if (value === undefined)
413
+ missing(key);
414
+ const number = Number(value);
415
+ if (!Number.isFinite(number))
416
+ throw new output_1.CliError(`--${key} must be a number`, null, 2);
417
+ return number;
418
+ }
419
+ function requiredOption(options, key) {
420
+ return stringOption(options, key) ?? missing(key);
421
+ }
422
+ function requiredJson(options) {
423
+ try {
424
+ return (0, partstudio_1.loadJson)(stringOption(options, "json"), stringOption(options, "jsonFile"));
425
+ }
426
+ catch (error) {
427
+ const message = error instanceof Error ? error.message : String(error);
428
+ throw new output_1.CliError(message, null, 2);
429
+ }
430
+ }
431
+ function missing(key) {
432
+ throw new output_1.CliError(`Missing required option --${key}`, null, 2);
433
+ }
434
+ function storeMode(options) {
435
+ const value = stringOption(options, "store") ?? "auto";
436
+ if (value === "auto" || value === "file" || value === "keychain")
437
+ return value;
438
+ throw new output_1.CliError("--store must be one of: auto, file, keychain", null, 2);
439
+ }
440
+ function printHelp() {
441
+ console.log(`onshape
442
+
443
+ Usage:
444
+ onshape <command> [options]
445
+
446
+ Commands:
447
+ login
448
+ logout
449
+ config set|show|path|clear
450
+ list-documents
451
+ search-documents
452
+ get-document
453
+ get-document-summary
454
+ create-document
455
+ delete-document
456
+ update-document
457
+ get-elements
458
+ find-part-studios
459
+ get-workspaces
460
+ list-versions
461
+ create-version
462
+ get-parts
463
+ get-features
464
+ get-feature-specs
465
+ get-sketch-info
466
+ get-body-details
467
+ create-part-studio
468
+ delete-feature
469
+ delete-element
470
+ add-feature
471
+ update-feature
472
+ rollback
473
+ get-edges
474
+ find-circular-edges
475
+ find-edges-by-feature
476
+ mass-properties
477
+ `);
478
+ }
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CredentialStore = exports.CredentialError = exports.DEFAULT_BASE_URL = void 0;
4
+ exports.redactSecret = redactSecret;
5
+ exports.accountForBaseUrl = accountForBaseUrl;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_os_1 = require("node:os");
8
+ const node_path_1 = require("node:path");
9
+ exports.DEFAULT_BASE_URL = "https://cad.onshape.com";
10
+ const KEYCHAIN_SERVICE = "onshape-cli";
11
+ class CredentialError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "CredentialError";
15
+ }
16
+ }
17
+ exports.CredentialError = CredentialError;
18
+ function redactSecret(secret) {
19
+ return secret.length > 8 ? `${secret.slice(0, 3)}...${secret.slice(-3)}` : "***";
20
+ }
21
+ function accountForBaseUrl(baseUrl) {
22
+ try {
23
+ return new URL(baseUrl).hostname;
24
+ }
25
+ catch {
26
+ try {
27
+ return new URL(`https://${baseUrl}`).hostname;
28
+ }
29
+ catch {
30
+ return baseUrl;
31
+ }
32
+ }
33
+ }
34
+ function expandHome(value) {
35
+ if (value === "~")
36
+ return (0, node_os_1.homedir)();
37
+ if (value.startsWith("~/"))
38
+ return (0, node_path_1.join)((0, node_os_1.homedir)(), value.slice(2));
39
+ return value;
40
+ }
41
+ function readJson(path) {
42
+ if (!(0, node_fs_1.existsSync)(path))
43
+ return {};
44
+ try {
45
+ const value = JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
46
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
47
+ }
48
+ catch {
49
+ return {};
50
+ }
51
+ }
52
+ function writeSecureJson(path, data) {
53
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
54
+ try {
55
+ (0, node_fs_1.chmodSync)((0, node_path_1.dirname)(path), 0o700);
56
+ }
57
+ catch {
58
+ // chmod is best-effort on some platforms.
59
+ }
60
+ (0, node_fs_1.writeFileSync)(path, `${JSON.stringify(data, null, 2)}\n`);
61
+ try {
62
+ (0, node_fs_1.chmodSync)(path, 0o600);
63
+ }
64
+ catch {
65
+ // chmod is best-effort on some platforms.
66
+ }
67
+ }
68
+ function loadKeyringEntry(account) {
69
+ try {
70
+ // Optional dependency. Keep it lazy so headless/CI installs can use file storage.
71
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
72
+ const keyring = require("@napi-rs/keyring");
73
+ return new keyring.Entry(KEYCHAIN_SERVICE, account);
74
+ }
75
+ catch (error) {
76
+ const message = error instanceof Error ? error.message : String(error);
77
+ throw new CredentialError(message);
78
+ }
79
+ }
80
+ class CredentialStore {
81
+ configPath() {
82
+ return process.env.ONSHAPE_CONFIG ? expandHome(process.env.ONSHAPE_CONFIG) : (0, node_path_1.join)((0, node_os_1.homedir)(), ".onshape", "credentials.json");
83
+ }
84
+ xdgConfigPath() {
85
+ if ((0, node_os_1.platform)() !== "linux" || !process.env.XDG_CONFIG_HOME)
86
+ return null;
87
+ return (0, node_path_1.join)(expandHome(process.env.XDG_CONFIG_HOME), "onshape", "credentials.json");
88
+ }
89
+ resolve(overrides = {}) {
90
+ const fileCreds = this.firstFileCredentials();
91
+ const mcpCreds = this.mcpCredentials();
92
+ const sourceBase = fileCreds?.baseUrl ?? mcpCreds?.baseUrl ?? exports.DEFAULT_BASE_URL;
93
+ const accessKey = overrides.accessKey ?? process.env.ONSHAPE_ACCESS_KEY ?? fileCreds?.accessKey ?? mcpCreds?.accessKey;
94
+ const secretKey = overrides.secretKey ?? process.env.ONSHAPE_SECRET_KEY ?? fileCreds?.secretKey ?? mcpCreds?.secretKey;
95
+ const baseUrl = overrides.baseUrl ?? process.env.ONSHAPE_BASE_URL ?? sourceBase;
96
+ if (!accessKey || !secretKey) {
97
+ throw new CredentialError("No credentials. Run 'onshape login', set ONSHAPE_ACCESS_KEY / ONSHAPE_SECRET_KEY, or pass --access-key/--secret-key.");
98
+ }
99
+ return { accessKey, secretKey, baseUrl };
100
+ }
101
+ save(creds, store = "auto") {
102
+ if (store !== "auto" && store !== "file" && store !== "keychain") {
103
+ throw new CredentialError("store must be one of: auto, file, keychain");
104
+ }
105
+ if (store === "auto" || store === "keychain") {
106
+ try {
107
+ return this.saveKeychain(creds);
108
+ }
109
+ catch (error) {
110
+ if (store === "keychain")
111
+ throw error;
112
+ }
113
+ }
114
+ writeSecureJson(this.configPath(), {
115
+ backend: "file",
116
+ access_key: creds.accessKey,
117
+ secret_key: creds.secretKey,
118
+ base_url: creds.baseUrl,
119
+ });
120
+ return { saved: true, backend: "file", path: this.configPath() };
121
+ }
122
+ clear() {
123
+ const path = this.configPath();
124
+ const data = readJson(path);
125
+ const existed = (0, node_fs_1.existsSync)(path);
126
+ if (existed)
127
+ (0, node_fs_1.rmSync)(path);
128
+ let keychainDeleted = false;
129
+ if (data.backend === "keychain") {
130
+ const account = data.account ?? accountForBaseUrl(data.base_url ?? exports.DEFAULT_BASE_URL);
131
+ try {
132
+ keychainDeleted = loadKeyringEntry(account).deleteCredential();
133
+ }
134
+ catch {
135
+ keychainDeleted = false;
136
+ }
137
+ }
138
+ return { cleared: existed || keychainDeleted, path, keychain_deleted: keychainDeleted };
139
+ }
140
+ describe() {
141
+ const path = this.configPath();
142
+ const data = readJson(path);
143
+ if (!data.access_key)
144
+ return { configured: false, path, base_url: exports.DEFAULT_BASE_URL };
145
+ const backend = data.backend ?? (data.secret_key ? "file" : undefined);
146
+ const baseUrl = data.base_url ?? exports.DEFAULT_BASE_URL;
147
+ const result = {
148
+ configured: true,
149
+ path,
150
+ base_url: baseUrl,
151
+ backend,
152
+ source: "file",
153
+ access_key: data.access_key,
154
+ };
155
+ let secret = data.secret_key;
156
+ if (backend === "keychain") {
157
+ const account = data.account ?? accountForBaseUrl(baseUrl);
158
+ result.account = account;
159
+ try {
160
+ const raw = loadKeyringEntry(account).getPassword();
161
+ secret = raw ? JSON.parse(raw).secret_key : undefined;
162
+ result.keychain_available = true;
163
+ }
164
+ catch {
165
+ result.keychain_available = false;
166
+ }
167
+ }
168
+ if (secret)
169
+ result.secret_key = redactSecret(secret);
170
+ return result;
171
+ }
172
+ saveKeychain(creds) {
173
+ const account = accountForBaseUrl(creds.baseUrl);
174
+ const value = JSON.stringify({
175
+ access_key: creds.accessKey,
176
+ secret_key: creds.secretKey,
177
+ base_url: creds.baseUrl,
178
+ });
179
+ loadKeyringEntry(account).setPassword(value);
180
+ writeSecureJson(this.configPath(), {
181
+ backend: "keychain",
182
+ access_key: creds.accessKey,
183
+ base_url: creds.baseUrl,
184
+ account,
185
+ });
186
+ return { saved: true, backend: "keychain", path: this.configPath(), account };
187
+ }
188
+ firstFileCredentials() {
189
+ for (const path of [this.configPath(), this.xdgConfigPath()]) {
190
+ if (!path)
191
+ continue;
192
+ const creds = this.credentialsFromFile(path);
193
+ if (creds)
194
+ return creds;
195
+ }
196
+ return null;
197
+ }
198
+ credentialsFromFile(path) {
199
+ const data = readJson(path);
200
+ const baseUrl = data.base_url ?? exports.DEFAULT_BASE_URL;
201
+ if (data.backend === "keychain") {
202
+ const account = data.account ?? accountForBaseUrl(baseUrl);
203
+ try {
204
+ const raw = loadKeyringEntry(account).getPassword();
205
+ if (!raw)
206
+ return null;
207
+ const stored = JSON.parse(raw);
208
+ if (!stored.secret_key)
209
+ return null;
210
+ return {
211
+ accessKey: stored.access_key ?? data.access_key ?? "",
212
+ secretKey: stored.secret_key,
213
+ baseUrl: stored.base_url ?? baseUrl,
214
+ };
215
+ }
216
+ catch {
217
+ return null;
218
+ }
219
+ }
220
+ if (data.access_key && data.secret_key) {
221
+ return { accessKey: data.access_key, secretKey: data.secret_key, baseUrl };
222
+ }
223
+ return null;
224
+ }
225
+ mcpCredentials() {
226
+ const data = readJson((0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "mcp.json"));
227
+ const env = data?.mcpServers?.onshape?.env;
228
+ if (!env?.ONSHAPE_ACCESS_KEY || !env?.ONSHAPE_SECRET_KEY)
229
+ return null;
230
+ return {
231
+ accessKey: env.ONSHAPE_ACCESS_KEY,
232
+ secretKey: env.ONSHAPE_SECRET_KEY,
233
+ baseUrl: env.ONSHAPE_BASE_URL ?? exports.DEFAULT_BASE_URL,
234
+ };
235
+ }
236
+ }
237
+ exports.CredentialStore = CredentialStore;
package/dist/output.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliError = void 0;
4
+ exports.emit = emit;
5
+ exports.emitError = emitError;
6
+ function emit(result) {
7
+ console.log(JSON.stringify({ ok: true, result }, null, 2));
8
+ }
9
+ function emitError(error, detail = null) {
10
+ console.log(JSON.stringify({ ok: false, error, detail }, null, 2));
11
+ }
12
+ class CliError extends Error {
13
+ detail;
14
+ exitCode;
15
+ constructor(message, detail = null, exitCode = 1) {
16
+ super(message);
17
+ this.name = "CliError";
18
+ this.detail = detail;
19
+ this.exitCode = exitCode;
20
+ }
21
+ }
22
+ exports.CliError = CliError;
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "onshape",
3
+ "version": "0.1.0",
4
+ "description": "Node.js CLI for Onshape CAD automation with the same JSON contract as onshape-cli.",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "bin": {
8
+ "onshape": "bin/onshape.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "dist",
13
+ "package.json",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.json",
21
+ "check": "tsc -p tsconfig.json --noEmit"
22
+ },
23
+ "dependencies": {
24
+ "open": "^10.2.0"
25
+ },
26
+ "optionalDependencies": {
27
+ "@napi-rs/keyring": "^1.3.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^24.0.0",
31
+ "typescript": "^5.9.0"
32
+ }
33
+ }