@zivis/cli 0.1.0-alpha.4 → 0.1.0-alpha.40

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 (124) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +31 -0
  3. package/dist/commands/app/index.d.ts +0 -8
  4. package/dist/commands/app/index.js +4 -16
  5. package/dist/commands/auth/bind.d.ts +4 -0
  6. package/dist/commands/auth/bind.js +97 -0
  7. package/dist/commands/auth/index.js +20 -2
  8. package/dist/commands/auth/init.d.ts +1 -0
  9. package/dist/commands/auth/init.js +245 -73
  10. package/dist/commands/auth/login.d.ts +2 -0
  11. package/dist/commands/auth/login.js +136 -18
  12. package/dist/commands/auth/logout.js +19 -7
  13. package/dist/commands/auth/sessions.js +7 -4
  14. package/dist/commands/auth/status.js +33 -12
  15. package/dist/commands/auth/token.d.ts +4 -0
  16. package/dist/commands/auth/token.js +73 -0
  17. package/dist/commands/check/index.d.ts +2 -0
  18. package/dist/commands/check/index.js +463 -0
  19. package/dist/commands/credits/index.js +2 -3
  20. package/dist/commands/inspect/index.d.ts +2 -0
  21. package/dist/commands/inspect/index.js +136 -0
  22. package/dist/commands/mcp/index.js +81 -14
  23. package/dist/commands/open/index.d.ts +0 -8
  24. package/dist/commands/open/index.js +0 -11
  25. package/dist/commands/scan/index.js +3 -8
  26. package/dist/commands/target/index.js +0 -3
  27. package/dist/commands/tm/index.js +3 -10
  28. package/dist/index.js +13 -1
  29. package/dist/internal/binding-token-mint.d.ts +33 -0
  30. package/dist/internal/binding-token-mint.js +174 -0
  31. package/dist/internal/extractor/adapters/go/index.d.ts +0 -4
  32. package/dist/internal/extractor/adapters/go/index.js +0 -6
  33. package/dist/internal/extractor/adapters/go/manifest.d.ts +0 -17
  34. package/dist/internal/extractor/adapters/go/manifest.js +19 -25
  35. package/dist/internal/extractor/adapters/go/parser.d.ts +0 -4
  36. package/dist/internal/extractor/adapters/go/parser.js +0 -5
  37. package/dist/internal/extractor/adapters/go/scanner.d.ts +0 -20
  38. package/dist/internal/extractor/adapters/go/scanner.js +0 -43
  39. package/dist/internal/extractor/adapters/python/index.d.ts +0 -3
  40. package/dist/internal/extractor/adapters/python/index.js +0 -4
  41. package/dist/internal/extractor/adapters/python/manifest.d.ts +0 -8
  42. package/dist/internal/extractor/adapters/python/manifest.js +21 -18
  43. package/dist/internal/extractor/adapters/python/parser.d.ts +0 -3
  44. package/dist/internal/extractor/adapters/python/parser.js +0 -4
  45. package/dist/internal/extractor/adapters/python/scanner.d.ts +0 -18
  46. package/dist/internal/extractor/adapters/python/scanner.js +1 -54
  47. package/dist/internal/extractor/adapters/typescript/index.d.ts +0 -7
  48. package/dist/internal/extractor/adapters/typescript/index.js +1 -21
  49. package/dist/internal/extractor/adapters/typescript/manifest.d.ts +0 -14
  50. package/dist/internal/extractor/adapters/typescript/manifest.js +26 -17
  51. package/dist/internal/extractor/adapters/typescript/parser.d.ts +0 -8
  52. package/dist/internal/extractor/adapters/typescript/parser.js +0 -9
  53. package/dist/internal/extractor/adapters/typescript/scanner.d.ts +0 -24
  54. package/dist/internal/extractor/adapters/typescript/scanner.js +0 -43
  55. package/dist/internal/extractor/graph-builder.d.ts +10 -0
  56. package/dist/internal/extractor/graph-builder.js +245 -0
  57. package/dist/internal/extractor/graph-types.d.ts +89 -0
  58. package/dist/internal/extractor/graph-types.js +1 -0
  59. package/dist/internal/extractor/index.d.ts +0 -14
  60. package/dist/internal/extractor/index.js +0 -13
  61. package/dist/internal/extractor/types.d.ts +0 -45
  62. package/dist/internal/extractor/types.js +0 -27
  63. package/dist/internal/extractor/walker.d.ts +0 -18
  64. package/dist/internal/extractor/walker.js +0 -18
  65. package/dist/internal/github-linkage-prompt.d.ts +7 -0
  66. package/dist/internal/github-linkage-prompt.js +95 -0
  67. package/dist/internal/ide-setup.d.ts +16 -7
  68. package/dist/internal/ide-setup.js +320 -46
  69. package/dist/internal/org-picker.d.ts +11 -0
  70. package/dist/internal/org-picker.js +34 -0
  71. package/dist/internal/orgs.js +0 -1
  72. package/dist/internal/project-binding.d.ts +1 -0
  73. package/dist/internal/project-binding.js +14 -9
  74. package/dist/internal/target-auth/api-config.d.ts +0 -17
  75. package/dist/internal/target-auth/api-config.js +0 -18
  76. package/dist/internal/target-auth/config.d.ts +0 -12
  77. package/dist/internal/target-auth/config.js +0 -13
  78. package/dist/internal/target-auth/index.d.ts +0 -18
  79. package/dist/internal/target-auth/index.js +0 -19
  80. package/dist/internal/target-auth/keychain.d.ts +0 -18
  81. package/dist/internal/target-auth/keychain.js +3 -23
  82. package/dist/internal/target-auth/types.d.ts +0 -26
  83. package/dist/internal/target-auth/types.js +0 -10
  84. package/dist/internal/target-auth/workos.d.ts +0 -15
  85. package/dist/internal/target-auth/workos.js +9 -18
  86. package/package.json +19 -5
  87. package/dist/commands/app/index.js.map +0 -1
  88. package/dist/commands/auth/index.js.map +0 -1
  89. package/dist/commands/auth/init.js.map +0 -1
  90. package/dist/commands/auth/login.js.map +0 -1
  91. package/dist/commands/auth/logout.js.map +0 -1
  92. package/dist/commands/auth/sessions.js.map +0 -1
  93. package/dist/commands/auth/status.js.map +0 -1
  94. package/dist/commands/credits/index.js.map +0 -1
  95. package/dist/commands/mcp/index.js.map +0 -1
  96. package/dist/commands/open/index.js.map +0 -1
  97. package/dist/commands/scan/index.js.map +0 -1
  98. package/dist/commands/target/index.js.map +0 -1
  99. package/dist/commands/tm/index.js.map +0 -1
  100. package/dist/index.js.map +0 -1
  101. package/dist/internal/extractor/adapters/go/index.js.map +0 -1
  102. package/dist/internal/extractor/adapters/go/manifest.js.map +0 -1
  103. package/dist/internal/extractor/adapters/go/parser.js.map +0 -1
  104. package/dist/internal/extractor/adapters/go/scanner.js.map +0 -1
  105. package/dist/internal/extractor/adapters/python/index.js.map +0 -1
  106. package/dist/internal/extractor/adapters/python/manifest.js.map +0 -1
  107. package/dist/internal/extractor/adapters/python/parser.js.map +0 -1
  108. package/dist/internal/extractor/adapters/python/scanner.js.map +0 -1
  109. package/dist/internal/extractor/adapters/typescript/index.js.map +0 -1
  110. package/dist/internal/extractor/adapters/typescript/manifest.js.map +0 -1
  111. package/dist/internal/extractor/adapters/typescript/parser.js.map +0 -1
  112. package/dist/internal/extractor/adapters/typescript/scanner.js.map +0 -1
  113. package/dist/internal/extractor/index.js.map +0 -1
  114. package/dist/internal/extractor/types.js.map +0 -1
  115. package/dist/internal/extractor/walker.js.map +0 -1
  116. package/dist/internal/ide-setup.js.map +0 -1
  117. package/dist/internal/orgs.js.map +0 -1
  118. package/dist/internal/project-binding.js.map +0 -1
  119. package/dist/internal/target-auth/api-config.js.map +0 -1
  120. package/dist/internal/target-auth/config.js.map +0 -1
  121. package/dist/internal/target-auth/index.js.map +0 -1
  122. package/dist/internal/target-auth/keychain.js.map +0 -1
  123. package/dist/internal/target-auth/types.js.map +0 -1
  124. package/dist/internal/target-auth/workos.js.map +0 -1
@@ -1,16 +1,290 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- const SERVER_NAME = "zivis";
4
- function buildServerConfig(session, extra) {
5
- const config = {
3
+ function getServerName(pkg) {
4
+ return pkg === "builder" ? "zivis-builder" : "zivis";
5
+ }
6
+ function appendBlock(filePath, blockKey, content) {
7
+ const begin = `<!-- BEGIN ZIVIS:${blockKey} -->`;
8
+ const end = `<!-- END ZIVIS:${blockKey} -->`;
9
+ let existing = fs.existsSync(filePath)
10
+ ? fs.readFileSync(filePath, "utf-8")
11
+ : "";
12
+ const blockRegex = new RegExp(`${escapeRegex(begin)}[\\s\\S]*?${escapeRegex(end)}`, "g");
13
+ const newBlock = `${begin}\n${content}\n${end}`;
14
+ if (blockRegex.test(existing)) {
15
+ existing = existing.replace(blockRegex, newBlock);
16
+ }
17
+ else {
18
+ if (existing && !existing.endsWith("\n"))
19
+ existing += "\n";
20
+ existing += `\n${newBlock}\n`;
21
+ }
22
+ const dir = path.dirname(filePath);
23
+ if (!fs.existsSync(dir))
24
+ fs.mkdirSync(dir, { recursive: true });
25
+ fs.writeFileSync(filePath, existing);
26
+ }
27
+ function escapeRegex(s) {
28
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
29
+ }
30
+ function cursorRuleContent() {
31
+ return `---
32
+ description: ZIVIS security guidance — quietly suggest security checks at the right moments
33
+ globs:
34
+ - "**/*.{ts,tsx,js,jsx,py,go,rb,java}"
35
+ - "**/package.json"
36
+ - "**/requirements.txt"
37
+ - "**/pyproject.toml"
38
+ - "**/go.mod"
39
+ - "**/Dockerfile"
40
+ alwaysApply: false
41
+ ---
42
+
43
+ <!-- BEGIN ZIVIS:guidance -->
44
+
45
+ # ZIVIS Security Guidance
46
+
47
+ ZIVIS is a security platform connected to this project as an MCP server.
48
+ Use it sparingly and only when relevant — never nag.
49
+
50
+ ## When to suggest ZIVIS
51
+
52
+ **On first security question in this project (per session):**
53
+ If the user asks anything about security, safety, vulnerabilities, or "is this
54
+ secure", call \`zivis_get_started\` first (no arguments). Present the
55
+ \`recommended_next_steps\` as a numbered list. Do NOT invent your own security
56
+ checklist — let ZIVIS produce one based on actual project state.
57
+
58
+ **When dependencies change** (\`package.json\`, \`requirements.txt\`, \`go.mod\`,
59
+ \`pyproject.toml\` is being edited and a new package is added):
60
+ Mention once per session: "Want me to check this dependency with ZIVIS?
61
+ It scans for prompt-injection risks and known vulnerabilities."
62
+ If yes, call \`zivis_check_repo_trust\` with the package's source repo.
63
+ This tool requires NO auth — works for any public GitHub repo.
64
+
65
+ **When AI/LLM SDKs are imported** (\`openai\`, \`anthropic\`, \`langchain\`,
66
+ \`@anthropic-ai/sdk\`, \`llamaindex\`, \`cohere\`, \`together\`, etc.):
67
+ Mention once per session: "You're integrating an AI SDK. Want to set up a
68
+ red-team target with ZIVIS? It tests for prompt injection, jailbreaks, and
69
+ PII leakage." If yes, call \`zivis_setup_red_team_target\` with
70
+ target_type=ai_chat.
71
+
72
+ **When user mentions:** "audit", "compliance", "SOC 2", "pen test",
73
+ "customer security questionnaire", "going to production", "launch":
74
+ Call \`zivis_get_started\` with their phrase as the \`concern\` parameter.
75
+
76
+ **When user has unread findings:**
77
+ At the start of a session you may call \`zivis_get_findings\` (status=open) once.
78
+ If any critical findings exist, surface them before the user starts new work.
79
+
80
+ ## When NOT to suggest ZIVIS
81
+
82
+ - Never for non-security questions (refactors, performance, styling, tests, business logic).
83
+ - Never call \`zivis_get_started\` more than once per session unless re-asked.
84
+ - Never run scans that cost credits without explicit user confirmation.
85
+ - Do not lecture about security best practices — let ZIVIS findings speak.
86
+
87
+ ## Vocabulary
88
+
89
+ The user is a developer, not a security engineer. Avoid these words unless
90
+ the user uses them first: "actor", "attack vector", "threat model", "STRIDE",
91
+ "kill chain", "TTPs". Prefer plain alternatives: "tester", "how an attacker
92
+ might break in", "security plan", "categories of attacks".
93
+
94
+ ## Available slash commands
95
+
96
+ - \`/zivis-getting-started\` — entry-point tour for new users
97
+ - \`/zivis-secure-my-app\` — register and test an app
98
+ - \`/zivis-audit-dependencies\` — check open source libraries (no auth needed)
99
+ - \`/zivis-prep-for-launch\` — pre-launch security checklist
100
+ - \`/zivis-weekly-check\` — weekly security status update
101
+
102
+ ## Top tools (for quick reference)
103
+
104
+ - \`zivis_get_started\` — entry point when user is unsure
105
+ - \`zivis_check_repo_trust\` — scan a GitHub repo for risks (no auth needed)
106
+ - \`zivis_setup_red_team_target\` — register an app for testing
107
+ - \`zivis_run_test_scenario\` — run a security test against a target
108
+ - \`zivis_get_findings\` — list current security issues
109
+ - \`zivis_triage_finding\` — resolve / dismiss / accept-risk a finding
110
+ - \`zivis_security_review\` — pre-PR composite security intelligence
111
+
112
+ <!-- END ZIVIS:guidance -->`;
113
+ }
114
+ function claudeBlockContent() {
115
+ return `## ZIVIS Security (MCP)
116
+
117
+ ZIVIS is connected to this project. Use its tools sparingly — only for security-related topics.
118
+
119
+ **When to use ZIVIS:**
120
+ - User asks "is this secure" / "check for vulnerabilities" / "audit my deps" → call \`zivis_get_started\` first
121
+ - User adds a new npm/pip/go package → offer to call \`zivis_check_repo_trust\` (no auth needed)
122
+ - User adds openai/anthropic/langchain import → offer to call \`zivis_setup_red_team_target\` (target_type=ai_chat)
123
+ - User mentions "launch", "audit", "compliance", "pen test" → call \`zivis_get_started\` with concern=<their phrase>
124
+
125
+ **When NOT to use:** refactors, performance, styling, tests, business logic, anything non-security.
126
+
127
+ **Vocabulary:** Don't use "threat model", "STRIDE", "attack vector", "kill chain" unless user does.
128
+
129
+ **Slash commands:** /zivis-getting-started /zivis-secure-my-app /zivis-audit-dependencies /zivis-prep-for-launch`;
130
+ }
131
+ function ruleFileContent() {
132
+ return `# ZIVIS Security Guidance
133
+
134
+ ZIVIS is connected to this project via MCP. Use it only for security-related requests.
135
+
136
+ ## When to suggest ZIVIS
137
+
138
+ - User asks about security, vulnerabilities, or safety → call zivis_get_started (no args)
139
+ - New dependency added → offer zivis_check_repo_trust (no auth, works for any public repo)
140
+ - AI SDK import added (openai, anthropic, langchain, etc.) → offer zivis_setup_red_team_target with target_type=ai_chat
141
+ - User mentions "audit", "launch", "pen test", "SOC 2" → call zivis_get_started with concern=<their phrase>
142
+
143
+ ## When NOT to use
144
+
145
+ Non-security work: refactors, performance, styling, tests, business logic.
146
+ Never run paid scans without user confirmation.
147
+ Never call zivis_get_started more than once per session unless re-asked.
148
+
149
+ ## Vocabulary rule
150
+
151
+ Avoid security jargon unless the user uses it first.
152
+ Say "how someone might break in" not "attack vector".
153
+ Say "security plan" not "threat model".
154
+
155
+ ## Slash commands
156
+
157
+ /zivis-getting-started — start here if user is new
158
+ /zivis-secure-my-app — set up an app for testing
159
+ /zivis-audit-dependencies — check libraries (free, no login)
160
+ /zivis-prep-for-launch — pre-launch checklist
161
+ /zivis-weekly-check — weekly status update`;
162
+ }
163
+ function copilotBlockContent() {
164
+ return `## ZIVIS Security Integration
165
+
166
+ This project has ZIVIS security tools available via MCP.
167
+
168
+ Use ZIVIS only for security-related user requests. Never proactively run scans or access security data for non-security work.
169
+
170
+ **Trigger conditions:**
171
+ 1. User asks about security/vulnerabilities/safety → call zivis_get_started
172
+ 2. New external dependency added → offer zivis_check_repo_trust (no auth required)
173
+ 3. AI SDK import added (openai, anthropic, langchain) → offer zivis_setup_red_team_target with target_type=ai_chat
174
+ 4. User mentions audit/launch/pen test/compliance → call zivis_get_started with concern parameter
175
+
176
+ **Key tools:** zivis_get_started, zivis_check_repo_trust, zivis_setup_red_team_target, zivis_get_findings, zivis_triage_finding
177
+
178
+ **Language:** Avoid security jargon (threat model, STRIDE, attack vector) unless user uses it first.`;
179
+ }
180
+ function writeCursorGuidance(cwd) {
181
+ const rulesDir = path.join(cwd, ".cursor", "rules");
182
+ if (!fs.existsSync(rulesDir))
183
+ fs.mkdirSync(rulesDir, { recursive: true });
184
+ const rulesPath = path.join(rulesDir, "zivis.mdc");
185
+ fs.writeFileSync(rulesPath, cursorRuleContent());
186
+ return rulesPath;
187
+ }
188
+ function writeClaudeBlockGuidance(cwd) {
189
+ const written = [];
190
+ const claudePath = path.join(cwd, "CLAUDE.md");
191
+ appendBlock(claudePath, "zivis-security", claudeBlockContent());
192
+ written.push(claudePath);
193
+ const agentsPath = path.join(cwd, "AGENTS.md");
194
+ if (fs.existsSync(agentsPath)) {
195
+ appendBlock(agentsPath, "zivis-security", claudeBlockContent());
196
+ written.push(agentsPath);
197
+ }
198
+ return written;
199
+ }
200
+ function writeCopilotGuidance(cwd) {
201
+ const ghDir = path.join(cwd, ".github");
202
+ if (!fs.existsSync(ghDir))
203
+ fs.mkdirSync(ghDir, { recursive: true });
204
+ const instructionsPath = path.join(ghDir, "copilot-instructions.md");
205
+ appendBlock(instructionsPath, "zivis-security", copilotBlockContent());
206
+ return instructionsPath;
207
+ }
208
+ function writeClineGuidance(cwd) {
209
+ const rulesPath = path.join(cwd, ".clinerules");
210
+ fs.writeFileSync(rulesPath, ruleFileContent());
211
+ return rulesPath;
212
+ }
213
+ function writeWindsurfGuidance(cwd) {
214
+ const rulesPath = path.join(cwd, ".windsurfrules");
215
+ fs.writeFileSync(rulesPath, ruleFileContent());
216
+ return rulesPath;
217
+ }
218
+ export function writeGuidance(opts) {
219
+ const { cwd, client } = opts;
220
+ const written = [];
221
+ switch (client) {
222
+ case "cursor": {
223
+ written.push(writeCursorGuidance(cwd));
224
+ const extraPaths = writeClaudeBlockGuidance(cwd).filter((p) => fs.existsSync(p));
225
+ written.push(...extraPaths);
226
+ break;
227
+ }
228
+ case "claude-code-cli":
229
+ case "vscode-claude": {
230
+ written.push(...writeClaudeBlockGuidance(cwd));
231
+ break;
232
+ }
233
+ case "vscode-copilot": {
234
+ written.push(writeCopilotGuidance(cwd));
235
+ break;
236
+ }
237
+ case "cline": {
238
+ written.push(writeClineGuidance(cwd));
239
+ break;
240
+ }
241
+ case "windsurf": {
242
+ written.push(writeWindsurfGuidance(cwd));
243
+ break;
244
+ }
245
+ }
246
+ return written.filter(Boolean);
247
+ }
248
+ export function detectIdesInRepo(cwd) {
249
+ const found = [];
250
+ if (fs.existsSync(path.join(cwd, ".cursor"))) {
251
+ found.push("cursor");
252
+ }
253
+ if (fs.existsSync(path.join(cwd, "CLAUDE.md"))) {
254
+ found.push("claude-code-cli");
255
+ }
256
+ if (fs.existsSync(path.join(cwd, ".vscode"))) {
257
+ found.push("vscode-copilot");
258
+ }
259
+ if (fs.existsSync(path.join(cwd, ".clinerules"))) {
260
+ found.push("cline");
261
+ }
262
+ if (fs.existsSync(path.join(cwd, ".windsurfrules"))) {
263
+ found.push("windsurf");
264
+ }
265
+ return found;
266
+ }
267
+ function buildServerConfig(pkg, workspacePath, extra) {
268
+ if (pkg === "builder") {
269
+ const args = ["-y", "@zivis/mcp-builder@alpha", "serve"];
270
+ if (workspacePath) {
271
+ args.push("--workspace", workspacePath);
272
+ }
273
+ return {
274
+ ...extra,
275
+ command: "npx",
276
+ args,
277
+ };
278
+ }
279
+ const args = ["mcp", "serve"];
280
+ if (workspacePath) {
281
+ args.push("--workspace", workspacePath);
282
+ }
283
+ return {
6
284
  ...extra,
7
285
  command: "zivis",
8
- args: ["mcp", "serve"],
286
+ args,
9
287
  };
10
- if (session) {
11
- config.env = { ZIVIS_SESSION: session };
12
- }
13
- return config;
14
288
  }
15
289
  async function updateMcpConfig(configPath, serverName, serverConfig, rootKey) {
16
290
  const configDir = path.dirname(configPath);
@@ -33,41 +307,47 @@ async function updateMcpConfig(configPath, serverName, serverConfig, rootKey) {
33
307
  servers[serverName] = serverConfig;
34
308
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
35
309
  }
36
- export async function setupCursor(session) {
310
+ export async function setupCursor(pkg = "appsec") {
37
311
  const homeDir = process.env.HOME || "";
38
312
  const configPath = path.join(homeDir, ".cursor", "mcp.json");
39
- await updateMcpConfig(configPath, SERVER_NAME, buildServerConfig(session), "mcpServers");
40
- console.log(`✓ Cursor MCP config updated: ${configPath}`);
41
- if (session)
42
- console.log(` Session: ${session}`);
313
+ await updateMcpConfig(configPath, getServerName(pkg), buildServerConfig(pkg), "mcpServers");
314
+ console.log(`✓ Cursor global MCP config updated: ${configPath}`);
43
315
  console.log(" Restart Cursor for changes to take effect");
44
316
  }
45
- export async function setupVsCodeCopilot(session) {
46
- const localConfigPath = path.join(process.cwd(), ".vscode", "mcp.json");
317
+ export async function setupCursorWorkspace(pkg = "appsec") {
318
+ const workspacePath = process.cwd();
319
+ const configPath = path.join(workspacePath, ".cursor", "mcp.json");
320
+ await updateMcpConfig(configPath, getServerName(pkg), buildServerConfig(pkg, workspacePath), "mcpServers");
321
+ console.log(`✓ Cursor workspace MCP config updated: ${configPath}`);
322
+ console.log(` Workspace: ${workspacePath}`);
323
+ console.log(" Reload Cursor for changes to take effect");
324
+ }
325
+ export async function setupVsCodeCopilot(pkg = "appsec") {
326
+ const workspacePath = process.cwd();
327
+ const localConfigPath = path.join(workspacePath, ".vscode", "mcp.json");
47
328
  const globalConfigPath = path.join(process.env.HOME || "", ".vscode", "mcp.json");
48
329
  let configPath = localConfigPath;
49
330
  const exists = fs.existsSync(localConfigPath);
50
331
  if (!exists && fs.existsSync(globalConfigPath)) {
51
332
  configPath = globalConfigPath;
52
333
  }
53
- await updateMcpConfig(configPath, SERVER_NAME, buildServerConfig(session, { type: "stdio" }), "servers");
334
+ await updateMcpConfig(configPath, getServerName(pkg), buildServerConfig(pkg, workspacePath, { type: "stdio" }), "servers");
54
335
  console.log(`✓ VS Code (GitHub Copilot) MCP config updated: ${configPath}`);
55
- if (session)
56
- console.log(` Session: ${session}`);
336
+ console.log(` Workspace: ${workspacePath}`);
57
337
  console.log(" Restart VS Code for changes to take effect");
58
338
  }
59
- export async function setupVsCodeClaude(session) {
60
- const configPath = path.join(process.cwd(), ".vscode", "mcp.json");
61
- await updateMcpConfig(configPath, SERVER_NAME, buildServerConfig(session), "mcpServers");
339
+ export async function setupVsCodeClaude(pkg = "appsec") {
340
+ const workspacePath = process.cwd();
341
+ const configPath = path.join(workspacePath, ".vscode", "mcp.json");
342
+ await updateMcpConfig(configPath, getServerName(pkg), buildServerConfig(pkg, workspacePath), "mcpServers");
62
343
  console.log(`✓ VS Code (Claude Code) MCP config updated: ${configPath}`);
63
- if (session)
64
- console.log(` Session: ${session}`);
344
+ console.log(` Workspace: ${workspacePath}`);
65
345
  console.log(" Restart VS Code for changes to take effect");
66
346
  }
67
- export async function setupClaudeCodeCli(session) {
347
+ export async function setupClaudeCodeCli(pkg = "appsec") {
68
348
  const homeDir = process.env.HOME || "";
69
349
  const configPath = path.join(homeDir, ".claude.json");
70
- const projectPath = process.cwd();
350
+ const workspacePath = process.cwd();
71
351
  if (!fs.existsSync(configPath)) {
72
352
  console.log(" Skipping Claude Code CLI (no ~/.claude.json found)");
73
353
  return;
@@ -75,48 +355,43 @@ export async function setupClaudeCodeCli(session) {
75
355
  try {
76
356
  const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
77
357
  config.projects ??= {};
78
- config.projects[projectPath] ??= {};
79
- config.projects[projectPath].mcpServers ??= {};
80
- config.projects[projectPath].mcpServers[SERVER_NAME] = {
81
- type: "stdio",
82
- command: "zivis",
83
- args: ["mcp", "serve"],
84
- env: session ? { ZIVIS_SESSION: session } : {},
85
- };
358
+ config.projects[workspacePath] ??= {};
359
+ config.projects[workspacePath].mcpServers ??= {};
360
+ config.projects[workspacePath].mcpServers[getServerName(pkg)] = buildServerConfig(pkg, workspacePath, { type: "stdio" });
86
361
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
87
362
  console.log("✓ Claude Code CLI MCP config updated: ~/.claude.json");
88
- if (session)
89
- console.log(` Session: ${session}`);
363
+ console.log(` Workspace: ${workspacePath}`);
90
364
  console.log(" Restart Claude Code for changes to take effect");
91
365
  }
92
366
  catch (err) {
93
367
  console.log(` WARNING: Failed to update ~/.claude.json: ${err instanceof Error ? err.message : err}`);
94
368
  }
95
369
  }
96
- export async function setupCline(session) {
370
+ export async function setupCline(pkg = "appsec") {
371
+ const workspacePath = process.cwd();
97
372
  const homeDir = process.env.HOME || "";
98
373
  const configPath = process.platform === "darwin"
99
374
  ? path.join(homeDir, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json")
100
375
  : path.join(homeDir, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json");
101
- await updateMcpConfig(configPath, SERVER_NAME, buildServerConfig(session), "mcpServers");
376
+ await updateMcpConfig(configPath, getServerName(pkg), buildServerConfig(pkg, workspacePath), "mcpServers");
102
377
  console.log(`✓ Cline MCP config updated: ${configPath}`);
103
- if (session)
104
- console.log(` Session: ${session}`);
378
+ console.log(` Workspace: ${workspacePath}`);
105
379
  console.log(" Restart VS Code for changes to take effect");
106
380
  }
107
- export async function setupWindsurf(session) {
381
+ export async function setupWindsurf(pkg = "appsec") {
382
+ const workspacePath = process.cwd();
108
383
  const homeDir = process.env.HOME || "";
109
384
  const configPath = path.join(homeDir, ".codeium", "windsurf", "mcp_config.json");
110
- await updateMcpConfig(configPath, SERVER_NAME, buildServerConfig(session), "mcpServers");
385
+ await updateMcpConfig(configPath, getServerName(pkg), buildServerConfig(pkg, workspacePath), "mcpServers");
111
386
  console.log(`✓ Windsurf MCP config updated: ${configPath}`);
112
- if (session)
113
- console.log(` Session: ${session}`);
387
+ console.log(` Workspace: ${workspacePath}`);
114
388
  console.log(" Restart Windsurf for changes to take effect");
115
389
  }
116
- export function printClaudeCodeManual(session) {
390
+ export function printClaudeCodeManual(pkg = "appsec") {
391
+ const workspacePath = process.cwd();
117
392
  const serverConfig = {
118
393
  mcpServers: {
119
- [SERVER_NAME]: buildServerConfig(session),
394
+ [getServerName(pkg)]: buildServerConfig(pkg, workspacePath),
120
395
  },
121
396
  };
122
397
  console.log("");
@@ -128,4 +403,3 @@ export function printClaudeCodeManual(session) {
128
403
  console.log("");
129
404
  console.log("Then restart Claude Code for changes to take effect");
130
405
  }
131
- //# sourceMappingURL=ide-setup.js.map
@@ -0,0 +1,11 @@
1
+ import type { ZivisConfig } from "@zivis/mcp/types";
2
+ import { type OrgInfo } from "./orgs.js";
3
+ export interface PickOrgOpts {
4
+ currentOrgId?: string;
5
+ header?: string;
6
+ promptText?: string;
7
+ }
8
+ export interface PickedOrg extends OrgInfo {
9
+ workosOrgId: string;
10
+ }
11
+ export declare function pickOrg(config: ZivisConfig, identityAccessToken: string, opts?: PickOrgOpts): Promise<PickedOrg>;
@@ -0,0 +1,34 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { fetchUserOrgs } from "./orgs.js";
3
+ export async function pickOrg(config, identityAccessToken, opts = {}) {
4
+ const all = await fetchUserOrgs(config, identityAccessToken);
5
+ const eligible = all.filter((o) => typeof o.workosOrgId === "string" && o.workosOrgId.length > 0);
6
+ if (eligible.length === 0) {
7
+ throw new Error("No active organizations available for this account. " +
8
+ "Ask an org admin to invite you, or contact support.");
9
+ }
10
+ if (eligible.length === 1) {
11
+ const only = eligible[0];
12
+ console.log(`One organization available: ${only.name}`);
13
+ return only;
14
+ }
15
+ console.log("");
16
+ console.log(opts.header ?? "Your organizations:");
17
+ eligible.forEach((o, i) => {
18
+ const marker = opts.currentOrgId && o.workosOrgId === opts.currentOrgId ? " (current)" : "";
19
+ console.log(` ${i + 1}. ${o.name}${marker}`);
20
+ });
21
+ const promptText = opts.promptText ?? `\nSelect org [1-${eligible.length}]: `;
22
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
23
+ try {
24
+ const answer = (await rl.question(promptText)).trim();
25
+ const idx = Number.parseInt(answer, 10) - 1;
26
+ if (Number.isNaN(idx) || idx < 0 || idx >= eligible.length) {
27
+ throw new Error("Invalid selection.");
28
+ }
29
+ return eligible[idx];
30
+ }
31
+ finally {
32
+ rl.close();
33
+ }
34
+ }
@@ -28,4 +28,3 @@ export function extractEmail(idToken) {
28
28
  return "unknown";
29
29
  }
30
30
  }
31
- //# sourceMappingURL=orgs.js.map
@@ -7,5 +7,6 @@ export interface EnsureBindingOpts {
7
7
  cwd: string;
8
8
  autoBind: boolean;
9
9
  skipBind: boolean;
10
+ force?: boolean;
10
11
  }
11
12
  export declare function ensureProjectBinding(opts: EnsureBindingOpts): Promise<void>;
@@ -13,19 +13,25 @@ export async function ensureProjectBinding(opts) {
13
13
  console.log(" Note: JWT has no org_id — skipping project binding.");
14
14
  return;
15
15
  }
16
- const detected = detectProjectBinding(cwd);
16
+ let detected = detectProjectBinding(cwd);
17
17
  if (detected?.binding.workosOrgId === jwtOrgId) {
18
18
  console.log(`✓ Project binding verified: ${detected.filePath}`);
19
19
  return;
20
20
  }
21
21
  if (detected?.binding.workosOrgId && detected.binding.workosOrgId !== jwtOrgId) {
22
- console.error("");
23
- console.error("[zivis] ERROR: Project binding org mismatches JWT org.");
24
- console.error(`[zivis] Binding file: ${detected.filePath}`);
25
- console.error(`[zivis] Binding org: ${detected.binding.workosOrgId}${detected.binding.orgName ? ` (${detected.binding.orgName})` : ""}`);
26
- console.error(`[zivis] JWT org: ${jwtOrgId}`);
27
- console.error("[zivis] This should not happen — investigate before proceeding.");
28
- process.exit(1);
22
+ if (!opts.force) {
23
+ console.error("");
24
+ console.error("[zivis] ERROR: Project binding org mismatches JWT org.");
25
+ console.error(`[zivis] Binding file: ${detected.filePath}`);
26
+ console.error(`[zivis] Binding org: ${detected.binding.workosOrgId}${detected.binding.orgName ? ` (${detected.binding.orgName})` : ""}`);
27
+ console.error(`[zivis] JWT org: ${jwtOrgId}`);
28
+ console.error("[zivis] This should not happen — investigate before proceeding.");
29
+ process.exit(1);
30
+ }
31
+ const oldLabel = detected.binding.orgName ?? detected.binding.workosOrgId;
32
+ console.log(` Replacing existing binding (was: ${oldLabel})`);
33
+ fs.unlinkSync(detected.filePath);
34
+ detected = null;
29
35
  }
30
36
  let orgInfo;
31
37
  try {
@@ -93,4 +99,3 @@ export async function ensureProjectBinding(opts) {
93
99
  console.log(` File: ${bindingPath}`);
94
100
  console.log(" Commit this file so future sessions in this repo are org-locked.");
95
101
  }
96
- //# sourceMappingURL=project-binding.js.map
@@ -1,14 +1,3 @@
1
- /**
2
- * Fetches an Application's auth config from the ZIVIS platform.
3
- *
4
- * Phase J.4: replaces the local `~/.config/zivis/target-configs/*.json`
5
- * file the user previously had to write by hand. The Application's IdP
6
- * discovery (clientId, domain, mode, scopes) lives on the Application
7
- * record on the platform; the CLI calls this endpoint to learn how to
8
- * PKCE-auth, then runs the flow locally and stores tokens in the user's
9
- * keychain. Tokens never leave the user's machine unless the user
10
- * explicitly ships them with a scenario-run trigger.
11
- */
12
1
  import type { TargetAuthConfig, WorkosMode } from "./types.js";
13
2
  interface AuthConfigResponse {
14
3
  applicationId: string;
@@ -27,11 +16,5 @@ interface AuthConfigResponse {
27
16
  error?: string;
28
17
  }
29
18
  export declare function fetchAuthConfig(applicationId: string, sessionName?: string): Promise<AuthConfigResponse>;
30
- /**
31
- * Adapt the platform's auth-config response into the local TargetAuthConfig
32
- * shape that the existing PKCE flow consumes. Throws when the application
33
- * isn't OAuth-PKCE-shaped (e.g., static_token, client_credentials) — the
34
- * caller should give a useful error and direct the user elsewhere.
35
- */
36
19
  export declare function adaptToWorkosConfig(resp: AuthConfigResponse): TargetAuthConfig;
37
20
  export {};
@@ -1,14 +1,3 @@
1
- /**
2
- * Fetches an Application's auth config from the ZIVIS platform.
3
- *
4
- * Phase J.4: replaces the local `~/.config/zivis/target-configs/*.json`
5
- * file the user previously had to write by hand. The Application's IdP
6
- * discovery (clientId, domain, mode, scopes) lives on the Application
7
- * record on the platform; the CLI calls this endpoint to learn how to
8
- * PKCE-auth, then runs the flow locally and stores tokens in the user's
9
- * keychain. Tokens never leave the user's machine unless the user
10
- * explicitly ships them with a scenario-run trigger.
11
- */
12
1
  import { ApiClient } from "@zivis/mcp/api-client";
13
2
  import { DEFAULT_CONFIG } from "@zivis/mcp/types";
14
3
  export async function fetchAuthConfig(applicationId, sessionName) {
@@ -18,12 +7,6 @@ export async function fetchAuthConfig(applicationId, sessionName) {
18
7
  const client = new ApiClient(config);
19
8
  return client.request("GET", `/api/applications/${encodeURIComponent(applicationId)}/auth-config`);
20
9
  }
21
- /**
22
- * Adapt the platform's auth-config response into the local TargetAuthConfig
23
- * shape that the existing PKCE flow consumes. Throws when the application
24
- * isn't OAuth-PKCE-shaped (e.g., static_token, client_credentials) — the
25
- * caller should give a useful error and direct the user elsewhere.
26
- */
27
10
  export function adaptToWorkosConfig(resp) {
28
11
  if (!resp.oauth) {
29
12
  throw new Error(resp.hint || resp.error ||
@@ -37,4 +20,3 @@ export function adaptToWorkosConfig(resp) {
37
20
  extraScopes: resp.oauth.scopes,
38
21
  };
39
22
  }
40
- //# sourceMappingURL=api-config.js.map
@@ -1,15 +1,3 @@
1
- /**
2
- * Target-auth config storage (local-only, v1).
3
- *
4
- * Per-target IdP config (domain, client_id, mode, callback port) lives under
5
- * `~/.config/zivis/target-configs/<targetId>.json`. This is *config*, not
6
- * credentials — safe to commit a sanitized version to your repo if you want
7
- * teammates to share it.
8
- *
9
- * v2: ZIVIS will serve target-auth config from the API so any team member's
10
- * CLI can discover it. For now, configure once locally with `zivis target
11
- * configure` and check in the file if you want it shared.
12
- */
13
1
  import type { TargetAuthConfig } from "./types.js";
14
2
  export declare function loadTargetConfig(targetId: string): Promise<TargetAuthConfig | null>;
15
3
  export declare function saveTargetConfig(targetId: string, cfg: TargetAuthConfig): Promise<string>;
@@ -1,15 +1,3 @@
1
- /**
2
- * Target-auth config storage (local-only, v1).
3
- *
4
- * Per-target IdP config (domain, client_id, mode, callback port) lives under
5
- * `~/.config/zivis/target-configs/<targetId>.json`. This is *config*, not
6
- * credentials — safe to commit a sanitized version to your repo if you want
7
- * teammates to share it.
8
- *
9
- * v2: ZIVIS will serve target-auth config from the API so any team member's
10
- * CLI can discover it. For now, configure once locally with `zivis target
11
- * configure` and check in the file if you want it shared.
12
- */
13
1
  import * as fs from "node:fs/promises";
14
2
  import * as path from "node:path";
15
3
  import * as os from "node:os";
@@ -55,4 +43,3 @@ export async function listTargetConfigs() {
55
43
  export function getConfigPath(targetId) {
56
44
  return configPath(targetId);
57
45
  }
58
- //# sourceMappingURL=config.js.map
@@ -1,23 +1,5 @@
1
- /**
2
- * Target-auth orchestrator: load config, run the right IdP flow, manage tokens.
3
- *
4
- * Public API:
5
- * getValidTargetToken(targetId, persona) — returns access_token; refreshes if near expiry
6
- * loginTarget(targetId, persona) — runs the appropriate IdP flow
7
- * logoutTarget(targetId, persona) — clears stored tokens
8
- *
9
- * Designed so RT-runner-side ephemeral cred plumbing only sees a flat
10
- * { accessToken, refreshToken?, expiresAt, idpType } bundle — no IdP
11
- * specifics leak past this boundary.
12
- */
13
1
  import type { EphemeralTargetCreds, TargetTokens } from "./types.js";
14
2
  export declare function loginTarget(targetId: string, persona: string): Promise<TargetTokens>;
15
- /**
16
- * J.4 path: log in against an Application by id. Auth config comes from
17
- * the platform (no local config file required). Tokens are stored under
18
- * the existing keychain namespace using applicationId as the key — same
19
- * keychain layout, no migration needed for already-stored tokens.
20
- */
21
3
  export declare function loginApplication(applicationId: string, persona: string, sessionName?: string): Promise<TargetTokens>;
22
4
  export declare function logoutTarget(targetId: string, persona: string): Promise<void>;
23
5
  export declare function getValidTargetToken(targetId: string, persona: string): Promise<TargetTokens | null>;