@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,98 +1,270 @@
1
1
  import * as readline from "node:readline/promises";
2
- import { login, getStorageName, saveSessionEntry, loadTokens } from "@zivis/mcp/auth";
3
- import { DEFAULT_CONFIG } from "@zivis/mcp/types";
2
+ import { login, getStorageName, saveSessionEntry } from "@zivis/mcp/auth";
3
+ import { credentialStorageFromConfig, DEFAULT_CONFIG } from "@zivis/mcp/types";
4
+ import { resolveConfigFromBinding } from "@zivis/mcp/project-binding";
4
5
  import { fetchUserOrgs, extractEmail } from "../../internal/orgs.js";
5
6
  import { ensureProjectBinding } from "../../internal/project-binding.js";
6
- export async function initCommand(opts) {
7
- const config = DEFAULT_CONFIG;
8
- const session = opts.session || "default";
9
- // Reuse a stored token if one exists — avoids an extra browser open
10
- let bootstrapToken = null;
11
- const stored = await loadTokens(session);
12
- if (stored) {
13
- bootstrapToken = stored.access_token;
14
- const email = extractEmail(stored.id_token);
15
- console.log(`Using existing session (${email}). To force re-login use: zivis auth logout && zivis auth init`);
7
+ import { discardOauthAfterLogin, tryAutoMintBindingTokenAfterLogin, } from "../../internal/binding-token-mint.js";
8
+ import { decodeJwtPayload } from "@zivis/mcp/auth/jwt";
9
+ import { detectIdesInRepo, writeGuidance } from "../../internal/ide-setup.js";
10
+ import { buildLinkagePrompt } from "../../internal/github-linkage-prompt.js";
11
+ async function maybePromptGitHubLinkage(config) {
12
+ try {
13
+ const msg = await buildLinkagePrompt({ config });
14
+ if (msg)
15
+ console.log(msg);
16
16
  }
17
- else {
18
- console.log("Step 1/2: Authenticate (browser will open)...");
17
+ catch {
18
+ }
19
+ }
20
+ async function prompt(question) {
21
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
22
+ try {
23
+ return (await rl.question(question)).trim();
24
+ }
25
+ finally {
26
+ rl.close();
27
+ }
28
+ }
29
+ async function offerGuidanceInstall(cwd) {
30
+ const detectedIdes = detectIdesInRepo(cwd);
31
+ if (detectedIdes.length === 0)
32
+ return;
33
+ const ideLabels = {
34
+ cursor: "Cursor",
35
+ "claude-code-cli": "Claude Code (CLAUDE.md)",
36
+ "vscode-copilot": "VS Code (Copilot)",
37
+ "vscode-claude": "VS Code (Claude)",
38
+ cline: "Cline",
39
+ windsurf: "Windsurf",
40
+ };
41
+ const ideNames = detectedIdes.map((ide) => ideLabels[ide] ?? ide).join(", ");
42
+ console.log(`\nDetected IDE configs in this repo: ${ideNames}`);
43
+ const ans = await prompt("Add ZIVIS guidance so your AI assistant suggests security checks at the right moments?\n" +
44
+ "(Adds a rule/instruction file. You can edit or remove it anytime.) [Y/n] ");
45
+ if (ans.toLowerCase() === "n" || ans.toLowerCase() === "no") {
46
+ console.log(" Skipped. Run 'zivis mcp install --client <ide> --with-guidance' anytime to add later.");
47
+ return;
48
+ }
49
+ const written = [];
50
+ for (const ide of detectedIdes) {
19
51
  try {
20
- const tokens = await login(config, session, undefined, { skipConfirm: true });
21
- bootstrapToken = tokens.access_token;
22
- const email = extractEmail(tokens.id_token);
23
- console.log(`✓ Authenticated as ${email}`);
52
+ const files = writeGuidance({ cwd, client: ide });
53
+ written.push(...files);
24
54
  }
25
55
  catch (err) {
26
- console.error("Login failed:", err instanceof Error ? err.message : err);
27
- process.exit(1);
56
+ console.warn(` Warning: Could not write guidance for ${ide}: ${err instanceof Error ? err.message : err}`);
28
57
  }
29
58
  }
30
- // Fetch the user's orgs
31
- let orgs;
59
+ if (written.length > 0) {
60
+ console.log(`✓ Wrote guidance files:`);
61
+ for (const f of written)
62
+ console.log(` ${f}`);
63
+ }
64
+ }
65
+ export async function initCommand(opts) {
66
+ const config = {
67
+ ...DEFAULT_CONFIG,
68
+ ...resolveConfigFromBinding(process.cwd()),
69
+ };
70
+ const session = opts.session || "default";
71
+ const envLabel = config.apiBaseUrl.includes("staging") ? "staging" : "production";
72
+ console.log(`Environment: ${envLabel} (${config.apiBaseUrl})`);
73
+ console.log("");
74
+ console.log("Opening browser for authentication...");
75
+ console.log("Log into the org you want to bind this project to.\n");
76
+ let tokens;
32
77
  try {
33
- orgs = await fetchUserOrgs(config, bootstrapToken);
78
+ tokens = await login(config, session, undefined, { skipConfirm: true });
34
79
  }
35
80
  catch (err) {
36
- console.error("Failed to fetch organizations:", err instanceof Error ? err.message : err);
37
- process.exit(1);
38
- }
39
- if (orgs.length === 0) {
40
- console.error("No organizations found for this account. Contact support.");
81
+ console.error("Login failed:", err instanceof Error ? err.message : err);
41
82
  process.exit(1);
42
83
  }
43
- let selectedOrg = orgs[0];
44
- if (orgs.length > 1) {
45
- console.log("\nYour organizations:");
46
- orgs.forEach((org, i) => {
47
- console.log(` ${i + 1}. ${org.name}`);
84
+ const email = extractEmail(tokens.id_token);
85
+ const claims = decodeJwtPayload(tokens.access_token);
86
+ const jwtOrgId = claims?.org_id;
87
+ if (jwtOrgId) {
88
+ let orgName;
89
+ try {
90
+ const orgs = await fetchUserOrgs(config, tokens.access_token);
91
+ orgName = orgs.find((o) => o.workosOrgId === jwtOrgId)?.name;
92
+ if (!orgName && orgs.length > 0) {
93
+ console.log(`\n ⚠ WorkOS org ${jwtOrgId} is not registered in ZIVIS.`);
94
+ console.log(` Your ZIVIS orgs: ${orgs.map((o) => `${o.name} (${o.workosOrgId})`).join(", ")}`);
95
+ console.log(` Try logging into ${config.apiBaseUrl} in your browser first, then re-run init.\n`);
96
+ }
97
+ }
98
+ catch (e) {
99
+ console.error(` (note: org name lookup failed — ${e instanceof Error ? e.message : e})`);
100
+ }
101
+ const orgLabel = orgName ?? `unknown org (${jwtOrgId})`;
102
+ console.log(`\nAuthenticated as: ${email}`);
103
+ console.log(`Org: ${orgLabel}`);
104
+ console.log(`Environment: ${envLabel} (${config.apiBaseUrl})\n`);
105
+ const confirm = await prompt(`Bind this project to "${orgLabel}"? [Y/n] `);
106
+ if (confirm.toLowerCase() === "n" || confirm.toLowerCase() === "no") {
107
+ console.log("");
108
+ const retry = await prompt("Retry login in a private/incognito window? [Y/n] ");
109
+ if (retry.toLowerCase() !== "n" && retry.toLowerCase() !== "no") {
110
+ console.log("\nOpening a private/incognito window — log into the correct org...\n");
111
+ let retryTokens;
112
+ try {
113
+ retryTokens = await login(config, session, undefined, { skipConfirm: true, incognito: true });
114
+ }
115
+ catch (err) {
116
+ console.error("Login failed:", err instanceof Error ? err.message : err);
117
+ process.exit(1);
118
+ }
119
+ tokens = retryTokens;
120
+ const retryClaims = decodeJwtPayload(retryTokens.access_token);
121
+ const retryOrgId = retryClaims?.org_id;
122
+ if (!retryOrgId) {
123
+ console.error("JWT returned no org scope. Try `zivis auth logout` then `zivis auth init` again.");
124
+ process.exit(1);
125
+ }
126
+ let retryOrgName;
127
+ try {
128
+ const retryOrgs = await fetchUserOrgs(config, retryTokens.access_token);
129
+ retryOrgName = retryOrgs.find((o) => o.workosOrgId === retryOrgId)?.name;
130
+ }
131
+ catch (e) {
132
+ console.error(` (note: org name lookup failed — ${e instanceof Error ? e.message : e})`);
133
+ }
134
+ const retryLabel = retryOrgName ?? retryOrgId;
135
+ const retryEmail = extractEmail(retryTokens.id_token);
136
+ console.log(`\nAuthenticated as: ${retryEmail}`);
137
+ console.log(`Org: ${retryLabel}\n`);
138
+ const retryConfirm = await prompt(`Bind this project to "${retryLabel}"? [Y/n] `);
139
+ if (retryConfirm.toLowerCase() === "n" || retryConfirm.toLowerCase() === "no") {
140
+ console.log("Aborted.");
141
+ process.exit(0);
142
+ }
143
+ const storageName2 = getStorageName();
144
+ await saveSessionEntry(session, retryEmail, credentialStorageFromConfig(config));
145
+ console.log(`\n✓ Logged in as ${retryEmail} → ${retryLabel}`);
146
+ console.log(`✓ Credentials stored in ${storageName2}`);
147
+ await ensureProjectBinding({
148
+ config, session, accessToken: retryTokens.access_token,
149
+ organizationId: retryOrgId, cwd: process.cwd(), autoBind: true, skipBind: false, force: true,
150
+ });
151
+ await tryAutoMintBindingTokenAfterLogin({
152
+ cwd: process.cwd(),
153
+ sessionName: session,
154
+ accessToken: retryTokens.access_token,
155
+ skip: opts.skipMintKey === true,
156
+ warnOnFailure: true,
157
+ });
158
+ await discardOauthAfterLogin({ session, config });
159
+ await offerGuidanceInstall(process.cwd());
160
+ await maybePromptGitHubLinkage(config);
161
+ process.exit(0);
162
+ }
163
+ console.log("\nAborted. Switch orgs in your browser then run `zivis auth logout && zivis auth init`.");
164
+ process.exit(0);
165
+ }
166
+ const storageName = getStorageName();
167
+ await saveSessionEntry(session, email, credentialStorageFromConfig(config));
168
+ console.log(`\n✓ Logged in as ${email} → ${orgLabel}`);
169
+ console.log(`✓ Credentials stored in ${storageName}`);
170
+ await ensureProjectBinding({
171
+ config,
172
+ session,
173
+ accessToken: tokens.access_token,
174
+ organizationId: jwtOrgId,
175
+ cwd: process.cwd(),
176
+ autoBind: true,
177
+ skipBind: false,
178
+ force: true,
179
+ });
180
+ await tryAutoMintBindingTokenAfterLogin({
181
+ cwd: process.cwd(),
182
+ sessionName: session,
183
+ accessToken: tokens.access_token,
184
+ skip: opts.skipMintKey === true,
185
+ warnOnFailure: true,
48
186
  });
49
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
187
+ await discardOauthAfterLogin({ session, config });
188
+ await offerGuidanceInstall(process.cwd());
189
+ await maybePromptGitHubLinkage(config);
190
+ }
191
+ else {
192
+ console.log("\nYour account isn't scoped to a specific org yet.");
193
+ console.log("Fetching your organizations...\n");
194
+ let orgs;
50
195
  try {
51
- const answer = (await rl.question(`\nSelect org [1-${orgs.length}]: `)).trim();
196
+ orgs = await fetchUserOrgs(config, tokens.access_token);
197
+ }
198
+ catch (err) {
199
+ console.error("Failed to fetch organizations:", err instanceof Error ? err.message : err);
200
+ process.exit(1);
201
+ }
202
+ if (orgs.length === 0) {
203
+ console.error("No organizations found. Contact support.");
204
+ process.exit(1);
205
+ }
206
+ if (orgs.length === 1) {
207
+ console.log(`One org found: ${orgs[0].name}. Re-authenticating scoped to that org...`);
208
+ }
209
+ else {
210
+ console.log("Your organizations:");
211
+ orgs.forEach((org, i) => console.log(` ${i + 1}. ${org.name}`));
212
+ const answer = await prompt(`\nSelect org to bind this project to [1-${orgs.length}]: `);
52
213
  const idx = parseInt(answer, 10) - 1;
53
214
  if (isNaN(idx) || idx < 0 || idx >= orgs.length) {
54
215
  console.error("Invalid selection.");
55
216
  process.exit(1);
56
217
  }
57
- selectedOrg = orgs[idx];
58
- }
59
- finally {
60
- rl.close();
218
+ const selected = orgs[idx];
219
+ if (!selected.workosOrgId) {
220
+ console.error(`Org "${selected.name}" has no WorkOS org ID.`);
221
+ process.exit(1);
222
+ }
223
+ console.log(`\nRe-opening browser scoped to ${selected.name}...`);
224
+ console.log("(Tip: if your browser logs into the wrong org, use option 2 to get a URL for a private/incognito window)\n");
225
+ try {
226
+ const scopedTokens = await login(config, session, selected.workosOrgId, {
227
+ orgName: selected.name,
228
+ skipConfirm: true,
229
+ });
230
+ const storageName2 = getStorageName();
231
+ await saveSessionEntry(session, email, credentialStorageFromConfig(config));
232
+ console.log(`\n✓ Logged in as ${email} → ${selected.name}`);
233
+ console.log(`✓ Credentials stored in ${storageName2}`);
234
+ await ensureProjectBinding({
235
+ config,
236
+ session,
237
+ accessToken: scopedTokens.access_token,
238
+ organizationId: selected.workosOrgId,
239
+ cwd: process.cwd(),
240
+ autoBind: true,
241
+ skipBind: false,
242
+ force: true,
243
+ });
244
+ await tryAutoMintBindingTokenAfterLogin({
245
+ cwd: process.cwd(),
246
+ sessionName: session,
247
+ accessToken: scopedTokens.access_token,
248
+ skip: opts.skipMintKey === true,
249
+ warnOnFailure: true,
250
+ });
251
+ await discardOauthAfterLogin({ session, config });
252
+ await offerGuidanceInstall(process.cwd());
253
+ }
254
+ catch (err) {
255
+ const msg = err instanceof Error ? err.message : String(err);
256
+ const m = msg.match(/received (org_\w+)/);
257
+ if (m) {
258
+ console.error(`\n⚠ Browser authenticated to a different org than selected.`);
259
+ console.error(` Your browser has an active session for a different org.\n`);
260
+ console.error(` To fix: run \`zivis auth init\` again and paste the URL into a private/incognito window.`);
261
+ }
262
+ else {
263
+ console.error("Login failed:", msg);
264
+ }
265
+ process.exit(1);
266
+ }
61
267
  }
62
268
  }
63
- else {
64
- console.log(`\nOne org found: ${selectedOrg.name}`);
65
- }
66
- if (!selectedOrg.workosOrgId) {
67
- console.error(`Org "${selectedOrg.name}" has no WorkOS organization ID — cannot scope login.`);
68
- process.exit(1);
69
- }
70
- console.log(`\nStep 2/2: Logging in to ${selectedOrg.name} (browser will open)...`);
71
- let scopedTokens;
72
- try {
73
- scopedTokens = await login(config, session, selectedOrg.workosOrgId, {
74
- orgName: selectedOrg.name,
75
- skipConfirm: true,
76
- });
77
- }
78
- catch (err) {
79
- console.error("Login failed:", err instanceof Error ? err.message : err);
80
- process.exit(1);
81
- }
82
- const email = extractEmail(scopedTokens.id_token);
83
- const storageName = getStorageName();
84
- await saveSessionEntry(session, email);
85
- console.log(`\n✓ Logged in as ${email} → ${selectedOrg.name}`);
86
- console.log(`✓ Credentials stored in ${storageName}`);
87
- await ensureProjectBinding({
88
- config,
89
- session,
90
- accessToken: scopedTokens.access_token,
91
- organizationId: selectedOrg.workosOrgId,
92
- cwd: process.cwd(),
93
- autoBind: true,
94
- skipBind: false,
95
- });
96
269
  process.exit(0);
97
270
  }
98
- //# sourceMappingURL=init.js.map
@@ -4,5 +4,7 @@ export interface LoginCmdOpts {
4
4
  yes?: boolean;
5
5
  bind?: boolean;
6
6
  noBind?: boolean;
7
+ rebind?: boolean;
8
+ skipMintKey?: boolean;
7
9
  }
8
10
  export declare function loginCommand(opts: LoginCmdOpts): Promise<void>;
@@ -1,60 +1,161 @@
1
+ import * as readline from "node:readline/promises";
1
2
  import { login } from "@zivis/mcp/auth";
2
3
  import { getStorageName } from "@zivis/mcp/auth";
3
4
  import { saveSessionEntry } from "@zivis/mcp/auth";
4
- import { detectProjectBinding } from "@zivis/mcp/project-binding";
5
- import { DEFAULT_CONFIG } from "@zivis/mcp/types";
5
+ import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
6
+ import { credentialStorageFromConfig, DEFAULT_CONFIG } from "@zivis/mcp/types";
6
7
  import { extractEmail } from "../../internal/orgs.js";
8
+ import { pickOrg } from "../../internal/org-picker.js";
7
9
  import { ensureProjectBinding } from "../../internal/project-binding.js";
8
- import { setupVsCodeClaude, setupVsCodeCopilot, setupClaudeCodeCli, } from "../../internal/ide-setup.js";
10
+ import { discardOauthAfterLogin, tryAutoMintBindingTokenAfterLogin, } from "../../internal/binding-token-mint.js";
11
+ import { setupCursorWorkspace, setupVsCodeClaude, setupVsCodeCopilot, setupClaudeCodeCli, } from "../../internal/ide-setup.js";
12
+ import { buildLinkagePrompt } from "../../internal/github-linkage-prompt.js";
13
+ async function confirm(question) {
14
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
15
+ try {
16
+ const answer = (await rl.question(question)).trim().toLowerCase();
17
+ return answer !== "n" && answer !== "no";
18
+ }
19
+ finally {
20
+ rl.close();
21
+ }
22
+ }
9
23
  export async function loginCommand(opts) {
10
- const config = DEFAULT_CONFIG;
24
+ const config = {
25
+ ...DEFAULT_CONFIG,
26
+ ...resolveConfigFromBinding(process.cwd()),
27
+ };
11
28
  const detected = detectProjectBinding(process.cwd());
12
29
  const session = opts.session || detected?.binding.session || config.session;
13
- let organizationId = opts.org;
14
- if (!organizationId && detected?.binding.workosOrgId) {
30
+ const sessionName = session || "default";
31
+ let organizationId;
32
+ let orgName;
33
+ let writeMode = "no-write";
34
+ if (opts.org) {
35
+ organizationId = opts.org;
36
+ orgName = detected?.binding.orgName && detected.binding.workosOrgId === opts.org
37
+ ? detected.binding.orgName
38
+ : undefined;
39
+ if (opts.rebind) {
40
+ writeMode = "replace";
41
+ }
42
+ }
43
+ else if (opts.rebind) {
44
+ const picked = await runIdentityThenPicker(config, sessionName, {
45
+ currentOrgId: detected?.binding.workosOrgId ?? undefined,
46
+ header: detected?.binding.workosOrgId
47
+ ? "Switch this project to which org?"
48
+ : "Your organizations:",
49
+ });
50
+ if (detected?.binding.workosOrgId && detected.binding.workosOrgId === picked.workosOrgId) {
51
+ console.log(`Project is already bound to ${picked.name}. Nothing to change.`);
52
+ process.exit(0);
53
+ }
54
+ if (detected?.binding.workosOrgId) {
55
+ const fromLabel = detected.binding.orgName
56
+ ? `${detected.binding.orgName} (${detected.binding.workosOrgId})`
57
+ : detected.binding.workosOrgId;
58
+ const toLabel = `${picked.name} (${picked.workosOrgId})`;
59
+ if (!opts.yes) {
60
+ const ok = await confirm(`Change project binding from "${fromLabel}" to "${toLabel}"? [Y/n] `);
61
+ if (!ok) {
62
+ console.log("Aborted.");
63
+ process.exit(0);
64
+ }
65
+ }
66
+ writeMode = "replace";
67
+ }
68
+ else {
69
+ writeMode = "create";
70
+ }
71
+ organizationId = picked.workosOrgId;
72
+ orgName = picked.name;
73
+ }
74
+ else if (detected?.binding.workosOrgId) {
15
75
  organizationId = detected.binding.workosOrgId;
16
- console.log(`Using org from project binding: ${organizationId}${detected.binding.orgName ? ` (${detected.binding.orgName})` : ""}`);
76
+ orgName = detected.binding.orgName;
77
+ console.log(`Using org from project binding: ${organizationId}${orgName ? ` (${orgName})` : ""}`);
78
+ }
79
+ else {
80
+ const picked = await runIdentityThenPicker(config, sessionName, {
81
+ header: "Your organizations:",
82
+ });
83
+ organizationId = picked.workosOrgId;
84
+ orgName = picked.name;
85
+ writeMode = "create";
86
+ }
87
+ if (!organizationId) {
88
+ console.error("Internal error: no organization resolved for login.");
89
+ process.exit(1);
17
90
  }
18
91
  if (detected?.binding.session && !opts.session) {
19
92
  console.log(`Using session from project binding: ${session}`);
20
93
  }
21
94
  try {
22
95
  const tokens = await login(config, session, organizationId, {
23
- orgName: detected?.binding.orgName,
24
- skipConfirm: opts.yes === true,
96
+ orgName,
97
+ skipConfirm: opts.yes === true || writeMode !== "no-write",
25
98
  });
26
99
  const storageName = getStorageName();
27
100
  const email = extractEmail(tokens.id_token);
28
- const sessionName = session || "default";
29
- await saveSessionEntry(sessionName, email);
101
+ await saveSessionEntry(sessionName, email, credentialStorageFromConfig(config));
30
102
  const sessionLabel = session ? ` (session: ${session})` : "";
31
103
  console.log(`✓ Logged in as ${email}${sessionLabel}`);
32
104
  console.log(`✓ Credentials stored in ${storageName}`);
33
- if (organizationId) {
34
- console.log(`✓ JWT scoped to organization: ${organizationId}`);
105
+ console.log(`✓ JWT scoped to organization: ${organizationId}${orgName ? ` (${orgName})` : ""}`);
106
+ try {
107
+ await setupCursorWorkspace();
108
+ }
109
+ catch (err) {
110
+ console.log(` WARNING: Failed to update .cursor/mcp.json: ${err instanceof Error ? err.message : err}`);
35
111
  }
36
112
  try {
37
- await setupVsCodeClaude(session);
38
- await setupVsCodeCopilot(session);
113
+ await setupVsCodeClaude();
114
+ await setupVsCodeCopilot();
39
115
  }
40
116
  catch (err) {
41
117
  console.log(` WARNING: Failed to update .vscode/mcp.json: ${err instanceof Error ? err.message : err}`);
42
118
  }
43
119
  try {
44
- await setupClaudeCodeCli(session);
120
+ await setupClaudeCodeCli();
45
121
  }
46
122
  catch (err) {
47
123
  console.log(` WARNING: Failed to update Claude Code CLI config: ${err instanceof Error ? err.message : err}`);
48
124
  }
125
+ if (writeMode === "create" && !opts.yes && !opts.noBind) {
126
+ const orgLabel = orgName ? `${orgName} (${organizationId})` : organizationId;
127
+ const ok = await confirm(`Bind this project to ${orgLabel}? [Y/n] `);
128
+ if (!ok) {
129
+ console.log("Login succeeded but project was not bound.");
130
+ await discardOauthAfterLogin({ session, config });
131
+ process.exit(0);
132
+ }
133
+ }
49
134
  await ensureProjectBinding({
50
135
  config,
51
136
  session: sessionName,
52
137
  accessToken: tokens.access_token,
53
138
  organizationId,
54
139
  cwd: process.cwd(),
55
- autoBind: opts.bind === true,
140
+ autoBind: opts.bind === true || writeMode !== "no-write",
56
141
  skipBind: opts.noBind === true,
142
+ force: writeMode === "replace",
57
143
  });
144
+ await tryAutoMintBindingTokenAfterLogin({
145
+ cwd: process.cwd(),
146
+ sessionName,
147
+ accessToken: tokens.access_token,
148
+ skip: opts.skipMintKey === true,
149
+ warnOnFailure: true,
150
+ });
151
+ await discardOauthAfterLogin({ session, config });
152
+ try {
153
+ const linkagePrompt = await buildLinkagePrompt({ config });
154
+ if (linkagePrompt)
155
+ console.log(linkagePrompt);
156
+ }
157
+ catch {
158
+ }
58
159
  process.exit(0);
59
160
  }
60
161
  catch (err) {
@@ -62,4 +163,21 @@ export async function loginCommand(opts) {
62
163
  process.exit(1);
63
164
  }
64
165
  }
65
- //# sourceMappingURL=login.js.map
166
+ async function runIdentityThenPicker(config, session, pickOpts) {
167
+ console.log("Opening browser to identify your account (no org scope yet)...");
168
+ let identity;
169
+ try {
170
+ identity = await login(config, session, undefined, { skipConfirm: true });
171
+ }
172
+ catch (err) {
173
+ console.error("Login failed:", err instanceof Error ? err.message : err);
174
+ process.exit(1);
175
+ }
176
+ try {
177
+ return await pickOrg(config, identity.access_token, pickOpts);
178
+ }
179
+ catch (err) {
180
+ console.error(err instanceof Error ? err.message : err);
181
+ process.exit(1);
182
+ }
183
+ }
@@ -1,14 +1,26 @@
1
- import { keychainClearAll, keychainClearSession } from "@zivis/mcp/auth";
1
+ import { keychainClearAll, keychainClearSession, keychainDelete, keychainRead } from "@zivis/mcp/auth";
2
2
  import { removeSessionEntry } from "@zivis/mcp/auth";
3
+ import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
4
+ import { credentialStorageFromConfig } from "@zivis/mcp/types";
3
5
  export async function logoutCommand(opts) {
4
- if (opts.session) {
5
- await keychainClearSession(opts.session);
6
- await removeSessionEntry(opts.session);
7
- console.log(`✓ Session "${opts.session}" credentials cleared`);
6
+ const config = resolveConfigFromBinding(process.cwd());
7
+ const st = credentialStorageFromConfig(config);
8
+ const sessionName = opts.session ??
9
+ detectProjectBinding(process.cwd())?.binding.session;
10
+ if (sessionName) {
11
+ await keychainClearSession(sessionName, st);
12
+ const apiKeyAccount = `apikey.${sessionName}`;
13
+ const hadApiKey = !!(await keychainRead(apiKeyAccount, st).catch(() => null));
14
+ if (hadApiKey) {
15
+ await keychainDelete(apiKeyAccount, st);
16
+ console.log(`✓ Local Project Binding Token cleared for session "${sessionName}"`);
17
+ console.log(` Note: To revoke the key server-side, visit Settings → API Tokens in the ZIVIS UI.`);
18
+ }
19
+ await removeSessionEntry(sessionName, st);
20
+ console.log(`✓ Session "${sessionName}" credentials cleared`);
8
21
  return;
9
22
  }
10
- await keychainClearAll();
23
+ await keychainClearAll(st);
11
24
  console.log("✓ Default credentials cleared");
12
25
  console.log(" Note: Named sessions are not affected. Use 'zivis auth sessions' to list them.");
13
26
  }
14
- //# sourceMappingURL=logout.js.map
@@ -1,12 +1,16 @@
1
1
  import { listSessions, removeSessionEntry, keychainClearSession } from "@zivis/mcp/auth";
2
+ import { resolveConfigFromBinding } from "@zivis/mcp/project-binding";
3
+ import { credentialStorageFromConfig } from "@zivis/mcp/types";
2
4
  export async function sessionsCommand(opts) {
5
+ const config = resolveConfigFromBinding(process.cwd());
6
+ const st = credentialStorageFromConfig(config);
3
7
  if (opts.remove) {
4
- await keychainClearSession(opts.remove);
5
- await removeSessionEntry(opts.remove);
8
+ await keychainClearSession(opts.remove, st);
9
+ await removeSessionEntry(opts.remove, st);
6
10
  console.log(`✓ Session "${opts.remove}" removed`);
7
11
  return;
8
12
  }
9
- const sessions = await listSessions();
13
+ const sessions = await listSessions(st);
10
14
  const activeSession = process.env.ZIVIS_SESSION;
11
15
  if (sessions.length === 0) {
12
16
  console.log("No sessions stored.");
@@ -27,4 +31,3 @@ export async function sessionsCommand(opts) {
27
31
  console.log("To use a session in a project, add to .mcp.json:");
28
32
  console.log(' "env": { "ZIVIS_SESSION": "<session-name>" }');
29
33
  }
30
- //# sourceMappingURL=sessions.js.map