@zivis/mcp 0.1.0-alpha.1

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 (253) hide show
  1. package/dist/api-client.d.ts +42 -0
  2. package/dist/api-client.js +145 -0
  3. package/dist/api-client.js.map +1 -0
  4. package/dist/auth/index.d.ts +29 -0
  5. package/dist/auth/index.js +73 -0
  6. package/dist/auth/index.js.map +1 -0
  7. package/dist/auth/jwt.d.ts +24 -0
  8. package/dist/auth/jwt.js +25 -0
  9. package/dist/auth/jwt.js.map +1 -0
  10. package/dist/auth/keychain.d.ts +34 -0
  11. package/dist/auth/keychain.js +272 -0
  12. package/dist/auth/keychain.js.map +1 -0
  13. package/dist/auth/oauth.d.ts +38 -0
  14. package/dist/auth/oauth.js +241 -0
  15. package/dist/auth/oauth.js.map +1 -0
  16. package/dist/auth/sessions.d.ts +28 -0
  17. package/dist/auth/sessions.js +69 -0
  18. package/dist/auth/sessions.js.map +1 -0
  19. package/dist/auth/token-refresh.d.ts +24 -0
  20. package/dist/auth/token-refresh.js +91 -0
  21. package/dist/auth/token-refresh.js.map +1 -0
  22. package/dist/cli.d.ts +23 -0
  23. package/dist/cli.js +962 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/index.d.ts +8 -0
  26. package/dist/index.js +15 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/project-binding.d.ts +25 -0
  29. package/dist/project-binding.js +67 -0
  30. package/dist/project-binding.js.map +1 -0
  31. package/dist/resolve-application-id.d.ts +12 -0
  32. package/dist/resolve-application-id.js +21 -0
  33. package/dist/resolve-application-id.js.map +1 -0
  34. package/dist/sanitize.d.ts +23 -0
  35. package/dist/sanitize.js +125 -0
  36. package/dist/sanitize.js.map +1 -0
  37. package/dist/server.d.ts +14 -0
  38. package/dist/server.js +447 -0
  39. package/dist/server.js.map +1 -0
  40. package/dist/tools/check-project.d.ts +32 -0
  41. package/dist/tools/check-project.js +180 -0
  42. package/dist/tools/check-project.js.map +1 -0
  43. package/dist/tools/check-repo-trust.d.ts +33 -0
  44. package/dist/tools/check-repo-trust.js +212 -0
  45. package/dist/tools/check-repo-trust.js.map +1 -0
  46. package/dist/tools/create-diagram.d.ts +114 -0
  47. package/dist/tools/create-diagram.js +176 -0
  48. package/dist/tools/create-diagram.js.map +1 -0
  49. package/dist/tools/create-document.d.ts +28 -0
  50. package/dist/tools/create-document.js +53 -0
  51. package/dist/tools/create-document.js.map +1 -0
  52. package/dist/tools/discover-local-infra.d.ts +24 -0
  53. package/dist/tools/discover-local-infra.js +763 -0
  54. package/dist/tools/discover-local-infra.js.map +1 -0
  55. package/dist/tools/enterprise/capture-evidence.d.ts +54 -0
  56. package/dist/tools/enterprise/capture-evidence.js +72 -0
  57. package/dist/tools/enterprise/capture-evidence.js.map +1 -0
  58. package/dist/tools/enterprise/create-report-ticket.d.ts +34 -0
  59. package/dist/tools/enterprise/create-report-ticket.js +44 -0
  60. package/dist/tools/enterprise/create-report-ticket.js.map +1 -0
  61. package/dist/tools/enterprise/end-pentest-session.d.ts +33 -0
  62. package/dist/tools/enterprise/end-pentest-session.js +42 -0
  63. package/dist/tools/enterprise/end-pentest-session.js.map +1 -0
  64. package/dist/tools/enterprise/generate-report.d.ts +55 -0
  65. package/dist/tools/enterprise/generate-report.js +111 -0
  66. package/dist/tools/enterprise/generate-report.js.map +1 -0
  67. package/dist/tools/enterprise/get-org-zat.d.ts +27 -0
  68. package/dist/tools/enterprise/get-org-zat.js +62 -0
  69. package/dist/tools/enterprise/get-org-zat.js.map +1 -0
  70. package/dist/tools/enterprise/get-report-status.d.ts +27 -0
  71. package/dist/tools/enterprise/get-report-status.js +38 -0
  72. package/dist/tools/enterprise/get-report-status.js.map +1 -0
  73. package/dist/tools/enterprise/get-threat-model.d.ts +29 -0
  74. package/dist/tools/enterprise/get-threat-model.js +83 -0
  75. package/dist/tools/enterprise/get-threat-model.js.map +1 -0
  76. package/dist/tools/enterprise/list-agents.d.ts +42 -0
  77. package/dist/tools/enterprise/list-agents.js +95 -0
  78. package/dist/tools/enterprise/list-agents.js.map +1 -0
  79. package/dist/tools/enterprise/list-report-issues.d.ts +36 -0
  80. package/dist/tools/enterprise/list-report-issues.js +50 -0
  81. package/dist/tools/enterprise/list-report-issues.js.map +1 -0
  82. package/dist/tools/enterprise/list-threat-models.d.ts +36 -0
  83. package/dist/tools/enterprise/list-threat-models.js +82 -0
  84. package/dist/tools/enterprise/list-threat-models.js.map +1 -0
  85. package/dist/tools/enterprise/manage-actor.d.ts +58 -0
  86. package/dist/tools/enterprise/manage-actor.js +99 -0
  87. package/dist/tools/enterprise/manage-actor.js.map +1 -0
  88. package/dist/tools/enterprise/manage-attack-scenario.d.ts +70 -0
  89. package/dist/tools/enterprise/manage-attack-scenario.js +89 -0
  90. package/dist/tools/enterprise/manage-attack-scenario.js.map +1 -0
  91. package/dist/tools/enterprise/manage-component.d.ts +58 -0
  92. package/dist/tools/enterprise/manage-component.js +90 -0
  93. package/dist/tools/enterprise/manage-component.js.map +1 -0
  94. package/dist/tools/enterprise/manage-data-asset.d.ts +53 -0
  95. package/dist/tools/enterprise/manage-data-asset.js +90 -0
  96. package/dist/tools/enterprise/manage-data-asset.js.map +1 -0
  97. package/dist/tools/enterprise/manage-data-flow.d.ts +57 -0
  98. package/dist/tools/enterprise/manage-data-flow.js +98 -0
  99. package/dist/tools/enterprise/manage-data-flow.js.map +1 -0
  100. package/dist/tools/enterprise/manage-finding.d.ts +69 -0
  101. package/dist/tools/enterprise/manage-finding.js +106 -0
  102. package/dist/tools/enterprise/manage-finding.js.map +1 -0
  103. package/dist/tools/enterprise/manage-security-control.d.ts +59 -0
  104. package/dist/tools/enterprise/manage-security-control.js +96 -0
  105. package/dist/tools/enterprise/manage-security-control.js.map +1 -0
  106. package/dist/tools/enterprise/manage-test-case.d.ts +88 -0
  107. package/dist/tools/enterprise/manage-test-case.js +119 -0
  108. package/dist/tools/enterprise/manage-test-case.js.map +1 -0
  109. package/dist/tools/enterprise/promote-finding-to-issue.d.ts +36 -0
  110. package/dist/tools/enterprise/promote-finding-to-issue.js +65 -0
  111. package/dist/tools/enterprise/promote-finding-to-issue.js.map +1 -0
  112. package/dist/tools/enterprise/publish-report-version.d.ts +34 -0
  113. package/dist/tools/enterprise/publish-report-version.js +75 -0
  114. package/dist/tools/enterprise/publish-report-version.js.map +1 -0
  115. package/dist/tools/enterprise/publish-test-to-library.d.ts +31 -0
  116. package/dist/tools/enterprise/publish-test-to-library.js +40 -0
  117. package/dist/tools/enterprise/publish-test-to-library.js.map +1 -0
  118. package/dist/tools/enterprise/record-test-result.d.ts +67 -0
  119. package/dist/tools/enterprise/record-test-result.js +75 -0
  120. package/dist/tools/enterprise/record-test-result.js.map +1 -0
  121. package/dist/tools/enterprise/start-pentest-session.d.ts +35 -0
  122. package/dist/tools/enterprise/start-pentest-session.js +50 -0
  123. package/dist/tools/enterprise/start-pentest-session.js.map +1 -0
  124. package/dist/tools/enterprise/sync-report-section.d.ts +41 -0
  125. package/dist/tools/enterprise/sync-report-section.js +79 -0
  126. package/dist/tools/enterprise/sync-report-section.js.map +1 -0
  127. package/dist/tools/enterprise/update-report-issue.d.ts +55 -0
  128. package/dist/tools/enterprise/update-report-issue.js +69 -0
  129. package/dist/tools/enterprise/update-report-issue.js.map +1 -0
  130. package/dist/tools/generate-diagram.d.ts +36 -0
  131. package/dist/tools/generate-diagram.js +153 -0
  132. package/dist/tools/generate-diagram.js.map +1 -0
  133. package/dist/tools/get-agent-test-results.d.ts +41 -0
  134. package/dist/tools/get-agent-test-results.js +86 -0
  135. package/dist/tools/get-agent-test-results.js.map +1 -0
  136. package/dist/tools/get-application-overview.d.ts +27 -0
  137. package/dist/tools/get-application-overview.js +143 -0
  138. package/dist/tools/get-application-overview.js.map +1 -0
  139. package/dist/tools/get-application.d.ts +27 -0
  140. package/dist/tools/get-application.js +83 -0
  141. package/dist/tools/get-application.js.map +1 -0
  142. package/dist/tools/get-artifact-content.d.ts +29 -0
  143. package/dist/tools/get-artifact-content.js +86 -0
  144. package/dist/tools/get-artifact-content.js.map +1 -0
  145. package/dist/tools/get-diagram.d.ts +27 -0
  146. package/dist/tools/get-diagram.js +109 -0
  147. package/dist/tools/get-diagram.js.map +1 -0
  148. package/dist/tools/get-document.d.ts +22 -0
  149. package/dist/tools/get-document.js +43 -0
  150. package/dist/tools/get-document.js.map +1 -0
  151. package/dist/tools/get-finding-details.d.ts +28 -0
  152. package/dist/tools/get-finding-details.js +43 -0
  153. package/dist/tools/get-finding-details.js.map +1 -0
  154. package/dist/tools/get-findings.d.ts +57 -0
  155. package/dist/tools/get-findings.js +103 -0
  156. package/dist/tools/get-findings.js.map +1 -0
  157. package/dist/tools/get-oss-zat.d.ts +27 -0
  158. package/dist/tools/get-oss-zat.js +105 -0
  159. package/dist/tools/get-oss-zat.js.map +1 -0
  160. package/dist/tools/get-recon-scan.d.ts +27 -0
  161. package/dist/tools/get-recon-scan.js +46 -0
  162. package/dist/tools/get-recon-scan.js.map +1 -0
  163. package/dist/tools/get-scan-output.d.ts +27 -0
  164. package/dist/tools/get-scan-output.js +64 -0
  165. package/dist/tools/get-scan-output.js.map +1 -0
  166. package/dist/tools/get-scenario-details.d.ts +27 -0
  167. package/dist/tools/get-scenario-details.js +46 -0
  168. package/dist/tools/get-scenario-details.js.map +1 -0
  169. package/dist/tools/get-test-case-results.d.ts +37 -0
  170. package/dist/tools/get-test-case-results.js +66 -0
  171. package/dist/tools/get-test-case-results.js.map +1 -0
  172. package/dist/tools/get-test-details.d.ts +29 -0
  173. package/dist/tools/get-test-details.js +49 -0
  174. package/dist/tools/get-test-details.js.map +1 -0
  175. package/dist/tools/get-trust-keys.d.ts +22 -0
  176. package/dist/tools/get-trust-keys.js +67 -0
  177. package/dist/tools/get-trust-keys.js.map +1 -0
  178. package/dist/tools/import-openapi-endpoints.d.ts +33 -0
  179. package/dist/tools/import-openapi-endpoints.js +93 -0
  180. package/dist/tools/import-openapi-endpoints.js.map +1 -0
  181. package/dist/tools/inspect-zat.d.ts +26 -0
  182. package/dist/tools/inspect-zat.js +247 -0
  183. package/dist/tools/inspect-zat.js.map +1 -0
  184. package/dist/tools/list-agent-test-cases.d.ts +41 -0
  185. package/dist/tools/list-agent-test-cases.js +73 -0
  186. package/dist/tools/list-agent-test-cases.js.map +1 -0
  187. package/dist/tools/list-applications.d.ts +34 -0
  188. package/dist/tools/list-applications.js +74 -0
  189. package/dist/tools/list-applications.js.map +1 -0
  190. package/dist/tools/list-diagrams.d.ts +42 -0
  191. package/dist/tools/list-diagrams.js +81 -0
  192. package/dist/tools/list-diagrams.js.map +1 -0
  193. package/dist/tools/list-documents.d.ts +24 -0
  194. package/dist/tools/list-documents.js +50 -0
  195. package/dist/tools/list-documents.js.map +1 -0
  196. package/dist/tools/list-endpoints.d.ts +47 -0
  197. package/dist/tools/list-endpoints.js +98 -0
  198. package/dist/tools/list-endpoints.js.map +1 -0
  199. package/dist/tools/list-recon-scans.d.ts +36 -0
  200. package/dist/tools/list-recon-scans.js +74 -0
  201. package/dist/tools/list-recon-scans.js.map +1 -0
  202. package/dist/tools/list-scan-artifacts.d.ts +41 -0
  203. package/dist/tools/list-scan-artifacts.js +63 -0
  204. package/dist/tools/list-scan-artifacts.js.map +1 -0
  205. package/dist/tools/list-scans.d.ts +44 -0
  206. package/dist/tools/list-scans.js +91 -0
  207. package/dist/tools/list-scans.js.map +1 -0
  208. package/dist/tools/list-test-library.d.ts +44 -0
  209. package/dist/tools/list-test-library.js +83 -0
  210. package/dist/tools/list-test-library.js.map +1 -0
  211. package/dist/tools/list-test-scenarios.d.ts +31 -0
  212. package/dist/tools/list-test-scenarios.js +66 -0
  213. package/dist/tools/list-test-scenarios.js.map +1 -0
  214. package/dist/tools/local-scan.d.ts +40 -0
  215. package/dist/tools/local-scan.js +261 -0
  216. package/dist/tools/local-scan.js.map +1 -0
  217. package/dist/tools/manage-diagram.d.ts +109 -0
  218. package/dist/tools/manage-diagram.js +316 -0
  219. package/dist/tools/manage-diagram.js.map +1 -0
  220. package/dist/tools/open-in-ide.d.ts +39 -0
  221. package/dist/tools/open-in-ide.js +61 -0
  222. package/dist/tools/open-in-ide.js.map +1 -0
  223. package/dist/tools/run-test-scenario.d.ts +37 -0
  224. package/dist/tools/run-test-scenario.js +70 -0
  225. package/dist/tools/run-test-scenario.js.map +1 -0
  226. package/dist/tools/scan-pr.d.ts +27 -0
  227. package/dist/tools/scan-pr.js +51 -0
  228. package/dist/tools/scan-pr.js.map +1 -0
  229. package/dist/tools/security-review.d.ts +65 -0
  230. package/dist/tools/security-review.js +378 -0
  231. package/dist/tools/security-review.js.map +1 -0
  232. package/dist/tools/setup-red-team-target.d.ts +79 -0
  233. package/dist/tools/setup-red-team-target.js +430 -0
  234. package/dist/tools/setup-red-team-target.js.map +1 -0
  235. package/dist/tools/triage-finding.d.ts +47 -0
  236. package/dist/tools/triage-finding.js +116 -0
  237. package/dist/tools/triage-finding.js.map +1 -0
  238. package/dist/tools/update-document.d.ts +28 -0
  239. package/dist/tools/update-document.js +63 -0
  240. package/dist/tools/update-document.js.map +1 -0
  241. package/dist/tools/update-endpoint.d.ts +65 -0
  242. package/dist/tools/update-endpoint.js +145 -0
  243. package/dist/tools/update-endpoint.js.map +1 -0
  244. package/dist/tools/update-mermaid-source.d.ts +34 -0
  245. package/dist/tools/update-mermaid-source.js +76 -0
  246. package/dist/tools/update-mermaid-source.js.map +1 -0
  247. package/dist/tools/verify-trust-mark.d.ts +29 -0
  248. package/dist/tools/verify-trust-mark.js +108 -0
  249. package/dist/tools/verify-trust-mark.js.map +1 -0
  250. package/dist/types.d.ts +220 -0
  251. package/dist/types.js +33 -0
  252. package/dist/types.js.map +1 -0
  253. package/package.json +50 -0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * ZIVIS API Client
3
+ *
4
+ * HTTPS client that attaches the appropriate auth header
5
+ * based on the active auth method:
6
+ * - OAuth: Authorization: Bearer <token>
7
+ * - API Key: X-API-Key: <key>
8
+ *
9
+ * Handles 401 responses by attempting token refresh (OAuth)
10
+ * before returning an auth error.
11
+ */
12
+ import type { ZivisConfig } from "./types.js";
13
+ export declare class ApiClient {
14
+ private config;
15
+ constructor(config?: ZivisConfig);
16
+ /**
17
+ * Build auth headers from current credentials
18
+ */
19
+ private buildAuthHeaders;
20
+ /**
21
+ * Make an authenticated request to the ZIVIS API.
22
+ *
23
+ * Automatically resolves credentials and attaches auth headers.
24
+ * On 401 with OAuth, attempts one token refresh and retries.
25
+ */
26
+ request<T>(method: string, path: string, body?: unknown): Promise<T>;
27
+ private doRequest;
28
+ get<T>(path: string): Promise<T>;
29
+ /**
30
+ * Fetch a path and return the response as plain text (not JSON).
31
+ * Used for artifact content streaming where the response is not JSON.
32
+ */
33
+ getText(path: string): Promise<string>;
34
+ patch<T>(path: string, body: unknown): Promise<T>;
35
+ post<T>(path: string, body: unknown): Promise<T>;
36
+ del<T>(path: string): Promise<T>;
37
+ }
38
+ export declare class ApiError extends Error {
39
+ readonly status: number;
40
+ readonly code: string;
41
+ constructor(message: string, status: number, code: string);
42
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * ZIVIS API Client
3
+ *
4
+ * HTTPS client that attaches the appropriate auth header
5
+ * based on the active auth method:
6
+ * - OAuth: Authorization: Bearer <token>
7
+ * - API Key: X-API-Key: <key>
8
+ *
9
+ * Handles 401 responses by attempting token refresh (OAuth)
10
+ * before returning an auth error.
11
+ */
12
+ import { resolveAuth } from "./auth/index.js";
13
+ import { DEFAULT_CONFIG } from "./types.js";
14
+ export class ApiClient {
15
+ config;
16
+ constructor(config = DEFAULT_CONFIG) {
17
+ this.config = config;
18
+ }
19
+ /**
20
+ * Build auth headers from current credentials
21
+ */
22
+ buildAuthHeaders(creds) {
23
+ switch (creds.method) {
24
+ case "oauth":
25
+ return { Authorization: `Bearer ${creds.accessToken}` };
26
+ case "apikey":
27
+ return { "X-API-Key": creds.apiKey };
28
+ case "none":
29
+ return {};
30
+ }
31
+ }
32
+ /**
33
+ * Make an authenticated request to the ZIVIS API.
34
+ *
35
+ * Automatically resolves credentials and attaches auth headers.
36
+ * On 401 with OAuth, attempts one token refresh and retries.
37
+ */
38
+ async request(method, path, body) {
39
+ const creds = await resolveAuth(this.config);
40
+ if (creds.method === "none") {
41
+ throw new ApiError("Not authenticated. Run: zivis auth login", 401, "AUTH_REQUIRED");
42
+ }
43
+ const result = await this.doRequest(method, path, body, creds);
44
+ // If 401 and using OAuth, try refreshing
45
+ if (result.status === 401 && creds.method === "oauth") {
46
+ const freshCreds = await resolveAuth(this.config);
47
+ if (freshCreds.method === "oauth" &&
48
+ freshCreds.accessToken !== creds.accessToken) {
49
+ // Token was refreshed, retry
50
+ const retryResult = await this.doRequest(method, path, body, freshCreds);
51
+ if (retryResult.status === 401) {
52
+ throw new ApiError("Session expired. Run: zivis auth login", 401, "SESSION_EXPIRED");
53
+ }
54
+ if (!retryResult.ok) {
55
+ throw new ApiError(retryResult.errorMessage || "API request failed", retryResult.status, "API_ERROR");
56
+ }
57
+ return retryResult.data;
58
+ }
59
+ throw new ApiError("Session expired. Run: zivis auth login", 401, "SESSION_EXPIRED");
60
+ }
61
+ if (!result.ok) {
62
+ throw new ApiError(result.errorMessage || `API request failed: ${method} ${path}`, result.status, "API_ERROR");
63
+ }
64
+ return result.data;
65
+ }
66
+ async doRequest(method, path, body, creds) {
67
+ const url = `${this.config.apiBaseUrl}${path}`;
68
+ const headers = {
69
+ "Content-Type": "application/json",
70
+ "User-Agent": "@zivis-ai/cli",
71
+ ...this.buildAuthHeaders(creds),
72
+ };
73
+ const fetchOptions = { method, headers };
74
+ if (body && method !== "GET") {
75
+ fetchOptions.body = JSON.stringify(body);
76
+ }
77
+ try {
78
+ const response = await fetch(url, fetchOptions);
79
+ if (!response.ok) {
80
+ const errorBody = await response.json().catch(() => ({}));
81
+ return {
82
+ ok: false,
83
+ status: response.status,
84
+ errorMessage: errorBody.message ||
85
+ errorBody.error ||
86
+ `HTTP ${response.status}`,
87
+ };
88
+ }
89
+ const data = (await response.json());
90
+ return { ok: true, status: response.status, data };
91
+ }
92
+ catch (err) {
93
+ // Network error
94
+ const message = err instanceof Error ? err.message : "Network request failed";
95
+ throw new ApiError(`Failed to connect to ZIVIS API (${url}): ${message}`, 0, "NETWORK_ERROR");
96
+ }
97
+ }
98
+ // ── Convenience methods ──
99
+ async get(path) {
100
+ return this.request("GET", path);
101
+ }
102
+ /**
103
+ * Fetch a path and return the response as plain text (not JSON).
104
+ * Used for artifact content streaming where the response is not JSON.
105
+ */
106
+ async getText(path) {
107
+ const creds = await resolveAuth(this.config);
108
+ if (creds.method === "none") {
109
+ throw new ApiError("Not authenticated. Run: zivis auth login", 401, "AUTH_REQUIRED");
110
+ }
111
+ const url = `${this.config.apiBaseUrl}${path}`;
112
+ const headers = {
113
+ "User-Agent": "@zivis-ai/cli",
114
+ ...this.buildAuthHeaders(creds),
115
+ };
116
+ const response = await fetch(url, { method: "GET", headers });
117
+ if (!response.ok) {
118
+ const errorBody = await response.json().catch(() => ({}));
119
+ throw new ApiError(errorBody.message ||
120
+ errorBody.error ||
121
+ `HTTP ${response.status}`, response.status, "API_ERROR");
122
+ }
123
+ return response.text();
124
+ }
125
+ async patch(path, body) {
126
+ return this.request("PATCH", path, body);
127
+ }
128
+ async post(path, body) {
129
+ return this.request("POST", path, body);
130
+ }
131
+ async del(path) {
132
+ return this.request("DELETE", path);
133
+ }
134
+ }
135
+ export class ApiError extends Error {
136
+ status;
137
+ code;
138
+ constructor(message, status, code) {
139
+ super(message);
140
+ this.status = status;
141
+ this.code = code;
142
+ this.name = "ApiError";
143
+ }
144
+ }
145
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Auth Orchestrator
3
+ *
4
+ * Resolves credentials using the defined precedence:
5
+ * 1. OS Keychain — OAuth tokens (preferred, user-scoped, auto-refresh)
6
+ * 2. OS Keychain — API key (fallback, org-scoped)
7
+ * 3. ZIVIS_API_KEY env var (CI environments)
8
+ * 4. Not authenticated (tools return auth error with setup instructions)
9
+ */
10
+ import type { AuthCredentials, AuthMethod, ZivisConfig } from "../types.js";
11
+ export { login } from "./oauth.js";
12
+ export { keychainClearAll, keychainClearSession, getStorageName, getSessionAccount } from "./keychain.js";
13
+ export { keychainStore, keychainRead } from "./keychain.js";
14
+ export { loadTokens } from "./token-refresh.js";
15
+ export { loadSessionIndex, saveSessionEntry, removeSessionEntry, listSessions } from "./sessions.js";
16
+ /**
17
+ * Resolve current auth credentials following the precedence chain.
18
+ *
19
+ * IMPORTANT: When config.session is set (ZIVIS_SESSION env var), auth resolution
20
+ * is scoped to that session ONLY. It will NOT fall through to the default session
21
+ * or API key — this prevents accidental cross-tenant data access.
22
+ *
23
+ * @param forceMethod - Override the automatic method selection
24
+ */
25
+ export declare function resolveAuth(config?: ZivisConfig, forceMethod?: AuthMethod): Promise<AuthCredentials>;
26
+ /**
27
+ * Get a human-readable description of the current auth state
28
+ */
29
+ export declare function getAuthDescription(config?: ZivisConfig): Promise<string>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Auth Orchestrator
3
+ *
4
+ * Resolves credentials using the defined precedence:
5
+ * 1. OS Keychain — OAuth tokens (preferred, user-scoped, auto-refresh)
6
+ * 2. OS Keychain — API key (fallback, org-scoped)
7
+ * 3. ZIVIS_API_KEY env var (CI environments)
8
+ * 4. Not authenticated (tools return auth error with setup instructions)
9
+ */
10
+ import { keychainRead } from "./keychain.js";
11
+ import { getValidAccessToken } from "./token-refresh.js";
12
+ import { DEFAULT_CONFIG } from "../types.js";
13
+ export { login } from "./oauth.js";
14
+ export { keychainClearAll, keychainClearSession, getStorageName, getSessionAccount } from "./keychain.js";
15
+ export { keychainStore, keychainRead } from "./keychain.js";
16
+ export { loadTokens } from "./token-refresh.js";
17
+ export { loadSessionIndex, saveSessionEntry, removeSessionEntry, listSessions } from "./sessions.js";
18
+ /**
19
+ * Resolve current auth credentials following the precedence chain.
20
+ *
21
+ * IMPORTANT: When config.session is set (ZIVIS_SESSION env var), auth resolution
22
+ * is scoped to that session ONLY. It will NOT fall through to the default session
23
+ * or API key — this prevents accidental cross-tenant data access.
24
+ *
25
+ * @param forceMethod - Override the automatic method selection
26
+ */
27
+ export async function resolveAuth(config = DEFAULT_CONFIG, forceMethod) {
28
+ const session = config.session;
29
+ // 1. OAuth tokens (unless forced to apikey)
30
+ if (forceMethod !== "apikey") {
31
+ const accessToken = await getValidAccessToken(config);
32
+ if (accessToken) {
33
+ return { method: "oauth", accessToken };
34
+ }
35
+ }
36
+ // 2. ZIVIS_API_KEY env var — always checked, even for named sessions.
37
+ // This is an explicit user action (they set the env var in their MCP config),
38
+ // so it's safe to use regardless of session scope.
39
+ const envKey = process.env.ZIVIS_API_KEY;
40
+ if (envKey) {
41
+ return { method: "apikey", apiKey: envKey };
42
+ }
43
+ // CROSS-TENANT SAFETY: If a named session is active, do NOT fall through
44
+ // to default keychain credentials. This prevents accidentally using admin tokens
45
+ // when working in a customer's project.
46
+ if (session) {
47
+ return { method: "none" };
48
+ }
49
+ // 3. API key from keychain (only for default/no-session)
50
+ if (forceMethod !== "oauth") {
51
+ const apiKey = await keychainRead("apikey");
52
+ if (apiKey) {
53
+ return { method: "apikey", apiKey };
54
+ }
55
+ }
56
+ // 4. Not authenticated
57
+ return { method: "none" };
58
+ }
59
+ /**
60
+ * Get a human-readable description of the current auth state
61
+ */
62
+ export async function getAuthDescription(config = DEFAULT_CONFIG) {
63
+ const creds = await resolveAuth(config);
64
+ switch (creds.method) {
65
+ case "oauth":
66
+ return "OAuth (access token)";
67
+ case "apikey":
68
+ return `API Key (${creds.apiKey?.startsWith("sk_") ? `sk_...${creds.apiKey.slice(-4)}` : "***"})`;
69
+ case "none":
70
+ return "Not authenticated";
71
+ }
72
+ }
73
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC1G,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErG;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAsB,cAAc,EACpC,WAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAE/B,4CAA4C;IAC5C,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,8EAA8E;IAC9E,mDAAmD;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACzC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC;IAED,yEAAyE;IACzE,iFAAiF;IACjF,wCAAwC;IACxC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED,yDAAyD;IACzD,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAsB,cAAc;IAEpC,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,sBAAsB,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;QACpG,KAAK,MAAM;YACT,OAAO,mBAAmB,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * JWT helpers for local safety checks.
3
+ *
4
+ * The API is the authoritative verifier of JWT signatures. These helpers
5
+ * decode the payload only to read claims locally (org_id, email, sub) so
6
+ * the CLI/MCP server can enforce project-binding guardrails before making
7
+ * any API calls.
8
+ */
9
+ export interface JwtClaims {
10
+ /** WorkOS user sub (e.g., "user_01HXYZ...") */
11
+ sub?: string;
12
+ /** WorkOS org ID (e.g., "org_01HXYZ...") */
13
+ org_id?: string;
14
+ /** Email (present on id_token, sometimes on access_token) */
15
+ email?: string;
16
+ /** Expiry (unix seconds) */
17
+ exp?: number;
18
+ [key: string]: unknown;
19
+ }
20
+ /**
21
+ * Decode a JWT payload WITHOUT verifying the signature.
22
+ * Returns null if the token is malformed.
23
+ */
24
+ export declare function decodeJwtPayload(token: string): JwtClaims | null;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * JWT helpers for local safety checks.
3
+ *
4
+ * The API is the authoritative verifier of JWT signatures. These helpers
5
+ * decode the payload only to read claims locally (org_id, email, sub) so
6
+ * the CLI/MCP server can enforce project-binding guardrails before making
7
+ * any API calls.
8
+ */
9
+ /**
10
+ * Decode a JWT payload WITHOUT verifying the signature.
11
+ * Returns null if the token is malformed.
12
+ */
13
+ export function decodeJwtPayload(token) {
14
+ try {
15
+ const parts = token.split(".");
16
+ if (parts.length !== 3)
17
+ return null;
18
+ const payload = Buffer.from(parts[1], "base64url").toString("utf8");
19
+ return JSON.parse(payload);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * OS Keychain credential storage
3
+ *
4
+ * Uses native OS CLI commands to store/retrieve credentials securely.
5
+ * No native npm dependencies (no keytar).
6
+ *
7
+ * - macOS: Keychain Services via `security` CLI
8
+ * - Linux: Secret Service API via `secret-tool` CLI
9
+ * - Windows: DPAPI via PowerShell
10
+ *
11
+ * Falls back to encrypted file if keychain CLI is unavailable.
12
+ */
13
+ /**
14
+ * Returns the keychain account key for a given session.
15
+ * - No session or undefined → "oauth" (backward compatible)
16
+ * - Named session → "oauth.{session}"
17
+ */
18
+ export declare function getSessionAccount(session?: string): string;
19
+ export declare function keychainStore(account: string, value: string): Promise<void>;
20
+ export declare function keychainRead(account: string): Promise<string | null>;
21
+ export declare function keychainDelete(account: string): Promise<void>;
22
+ /**
23
+ * Delete credentials for a specific session
24
+ */
25
+ export declare function keychainClearSession(session?: string): Promise<void>;
26
+ /**
27
+ * Delete all stored credentials (default OAuth + API key).
28
+ * Does NOT clear named sessions — use keychainClearSession() for those.
29
+ */
30
+ export declare function keychainClearAll(): Promise<void>;
31
+ /**
32
+ * Returns the storage mechanism name for display purposes
33
+ */
34
+ export declare function getStorageName(): string;
@@ -0,0 +1,272 @@
1
+ /**
2
+ * OS Keychain credential storage
3
+ *
4
+ * Uses native OS CLI commands to store/retrieve credentials securely.
5
+ * No native npm dependencies (no keytar).
6
+ *
7
+ * - macOS: Keychain Services via `security` CLI
8
+ * - Linux: Secret Service API via `secret-tool` CLI
9
+ * - Windows: DPAPI via PowerShell
10
+ *
11
+ * Falls back to encrypted file if keychain CLI is unavailable.
12
+ */
13
+ import { execFile } from "node:child_process";
14
+ import { promisify } from "node:util";
15
+ import * as fs from "node:fs/promises";
16
+ import * as path from "node:path";
17
+ import * as os from "node:os";
18
+ import * as crypto from "node:crypto";
19
+ import { ENV_CONFIG } from "../types.js";
20
+ const execFileAsync = promisify(execFile);
21
+ // Use environment-aware prefix so staging and production credentials are isolated
22
+ const SERVICE_PREFIX = ENV_CONFIG.keychainPrefix;
23
+ /**
24
+ * Returns the keychain account key for a given session.
25
+ * - No session or undefined → "oauth" (backward compatible)
26
+ * - Named session → "oauth.{session}"
27
+ */
28
+ export function getSessionAccount(session) {
29
+ return session ? `oauth.${session}` : "oauth";
30
+ }
31
+ function getServiceName(account) {
32
+ return `${SERVICE_PREFIX}.${account}`;
33
+ }
34
+ function getFallbackDir() {
35
+ // Use environment-aware config dir so staging and production are isolated
36
+ const configDir = ENV_CONFIG.configDir;
37
+ if (process.platform === "win32") {
38
+ return path.join(process.env.APPDATA || os.homedir(), configDir);
39
+ }
40
+ return path.join(os.homedir(), ".config", configDir);
41
+ }
42
+ function getFallbackPath(account) {
43
+ return path.join(getFallbackDir(), `${account}.enc`);
44
+ }
45
+ // ── macOS Keychain ──
46
+ async function macosStore(account, value) {
47
+ const service = getServiceName(account);
48
+ // Delete existing entry first (ignore errors if not found)
49
+ try {
50
+ await execFileAsync("security", [
51
+ "delete-generic-password",
52
+ "-a",
53
+ "zivis-mcp",
54
+ "-s",
55
+ service,
56
+ ]);
57
+ }
58
+ catch {
59
+ // Entry didn't exist, that's fine
60
+ }
61
+ await execFileAsync("security", [
62
+ "add-generic-password",
63
+ "-a",
64
+ "zivis-mcp",
65
+ "-s",
66
+ service,
67
+ "-w",
68
+ value,
69
+ ]);
70
+ }
71
+ async function macosRead(account) {
72
+ const service = getServiceName(account);
73
+ try {
74
+ const { stdout } = await execFileAsync("security", [
75
+ "find-generic-password",
76
+ "-a",
77
+ "zivis-mcp",
78
+ "-s",
79
+ service,
80
+ "-w",
81
+ ]);
82
+ return stdout.trim();
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
88
+ async function macosDelete(account) {
89
+ const service = getServiceName(account);
90
+ try {
91
+ await execFileAsync("security", [
92
+ "delete-generic-password",
93
+ "-a",
94
+ "zivis-mcp",
95
+ "-s",
96
+ service,
97
+ ]);
98
+ }
99
+ catch {
100
+ // Entry didn't exist
101
+ }
102
+ }
103
+ // ── Linux Secret Service ──
104
+ async function linuxStore(account, value) {
105
+ const child = execFileAsync("secret-tool", [
106
+ "store",
107
+ "--label",
108
+ `ZIVIS MCP ${account}`,
109
+ "service",
110
+ SERVICE_PREFIX,
111
+ "account",
112
+ account,
113
+ ]);
114
+ // secret-tool reads value from stdin
115
+ child.child.stdin?.write(value);
116
+ child.child.stdin?.end();
117
+ await child;
118
+ }
119
+ async function linuxRead(account) {
120
+ try {
121
+ const { stdout } = await execFileAsync("secret-tool", [
122
+ "lookup",
123
+ "service",
124
+ SERVICE_PREFIX,
125
+ "account",
126
+ account,
127
+ ]);
128
+ return stdout.trim() || null;
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ async function linuxDelete(account) {
135
+ try {
136
+ await execFileAsync("secret-tool", [
137
+ "clear",
138
+ "service",
139
+ SERVICE_PREFIX,
140
+ "account",
141
+ account,
142
+ ]);
143
+ }
144
+ catch {
145
+ // Entry didn't exist
146
+ }
147
+ }
148
+ // ── Encrypted File Fallback (Windows + fallback for all platforms) ──
149
+ function deriveKey() {
150
+ // Use machine-specific data as encryption key seed
151
+ const seed = `zivis-mcp:${os.hostname()}:${os.userInfo().username}`;
152
+ return crypto.createHash("sha256").update(seed).digest();
153
+ }
154
+ async function fileStore(account, value) {
155
+ const key = deriveKey();
156
+ const iv = crypto.randomBytes(16);
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
159
+ let encrypted = cipher.update(value, "utf8", "hex");
160
+ encrypted += cipher.final("hex");
161
+ const data = JSON.stringify({ iv: iv.toString("hex"), data: encrypted });
162
+ const dir = getFallbackDir();
163
+ await fs.mkdir(dir, { recursive: true });
164
+ await fs.writeFile(getFallbackPath(account), data, { mode: 0o600 });
165
+ }
166
+ async function fileRead(account) {
167
+ try {
168
+ const raw = await fs.readFile(getFallbackPath(account), "utf8");
169
+ const { iv, data } = JSON.parse(raw);
170
+ const key = deriveKey();
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ const decipher = crypto.createDecipheriv("aes-256-cbc", key, Buffer.from(iv, "hex"));
173
+ let decrypted = decipher.update(data, "hex", "utf8");
174
+ decrypted += decipher.final("utf8");
175
+ return decrypted;
176
+ }
177
+ catch {
178
+ return null;
179
+ }
180
+ }
181
+ async function fileDelete(account) {
182
+ try {
183
+ await fs.unlink(getFallbackPath(account));
184
+ }
185
+ catch {
186
+ // File didn't exist
187
+ }
188
+ }
189
+ // ── Platform Detection & Dispatch ──
190
+ async function isKeychainAvailable() {
191
+ try {
192
+ if (process.platform === "darwin") {
193
+ await execFileAsync("security", ["help"]);
194
+ return true;
195
+ }
196
+ if (process.platform === "linux") {
197
+ await execFileAsync("which", ["secret-tool"]);
198
+ return true;
199
+ }
200
+ return false;
201
+ }
202
+ catch {
203
+ return false;
204
+ }
205
+ }
206
+ let keychainAvailable = null;
207
+ async function useKeychain() {
208
+ if (keychainAvailable === null) {
209
+ keychainAvailable = await isKeychainAvailable();
210
+ }
211
+ return keychainAvailable;
212
+ }
213
+ // ── Public API ──
214
+ export async function keychainStore(account, value) {
215
+ // Always store to encrypted file as fallback (for environments where keychain CLI
216
+ // may not be available, e.g., MCP servers spawned by IDEs with different $PATH)
217
+ await fileStore(account, value);
218
+ // Also store to native keychain if available
219
+ if (await useKeychain()) {
220
+ if (process.platform === "darwin") {
221
+ await macosStore(account, value);
222
+ }
223
+ else if (process.platform === "linux") {
224
+ await linuxStore(account, value);
225
+ }
226
+ }
227
+ }
228
+ export async function keychainRead(account) {
229
+ if (await useKeychain()) {
230
+ if (process.platform === "darwin")
231
+ return macosRead(account);
232
+ if (process.platform === "linux")
233
+ return linuxRead(account);
234
+ }
235
+ return fileRead(account);
236
+ }
237
+ export async function keychainDelete(account) {
238
+ if (await useKeychain()) {
239
+ if (process.platform === "darwin")
240
+ return macosDelete(account);
241
+ if (process.platform === "linux")
242
+ return linuxDelete(account);
243
+ }
244
+ return fileDelete(account);
245
+ }
246
+ /**
247
+ * Delete credentials for a specific session
248
+ */
249
+ export async function keychainClearSession(session) {
250
+ await keychainDelete(getSessionAccount(session));
251
+ }
252
+ /**
253
+ * Delete all stored credentials (default OAuth + API key).
254
+ * Does NOT clear named sessions — use keychainClearSession() for those.
255
+ */
256
+ export async function keychainClearAll() {
257
+ await keychainDelete("oauth");
258
+ await keychainDelete("apikey");
259
+ }
260
+ /**
261
+ * Returns the storage mechanism name for display purposes
262
+ */
263
+ export function getStorageName() {
264
+ if (process.platform === "darwin")
265
+ return "macOS Keychain";
266
+ if (process.platform === "linux")
267
+ return "Secret Service";
268
+ if (process.platform === "win32")
269
+ return "encrypted file";
270
+ return "encrypted file";
271
+ }
272
+ //# sourceMappingURL=keychain.js.map