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

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 (180) 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 +8 -18
  5. package/dist/commands/assurance/index.d.ts +33 -0
  6. package/dist/commands/assurance/index.js +149 -0
  7. package/dist/commands/auth/bind.d.ts +4 -0
  8. package/dist/commands/auth/bind.js +97 -0
  9. package/dist/commands/auth/index.js +21 -2
  10. package/dist/commands/auth/init.d.ts +2 -0
  11. package/dist/commands/auth/init.js +249 -73
  12. package/dist/commands/auth/login.d.ts +2 -0
  13. package/dist/commands/auth/login.js +136 -18
  14. package/dist/commands/auth/logout.js +19 -7
  15. package/dist/commands/auth/sessions.js +7 -4
  16. package/dist/commands/auth/status.js +33 -12
  17. package/dist/commands/auth/token.d.ts +4 -0
  18. package/dist/commands/auth/token.js +73 -0
  19. package/dist/commands/check/index.d.ts +2 -0
  20. package/dist/commands/check/index.js +463 -0
  21. package/dist/commands/credits/index.js +2 -3
  22. package/dist/commands/gate/index.d.ts +2 -0
  23. package/dist/commands/gate/index.js +171 -0
  24. package/dist/commands/inspect/index.d.ts +2 -0
  25. package/dist/commands/inspect/index.js +136 -0
  26. package/dist/commands/mcp/index.js +113 -14
  27. package/dist/commands/open/index.d.ts +0 -8
  28. package/dist/commands/open/index.js +0 -11
  29. package/dist/commands/run/index.d.ts +2 -0
  30. package/dist/commands/run/index.js +111 -0
  31. package/dist/commands/scan/index.js +3 -8
  32. package/dist/commands/sync/index.d.ts +2 -0
  33. package/dist/commands/sync/index.js +187 -0
  34. package/dist/commands/target/index.js +0 -3
  35. package/dist/commands/test/index.d.ts +2 -0
  36. package/dist/commands/test/index.js +115 -0
  37. package/dist/commands/tm/index.js +7 -10
  38. package/dist/index.js +24 -1
  39. package/dist/internal/application-binding.d.ts +8 -0
  40. package/dist/internal/application-binding.js +167 -0
  41. package/dist/internal/binding-token-mint.d.ts +33 -0
  42. package/dist/internal/binding-token-mint.js +174 -0
  43. package/dist/internal/cli-output.d.ts +16 -0
  44. package/dist/internal/cli-output.js +39 -0
  45. package/dist/internal/devx-run.d.ts +47 -0
  46. package/dist/internal/devx-run.js +36 -0
  47. package/dist/internal/extractor/adapters/go/index.d.ts +0 -4
  48. package/dist/internal/extractor/adapters/go/index.js +0 -6
  49. package/dist/internal/extractor/adapters/go/manifest.d.ts +0 -17
  50. package/dist/internal/extractor/adapters/go/manifest.js +19 -25
  51. package/dist/internal/extractor/adapters/go/parser.d.ts +0 -4
  52. package/dist/internal/extractor/adapters/go/parser.js +0 -5
  53. package/dist/internal/extractor/adapters/go/scanner.d.ts +0 -20
  54. package/dist/internal/extractor/adapters/go/scanner.js +0 -43
  55. package/dist/internal/extractor/adapters/python/index.d.ts +0 -3
  56. package/dist/internal/extractor/adapters/python/index.js +0 -4
  57. package/dist/internal/extractor/adapters/python/manifest.d.ts +0 -8
  58. package/dist/internal/extractor/adapters/python/manifest.js +21 -18
  59. package/dist/internal/extractor/adapters/python/parser.d.ts +0 -3
  60. package/dist/internal/extractor/adapters/python/parser.js +0 -4
  61. package/dist/internal/extractor/adapters/python/scanner.d.ts +0 -18
  62. package/dist/internal/extractor/adapters/python/scanner.js +1 -54
  63. package/dist/internal/extractor/adapters/typescript/index.d.ts +0 -7
  64. package/dist/internal/extractor/adapters/typescript/index.js +1 -21
  65. package/dist/internal/extractor/adapters/typescript/manifest.d.ts +0 -14
  66. package/dist/internal/extractor/adapters/typescript/manifest.js +26 -17
  67. package/dist/internal/extractor/adapters/typescript/parser.d.ts +0 -8
  68. package/dist/internal/extractor/adapters/typescript/parser.js +0 -9
  69. package/dist/internal/extractor/adapters/typescript/scanner.d.ts +0 -24
  70. package/dist/internal/extractor/adapters/typescript/scanner.js +0 -43
  71. package/dist/internal/extractor/graph-builder.d.ts +10 -0
  72. package/dist/internal/extractor/graph-builder.js +245 -0
  73. package/dist/internal/extractor/graph-types.d.ts +89 -0
  74. package/dist/internal/extractor/graph-types.js +1 -0
  75. package/dist/internal/extractor/index.d.ts +0 -14
  76. package/dist/internal/extractor/index.js +0 -13
  77. package/dist/internal/extractor/types.d.ts +0 -45
  78. package/dist/internal/extractor/types.js +0 -27
  79. package/dist/internal/extractor/walker.d.ts +0 -18
  80. package/dist/internal/extractor/walker.js +0 -18
  81. package/dist/internal/gate-evaluate.d.ts +50 -0
  82. package/dist/internal/gate-evaluate.js +111 -0
  83. package/dist/internal/gate-policy.d.ts +38 -0
  84. package/dist/internal/gate-policy.js +167 -0
  85. package/dist/internal/git-metadata.d.ts +8 -0
  86. package/dist/internal/git-metadata.js +38 -0
  87. package/dist/internal/git.d.ts +8 -0
  88. package/dist/internal/git.js +30 -0
  89. package/dist/internal/github-linkage-prompt.d.ts +7 -0
  90. package/dist/internal/github-linkage-prompt.js +95 -0
  91. package/dist/internal/ide-setup.d.ts +17 -7
  92. package/dist/internal/ide-setup.js +377 -46
  93. package/dist/internal/inventory-sync.d.ts +74 -0
  94. package/dist/internal/inventory-sync.js +189 -0
  95. package/dist/internal/org-picker.d.ts +11 -0
  96. package/dist/internal/org-picker.js +34 -0
  97. package/dist/internal/orgs.js +0 -1
  98. package/dist/internal/packs/cache.d.ts +8 -0
  99. package/dist/internal/packs/cache.js +60 -0
  100. package/dist/internal/packs/index.d.ts +10 -0
  101. package/dist/internal/packs/index.js +7 -0
  102. package/dist/internal/packs/integrity.d.ts +9 -0
  103. package/dist/internal/packs/integrity.js +24 -0
  104. package/dist/internal/packs/jcs.d.ts +2 -0
  105. package/dist/internal/packs/jcs.js +57 -0
  106. package/dist/internal/packs/local-paths.d.ts +4 -0
  107. package/dist/internal/packs/local-paths.js +26 -0
  108. package/dist/internal/packs/registry-client.d.ts +20 -0
  109. package/dist/internal/packs/registry-client.js +40 -0
  110. package/dist/internal/packs/resolve.d.ts +8 -0
  111. package/dist/internal/packs/resolve.js +89 -0
  112. package/dist/internal/packs/semver.d.ts +9 -0
  113. package/dist/internal/packs/semver.js +57 -0
  114. package/dist/internal/packs/signing.d.ts +5 -0
  115. package/dist/internal/packs/signing.js +49 -0
  116. package/dist/internal/packs/types.d.ts +70 -0
  117. package/dist/internal/packs/types.js +20 -0
  118. package/dist/internal/project-binding.d.ts +1 -0
  119. package/dist/internal/project-binding.js +14 -9
  120. package/dist/internal/run-workdir.d.ts +1 -0
  121. package/dist/internal/run-workdir.js +11 -0
  122. package/dist/internal/security-context.d.ts +62 -0
  123. package/dist/internal/security-context.js +50 -0
  124. package/dist/internal/sync-outbox.d.ts +40 -0
  125. package/dist/internal/sync-outbox.js +66 -0
  126. package/dist/internal/target-auth/api-config.d.ts +0 -17
  127. package/dist/internal/target-auth/api-config.js +0 -18
  128. package/dist/internal/target-auth/config.d.ts +0 -12
  129. package/dist/internal/target-auth/config.js +0 -13
  130. package/dist/internal/target-auth/index.d.ts +0 -18
  131. package/dist/internal/target-auth/index.js +0 -19
  132. package/dist/internal/target-auth/keychain.d.ts +0 -18
  133. package/dist/internal/target-auth/keychain.js +3 -23
  134. package/dist/internal/target-auth/types.d.ts +0 -26
  135. package/dist/internal/target-auth/types.js +0 -10
  136. package/dist/internal/target-auth/workos.d.ts +0 -15
  137. package/dist/internal/target-auth/workos.js +9 -18
  138. package/dist/internal/test-scope.d.ts +68 -0
  139. package/dist/internal/test-scope.js +69 -0
  140. package/dist/internal/zivis-local-state.d.ts +1 -0
  141. package/dist/internal/zivis-local-state.js +22 -0
  142. package/package.json +20 -5
  143. package/dist/commands/app/index.js.map +0 -1
  144. package/dist/commands/auth/index.js.map +0 -1
  145. package/dist/commands/auth/init.js.map +0 -1
  146. package/dist/commands/auth/login.js.map +0 -1
  147. package/dist/commands/auth/logout.js.map +0 -1
  148. package/dist/commands/auth/sessions.js.map +0 -1
  149. package/dist/commands/auth/status.js.map +0 -1
  150. package/dist/commands/credits/index.js.map +0 -1
  151. package/dist/commands/mcp/index.js.map +0 -1
  152. package/dist/commands/open/index.js.map +0 -1
  153. package/dist/commands/scan/index.js.map +0 -1
  154. package/dist/commands/target/index.js.map +0 -1
  155. package/dist/commands/tm/index.js.map +0 -1
  156. package/dist/index.js.map +0 -1
  157. package/dist/internal/extractor/adapters/go/index.js.map +0 -1
  158. package/dist/internal/extractor/adapters/go/manifest.js.map +0 -1
  159. package/dist/internal/extractor/adapters/go/parser.js.map +0 -1
  160. package/dist/internal/extractor/adapters/go/scanner.js.map +0 -1
  161. package/dist/internal/extractor/adapters/python/index.js.map +0 -1
  162. package/dist/internal/extractor/adapters/python/manifest.js.map +0 -1
  163. package/dist/internal/extractor/adapters/python/parser.js.map +0 -1
  164. package/dist/internal/extractor/adapters/python/scanner.js.map +0 -1
  165. package/dist/internal/extractor/adapters/typescript/index.js.map +0 -1
  166. package/dist/internal/extractor/adapters/typescript/manifest.js.map +0 -1
  167. package/dist/internal/extractor/adapters/typescript/parser.js.map +0 -1
  168. package/dist/internal/extractor/adapters/typescript/scanner.js.map +0 -1
  169. package/dist/internal/extractor/index.js.map +0 -1
  170. package/dist/internal/extractor/types.js.map +0 -1
  171. package/dist/internal/extractor/walker.js.map +0 -1
  172. package/dist/internal/ide-setup.js.map +0 -1
  173. package/dist/internal/orgs.js.map +0 -1
  174. package/dist/internal/project-binding.js.map +0 -1
  175. package/dist/internal/target-auth/api-config.js.map +0 -1
  176. package/dist/internal/target-auth/config.js.map +0 -1
  177. package/dist/internal/target-auth/index.js.map +0 -1
  178. package/dist/internal/target-auth/keychain.js.map +0 -1
  179. package/dist/internal/target-auth/types.js.map +0 -1
  180. package/dist/internal/target-auth/workos.js.map +0 -1
@@ -1,12 +1,13 @@
1
- import { resolveAuth, loadTokens, getStorageName, keychainRead } from "@zivis/mcp/auth";
2
- import { detectProjectBinding } from "@zivis/mcp/project-binding";
3
- import { DEFAULT_CONFIG } from "@zivis/mcp/types";
1
+ import { resolveAuth, loadTokens, keychainRead } from "@zivis/mcp/auth";
2
+ import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
3
+ import { DEFAULT_CONFIG, credentialStorageFromConfig } from "@zivis/mcp/types";
4
4
  import { extractEmail } from "../../internal/orgs.js";
5
5
  export async function statusCommand(opts) {
6
- const config = DEFAULT_CONFIG;
6
+ const config = { ...DEFAULT_CONFIG, ...resolveConfigFromBinding(process.cwd()) };
7
7
  const detected = detectProjectBinding(process.cwd());
8
8
  const session = opts.session || config.session || detected?.binding.session;
9
9
  const resolveConfig = session ? { ...config, session } : config;
10
+ const st = credentialStorageFromConfig(resolveConfig);
10
11
  if (session) {
11
12
  const source = opts.session
12
13
  ? "flag"
@@ -18,7 +19,7 @@ export async function statusCommand(opts) {
18
19
  const creds = await resolveAuth(resolveConfig);
19
20
  switch (creds.method) {
20
21
  case "oauth": {
21
- const tokens = await loadTokens(session);
22
+ const tokens = await loadTokens(session, st);
22
23
  const email = extractEmail(tokens?.id_token);
23
24
  console.log(`Auth: OAuth (${email})`);
24
25
  if (tokens) {
@@ -28,11 +29,30 @@ export async function statusCommand(opts) {
28
29
  break;
29
30
  }
30
31
  case "apikey": {
31
- const masked = creds.apiKey?.startsWith("sk_")
32
- ? `sk_...${creds.apiKey.slice(-4)}`
32
+ const key = creds.apiKey ?? "";
33
+ const masked = key.startsWith("sk_")
34
+ ? `${key.slice(0, 10)}...${key.slice(-4)}`
33
35
  : "***";
34
- const source = (await keychainRead("apikey")) ? getStorageName() : "environment variable";
35
- console.log(`Auth: API Key (${masked}) from ${source}`);
36
+ const sessionApiKeyAccount = session ? `apikey.${session}` : null;
37
+ const isSessionKey = sessionApiKeyAccount
38
+ ? !!(await keychainRead(sessionApiKeyAccount, st))
39
+ : false;
40
+ const isEnvKey = key === process.env.ZIVIS_API_KEY;
41
+ const isGlobalKey = !isSessionKey && !isEnvKey && !!(await keychainRead("apikey", st));
42
+ if (isSessionKey && key.startsWith("sk_")) {
43
+ console.log(`Auth: Local Project Binding Token (${masked})`);
44
+ console.log(` Minted by \`zivis auth bind\` — org-scoped, no refresh needed`);
45
+ }
46
+ else if (isEnvKey) {
47
+ console.log(`Auth: API Key from ZIVIS_API_KEY env var (${masked})`);
48
+ }
49
+ else if (isGlobalKey) {
50
+ console.log(`Auth: API Key from keychain (${masked})`);
51
+ console.log(` Run \`zivis auth bind\` in this repo for project-scoped credentials`);
52
+ }
53
+ else {
54
+ console.log(`Auth: API Key (${masked})`);
55
+ }
36
56
  break;
37
57
  }
38
58
  case "none":
@@ -40,14 +60,15 @@ export async function statusCommand(opts) {
40
60
  console.log("");
41
61
  if (session) {
42
62
  console.log(`Session "${session}" has no credentials.`);
43
- console.log(`Run: zivis auth login --session ${session}`);
63
+ console.log(` Run in this repo: zivis auth bind`);
64
+ console.log(` Or manually: zivis auth login --session ${session}`);
44
65
  }
45
66
  else {
46
67
  console.log("To authenticate:");
47
- console.log(" zivis auth login");
68
+ console.log(" zivis auth bind (recommended — mints a project-scoped token)");
69
+ console.log(" zivis auth login (interactive OAuth for web access)");
48
70
  }
49
71
  break;
50
72
  }
51
73
  console.log(`API: ${resolveConfig.apiBaseUrl}`);
52
74
  }
53
- //# sourceMappingURL=status.js.map
@@ -0,0 +1,4 @@
1
+ export interface TokenCmdOpts {
2
+ session?: string;
3
+ }
4
+ export declare function tokenCommand(apiKey: string, opts: TokenCmdOpts): Promise<void>;
@@ -0,0 +1,73 @@
1
+ import { keychainStore, getStorageName } from "@zivis/mcp/auth";
2
+ import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
3
+ import { DEFAULT_CONFIG, credentialStorageFromConfig } from "@zivis/mcp/types";
4
+ const API_KEY_PREFIX = "sk_";
5
+ const API_KEY_REGEX = /^sk_[a-z0-9_]{10,}$/i;
6
+ function getApiKeyAccount(session) {
7
+ return session ? `apikey.${session}` : "apikey";
8
+ }
9
+ function validateKeyFormat(key) {
10
+ return API_KEY_REGEX.test(key);
11
+ }
12
+ export async function tokenCommand(apiKey, opts) {
13
+ if (!validateKeyFormat(apiKey)) {
14
+ console.error(`Invalid API token format. Expected: ${API_KEY_PREFIX}<token value>`);
15
+ console.error(`Got: ${apiKey.substring(0, 20)}...`);
16
+ console.error(`\nGenerate a token at: Settings → API Tokens in the ZIVIS platform.`);
17
+ process.exit(1);
18
+ }
19
+ const cwd = process.cwd();
20
+ const detected = detectProjectBinding(cwd);
21
+ const session = opts.session ?? detected?.binding.session;
22
+ const config = { ...DEFAULT_CONFIG, ...resolveConfigFromBinding(cwd) };
23
+ const account = getApiKeyAccount(session);
24
+ const sessionLabel = session ? `session "${session}"` : "default session";
25
+ const storageName = getStorageName();
26
+ try {
27
+ await keychainStore(account, apiKey, credentialStorageFromConfig(config));
28
+ }
29
+ catch (err) {
30
+ console.error(`Failed to store token: ${err instanceof Error ? err.message : err}`);
31
+ process.exit(1);
32
+ }
33
+ const maskedKey = `${apiKey.substring(0, 12)}...${apiKey.slice(-4)}`;
34
+ console.log(`✓ API token stored for ${sessionLabel}`);
35
+ console.log(` Key: ${maskedKey}`);
36
+ console.log(` Storage: ${storageName} (account: ${account})`);
37
+ if (session) {
38
+ console.log(`\nThe MCP server will use this token automatically for projects bound to session "${session}".`);
39
+ }
40
+ else {
41
+ console.log(`\nThe MCP server will use this token for the default session.`);
42
+ console.log(`Tip: Use --session <name> to scope this token to a specific org.`);
43
+ }
44
+ if (detected) {
45
+ const orgLabel = detected.binding.orgName
46
+ ? ` (${detected.binding.orgName})`
47
+ : "";
48
+ console.log(`\nProject binding detected: this repo is bound to session "${detected.binding.session}"${orgLabel}.`);
49
+ if (!session) {
50
+ console.log(` Tip: Run with --session ${detected.binding.session} to scope this token to that org.`);
51
+ }
52
+ }
53
+ try {
54
+ const response = await fetch(`${config.apiBaseUrl}/api/mcp/session`, {
55
+ headers: { "X-API-Key": apiKey, "User-Agent": "@zivis/cli" },
56
+ });
57
+ if (response.ok) {
58
+ const data = (await response.json());
59
+ const orgName = data.org?.name ?? "unknown org";
60
+ const email = data.user?.email ?? "unknown user";
61
+ console.log(`\n✓ Token verified — authenticated as ${email} in ${orgName}`);
62
+ }
63
+ else {
64
+ const body = await response.json().catch(() => ({}));
65
+ console.log(`\n Warning: Could not verify token against API (HTTP ${response.status}): ${body.message ?? body.error ?? "unknown error"}`);
66
+ console.log(` Token was stored but may be invalid or lack the 'mcp' scope.`);
67
+ }
68
+ }
69
+ catch {
70
+ console.log(`\n Note: Could not reach the ZIVIS API to verify the token (stored anyway).`);
71
+ }
72
+ process.exit(0);
73
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerCheckCommands(program: Command): void;
@@ -0,0 +1,463 @@
1
+ import * as crypto from "node:crypto";
2
+ import * as readline from "node:readline";
3
+ import * as fs from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import * as os from "node:os";
6
+ import { exec } from "node:child_process";
7
+ import { promisify } from "node:util";
8
+ import { resolveAuth } from "@zivis/mcp/auth";
9
+ import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
10
+ import { DEFAULT_CONFIG } from "@zivis/mcp/types";
11
+ import { runMatcher } from "@zivis/mcp/matcher";
12
+ import { runExtractor } from "../../internal/extractor/index.js";
13
+ import { buildRepoGraphArtifact } from "../../internal/extractor/graph-builder.js";
14
+ import { loginCommand } from "../auth/login.js";
15
+ import { buildLinkagePrompt } from "../../internal/github-linkage-prompt.js";
16
+ const execAsync = promisify(exec);
17
+ const LLM_PACKAGE_NAMES = new Set([
18
+ "openai",
19
+ "@anthropic-ai/sdk",
20
+ "anthropic",
21
+ "@anthropic-ai/bedrock-sdk",
22
+ "@anthropic-ai/vertex-sdk",
23
+ "@google/generative-ai",
24
+ "@google-cloud/aiplatform",
25
+ "@google-cloud/vertexai",
26
+ "@aws-sdk/client-bedrock-runtime",
27
+ "@azure/openai",
28
+ "langchain",
29
+ "@langchain/core",
30
+ "@langchain/openai",
31
+ "@langchain/anthropic",
32
+ "llamaindex",
33
+ "ai",
34
+ "ollama",
35
+ "cohere-ai",
36
+ "@cohere-ai/sdk",
37
+ "replicate",
38
+ "mistralai",
39
+ "@mistralai/mistralai",
40
+ "groq-sdk",
41
+ "@groq/groq-sdk",
42
+ ]);
43
+ export function registerCheckCommands(program) {
44
+ program
45
+ .command("check")
46
+ .description("inspect this repo's architecture and post findings to your ZIVIS threat model")
47
+ .option("--cwd <path>", "repo root to inspect (default: cwd)")
48
+ .option("--format <fmt>", "output format: human or json", "human")
49
+ .option("--no-network", "skip server-side promotion; build the graph + run matcher locally only")
50
+ .action(async (opts) => {
51
+ const result = await runCheck(opts);
52
+ process.exit(result.exitCode);
53
+ });
54
+ }
55
+ async function runCheck(opts) {
56
+ const rootDir = opts.cwd ?? process.cwd();
57
+ const format = opts.format === "json" ? "json" : "human";
58
+ const network = opts.network !== false;
59
+ const config = { ...DEFAULT_CONFIG, ...resolveConfigFromBinding(rootDir) };
60
+ const authStatus = await resolveAuth(config);
61
+ if (authStatus.method === "none") {
62
+ const handled = await handleUnauthenticated({ format, config });
63
+ if (handled !== "continue") {
64
+ return { exitCode: handled };
65
+ }
66
+ const recheck = await resolveAuth(config);
67
+ if (recheck.method === "none") {
68
+ printIfHuman(format, "\nAuth did not complete. Run `zivis auth login` and try again.");
69
+ return { exitCode: 1 };
70
+ }
71
+ }
72
+ const creds = await resolveAuth(config);
73
+ const startedAt = Date.now();
74
+ const extractorOutput = await runExtractor({
75
+ rootDir,
76
+ extractorVersion: "check",
77
+ });
78
+ const gitMeta = await readGitMetadata(rootDir);
79
+ const binding = detectProjectBinding(rootDir);
80
+ const applicationId = binding?.binding.applicationId;
81
+ const repoId = deriveRepoId(gitMeta.remoteUrl, rootDir);
82
+ const artifact = buildRepoGraphArtifact(extractorOutput, {
83
+ applicationId,
84
+ repoId,
85
+ commitSha: gitMeta.commitSha,
86
+ branch: gitMeta.branch,
87
+ analysisMode: "local",
88
+ });
89
+ const artifactPath = await writeArtifactToCache(artifact);
90
+ let matcherFindings = [];
91
+ try {
92
+ const matcherResult = await runMatcher({ artifactPath, cwd: rootDir });
93
+ matcherFindings = matcherResult.findings;
94
+ }
95
+ catch (err) {
96
+ const msg = err instanceof Error ? err.message : String(err);
97
+ process.stderr.write(`\n[zivis check] matcher failed (continuing without pattern matches): ${msg}\n`);
98
+ }
99
+ const durationMs = Date.now() - startedAt;
100
+ const summary = summarizeArtifact(artifact, matcherFindings);
101
+ let promotion;
102
+ let promotionError;
103
+ if (network) {
104
+ try {
105
+ promotion = await postCheckArtifact({
106
+ config,
107
+ creds,
108
+ artifact,
109
+ findings: matcherFindings,
110
+ repoName: deriveRepoNameFromGit(gitMeta.remoteUrl, rootDir),
111
+ });
112
+ }
113
+ catch (err) {
114
+ promotionError = err instanceof Error ? err.message : String(err);
115
+ }
116
+ }
117
+ let linkagePrompt = null;
118
+ if (network) {
119
+ try {
120
+ linkagePrompt = await buildLinkagePrompt({
121
+ config,
122
+ applicationId: promotion?.applicationId ?? applicationId,
123
+ });
124
+ }
125
+ catch {
126
+ }
127
+ }
128
+ if (format === "json") {
129
+ process.stdout.write(JSON.stringify(buildJsonOutput(artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt), null, 2) + "\n");
130
+ }
131
+ else {
132
+ renderHumanOutput(artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt);
133
+ }
134
+ return { exitCode: 0 };
135
+ }
136
+ async function handleUnauthenticated(args) {
137
+ const isInteractive = process.stdout.isTTY && process.stdin.isTTY && process.env.CI !== "true";
138
+ if (!isInteractive) {
139
+ printNonTtyAuthInstructions(args.format);
140
+ return 1;
141
+ }
142
+ if (args.format === "json") {
143
+ process.stdout.write(JSON.stringify({
144
+ schema_version: "1",
145
+ error: "not_authenticated",
146
+ message: "zivis check requires authentication; run `zivis auth login` interactively.",
147
+ }, null, 2) + "\n");
148
+ return 1;
149
+ }
150
+ const choice = await promptAuthChoice();
151
+ if (choice === "quit") {
152
+ process.stdout.write("\nCancelled — run `zivis check` again when ready.\n");
153
+ return 1;
154
+ }
155
+ process.stdout.write(`\nOpening your browser to ${choice === "create" ? "sign up" : "sign in"}...\n`);
156
+ try {
157
+ await loginCommand({});
158
+ }
159
+ catch (err) {
160
+ process.stdout.write(`\nAuthentication failed: ${err instanceof Error ? err.message : String(err)}\n`);
161
+ return 1;
162
+ }
163
+ process.stdout.write("\nRunning zivis check ...\n\n");
164
+ return "continue";
165
+ }
166
+ function printNonTtyAuthInstructions(format) {
167
+ if (format === "json") {
168
+ process.stdout.write(JSON.stringify({
169
+ schema_version: "1",
170
+ error: "not_authenticated",
171
+ message: "zivis check requires authentication; set ZIVIS_API_KEY for CI or run interactively.",
172
+ help: {
173
+ sign_up: "https://app.zivis.ai/sign-up?source=cli",
174
+ sign_in: "https://app.zivis.ai/sign-in?source=cli",
175
+ api_keys: "https://app.zivis.ai/settings/api-keys",
176
+ },
177
+ }, null, 2) + "\n");
178
+ return;
179
+ }
180
+ process.stdout.write(`\nZIVIS requires an account to inspect repos.\n\n` +
181
+ ` → Sign up: https://app.zivis.ai/sign-up?source=cli\n` +
182
+ ` → Sign in: https://app.zivis.ai/sign-in?source=cli\n` +
183
+ ` → For CI / headless use: set ZIVIS_API_KEY (issue a service key from\n` +
184
+ ` https://app.zivis.ai/settings/api-keys)\n\n`);
185
+ }
186
+ async function promptAuthChoice() {
187
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
188
+ try {
189
+ process.stdout.write(`\nZIVIS requires an account to inspect repos.\n\n` +
190
+ ` 1) Create account (recommended — first time using ZIVIS)\n` +
191
+ ` 2) Sign in (already have an account)\n` +
192
+ ` q) Quit\n\n`);
193
+ for (;;) {
194
+ const raw = await new Promise((resolve) => rl.question("> ", resolve));
195
+ const choice = raw.trim().toLowerCase();
196
+ if (choice === "" || choice === "1")
197
+ return "create";
198
+ if (choice === "2")
199
+ return "signin";
200
+ if (choice === "q" || choice === "quit")
201
+ return "quit";
202
+ process.stdout.write("Please choose 1, 2, or q.\n");
203
+ }
204
+ }
205
+ finally {
206
+ rl.close();
207
+ }
208
+ }
209
+ async function postCheckArtifact(args) {
210
+ const { config, creds, artifact, findings, repoName } = args;
211
+ const url = `${config.apiBaseUrl}/api/cli/check`;
212
+ const headers = {
213
+ "Content-Type": "application/json",
214
+ "User-Agent": "@zivis/cli",
215
+ };
216
+ if (creds.method === "oauth" && creds.accessToken) {
217
+ headers.Authorization = `Bearer ${creds.accessToken}`;
218
+ }
219
+ else if (creds.method === "apikey" && creds.apiKey) {
220
+ headers["X-API-Key"] = creds.apiKey;
221
+ }
222
+ const body = {
223
+ artifact,
224
+ matches: findings.map((f) => ({
225
+ pattern_id: f.pattern_id,
226
+ lane: f.lane,
227
+ confidence: f.confidence,
228
+ evidence_spans: (f.evidence ?? []).map((e) => ({
229
+ file: e.file,
230
+ line_span: e.end_line ? [e.start_line, e.end_line] : undefined,
231
+ })),
232
+ capsule: {
233
+ safe_summary: f.safe_summary,
234
+ threats: Array.isArray(f.threats) ? f.threats : undefined,
235
+ },
236
+ })),
237
+ repoName,
238
+ };
239
+ const response = await fetch(url, {
240
+ method: "POST",
241
+ headers,
242
+ body: JSON.stringify(body),
243
+ });
244
+ if (!response.ok) {
245
+ const text = await response.text().catch(() => "");
246
+ throw new Error(`HTTP ${response.status} from /api/cli/check: ${text.slice(0, 400)}`);
247
+ }
248
+ return (await response.json());
249
+ }
250
+ function summarizeArtifact(artifact, findings) {
251
+ const services = artifact.nodes.filter((n) => n.type === "service").length;
252
+ const endpoints = artifact.nodes.filter((n) => n.type === "endpoint").length;
253
+ const deps = artifact.nodes.filter((n) => n.type === "external_dependency");
254
+ const llmIntegrations = new Set();
255
+ for (const dep of deps) {
256
+ if (dep.type !== "external_dependency")
257
+ continue;
258
+ const pkg = dep.attributes.package;
259
+ if (!pkg)
260
+ continue;
261
+ if (LLM_PACKAGE_NAMES.has(pkg)) {
262
+ llmIntegrations.add(friendlyLlmName(pkg));
263
+ }
264
+ }
265
+ const trustedMatches = findings.filter((f) => f.lane === "trusted").length;
266
+ const inferredMatches = findings.filter((f) => f.lane === "inferred").length;
267
+ const topPatterns = findings
268
+ .slice()
269
+ .sort((a, b) => b.confidence - a.confidence)
270
+ .slice(0, 3)
271
+ .map((f) => f.pattern_id);
272
+ return {
273
+ serviceCount: services,
274
+ endpointCount: endpoints,
275
+ externalDependencyCount: deps.length,
276
+ llmIntegrations: Array.from(llmIntegrations).sort(),
277
+ languages: artifact.scope.languages ?? [],
278
+ filesScanned: artifact.scope.files_scanned ?? 0,
279
+ trustedMatches,
280
+ inferredMatches,
281
+ topPatterns,
282
+ };
283
+ }
284
+ function friendlyLlmName(pkg) {
285
+ if (pkg === "openai" || pkg === "@azure/openai")
286
+ return "openai";
287
+ if (pkg.startsWith("@anthropic-ai") || pkg === "anthropic")
288
+ return "anthropic";
289
+ if (pkg.startsWith("@google") || pkg.includes("vertexai") || pkg.includes("generative-ai")) {
290
+ return "google";
291
+ }
292
+ if (pkg.includes("bedrock"))
293
+ return "aws-bedrock";
294
+ if (pkg.startsWith("@langchain") || pkg === "langchain")
295
+ return "langchain";
296
+ if (pkg === "llamaindex")
297
+ return "llamaindex";
298
+ if (pkg === "ollama")
299
+ return "ollama";
300
+ if (pkg.includes("cohere"))
301
+ return "cohere";
302
+ if (pkg.includes("mistral"))
303
+ return "mistral";
304
+ if (pkg.includes("groq"))
305
+ return "groq";
306
+ if (pkg === "ai")
307
+ return "vercel-ai";
308
+ return pkg;
309
+ }
310
+ function renderHumanOutput(_artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt) {
311
+ const seconds = (durationMs / 1000).toFixed(1);
312
+ let out = `\nZIVIS read your repo (${seconds}s):\n\n`;
313
+ out += ` Architecture (current snapshot)\n`;
314
+ out += ` ${summary.serviceCount} service${plural(summary.serviceCount)} · ${summary.endpointCount} endpoint${plural(summary.endpointCount)}\n`;
315
+ if (summary.llmIntegrations.length > 0) {
316
+ out += ` ${summary.llmIntegrations.length} LLM integration${plural(summary.llmIntegrations.length)} (${summary.llmIntegrations.join(", ")})\n`;
317
+ }
318
+ if (summary.languages.length > 0) {
319
+ out += ` Languages: ${summary.languages.join(", ")}\n`;
320
+ }
321
+ const totalMatches = summary.trustedMatches + summary.inferredMatches;
322
+ if (totalMatches > 0) {
323
+ out += `\n Patterns matched\n`;
324
+ out += ` ${summary.trustedMatches} trusted · ${summary.inferredMatches} inferred (need IDE review)\n`;
325
+ for (const p of summary.topPatterns) {
326
+ out += ` • ${p}\n`;
327
+ }
328
+ }
329
+ if (promotion) {
330
+ out += `\n ${promotion.isNewApplication ? "Created" : "Updated"} threat model — "${promotion.applicationName}"\n`;
331
+ out += ` Components: +${promotion.componentsAdded}`;
332
+ if (promotion.componentsUnchanged > 0)
333
+ out += ` (${promotion.componentsUnchanged} already present)`;
334
+ out += `\n`;
335
+ out += ` Data flows: +${promotion.dataFlowsAdded}`;
336
+ if (promotion.dataFlowsUnchanged > 0)
337
+ out += ` (${promotion.dataFlowsUnchanged} already present)`;
338
+ out += `\n`;
339
+ out += ` Actors: +${promotion.actorsAttached}\n`;
340
+ out += ` Observations: +${promotion.findingsCreated}\n`;
341
+ out += ` Scenarios: +${promotion.scenariosGenerated}\n`;
342
+ out += `\n View it: ${promotion.webAppUrl}\n`;
343
+ }
344
+ else if (network && promotionError) {
345
+ out += `\n Could not post to your threat model: ${promotionError}\n`;
346
+ out += ` (graph + matches still produced locally; rerun once the API is reachable)\n`;
347
+ }
348
+ else if (!network) {
349
+ out += `\n (--no-network: nothing posted to your threat model)\n`;
350
+ }
351
+ out += `\nContinue:\n`;
352
+ out += ` → In your IDE: zivis mcp install --for claude-code\n`;
353
+ out += ` → Human-reviewed Mini-Threat-Model: zivis teardown ($500)\n`;
354
+ out += ` → Full Deal-Readiness Threat Model: zivis sales ($7.5K–$15K)\n`;
355
+ if (linkagePrompt) {
356
+ out += linkagePrompt + "\n";
357
+ }
358
+ out += "\n";
359
+ process.stdout.write(out);
360
+ }
361
+ function plural(n) {
362
+ return n === 1 ? "" : "s";
363
+ }
364
+ function buildJsonOutput(artifact, summary, durationMs, promotion, promotionError, network, linkagePrompt) {
365
+ let promotionField;
366
+ if (promotion)
367
+ promotionField = { status: "ok", ...promotion };
368
+ else if (!network)
369
+ promotionField = { status: "skipped", reason: "no-network" };
370
+ else if (promotionError)
371
+ promotionField = { status: "error", error: promotionError };
372
+ return {
373
+ schema_version: "1",
374
+ repo_id: artifact.provenance.repo_id ?? "unknown",
375
+ ran_at: artifact.extracted_at,
376
+ duration_ms: durationMs,
377
+ architecture: {
378
+ services: summary.serviceCount,
379
+ endpoints: summary.endpointCount,
380
+ external_dependencies: summary.externalDependencyCount,
381
+ llm_integrations: summary.llmIntegrations,
382
+ languages: summary.languages,
383
+ },
384
+ matches: {
385
+ trusted: summary.trustedMatches,
386
+ inferred: summary.inferredMatches,
387
+ top_patterns: summary.topPatterns,
388
+ },
389
+ promotion: promotionField,
390
+ conversion_links: {
391
+ mcp_install: "zivis mcp install --for <ide>",
392
+ teardown: "zivis teardown",
393
+ sales: "zivis sales",
394
+ },
395
+ linkage_prompt: linkagePrompt,
396
+ };
397
+ }
398
+ function printIfHuman(format, msg) {
399
+ if (format === "human")
400
+ process.stdout.write(msg + "\n");
401
+ }
402
+ async function readGitMetadata(rootDir) {
403
+ return {
404
+ remoteUrl: await tryGit(rootDir, "remote get-url origin"),
405
+ commitSha: await tryGit(rootDir, "rev-parse HEAD"),
406
+ branch: await tryGit(rootDir, "rev-parse --abbrev-ref HEAD"),
407
+ };
408
+ }
409
+ async function tryGit(cwd, args) {
410
+ try {
411
+ const { stdout } = await execAsync(`git ${args}`, { cwd });
412
+ const value = stdout.trim();
413
+ return value || undefined;
414
+ }
415
+ catch {
416
+ return undefined;
417
+ }
418
+ }
419
+ function deriveRepoId(remoteUrl, rootDir) {
420
+ if (remoteUrl) {
421
+ return "remote-" + sha256Short(remoteUrl);
422
+ }
423
+ return "path-" + sha256Short(rootDir);
424
+ }
425
+ function sha256Short(value) {
426
+ return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
427
+ }
428
+ function deriveRepoNameFromGit(remoteUrl, rootDir) {
429
+ if (remoteUrl) {
430
+ const m = /[/:]([^/:]+?)\/([^/]+?)(?:\.git)?$/.exec(remoteUrl);
431
+ if (m)
432
+ return m[2];
433
+ }
434
+ const base = path.basename(rootDir);
435
+ return base && base !== "." ? base : undefined;
436
+ }
437
+ function userCacheRoot() {
438
+ const home = os.homedir();
439
+ if (process.platform === "darwin") {
440
+ return path.join(home, "Library", "Caches", "zivis");
441
+ }
442
+ if (process.platform === "win32") {
443
+ const localAppData = process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local");
444
+ return path.join(localAppData, "zivis", "Cache");
445
+ }
446
+ const xdg = process.env.XDG_CACHE_HOME;
447
+ return path.join(xdg ?? path.join(home, ".cache"), "zivis");
448
+ }
449
+ async function writeArtifactToCache(artifact) {
450
+ const repoId = artifact.provenance.repo_id ?? "unknown";
451
+ const commitSha = artifact.provenance.commit_sha;
452
+ const runId = formatRunId(commitSha, artifact.extracted_at);
453
+ const outPath = path.join(userCacheRoot(), "repos", repoId, "inspect", "runs", `${runId}.json`);
454
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
455
+ await fs.writeFile(outPath, JSON.stringify(artifact, null, 2) + "\n");
456
+ return outPath;
457
+ }
458
+ function formatRunId(commitSha, extractedAt) {
459
+ const compactTs = extractedAt.replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
460
+ if (!commitSha)
461
+ return `nogit-${compactTs}`;
462
+ return `${commitSha.slice(0, 7)}-${compactTs}`;
463
+ }
@@ -1,7 +1,7 @@
1
1
  import { ApiClient } from "@zivis/mcp/api-client";
2
- import { DEFAULT_CONFIG } from "@zivis/mcp/types";
2
+ import { resolveConfigFromBinding } from "@zivis/mcp/project-binding";
3
3
  function buildClient(opts) {
4
- const config = opts.session ? { ...DEFAULT_CONFIG, session: opts.session } : DEFAULT_CONFIG;
4
+ const config = opts.session ? { ...resolveConfigFromBinding(process.cwd()), session: opts.session } : resolveConfigFromBinding(process.cwd());
5
5
  return new ApiClient(config);
6
6
  }
7
7
  export function registerCreditsCommands(program) {
@@ -48,4 +48,3 @@ export function registerCreditsCommands(program) {
48
48
  }
49
49
  });
50
50
  }
51
- //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerGateCommand(program: Command): void;