@zivis/cli 0.1.0-alpha.39 → 0.1.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/app/index.d.ts +8 -0
- package/dist/commands/app/index.js +14 -2
- package/dist/commands/app/index.js.map +1 -0
- package/dist/commands/auth/index.js +2 -19
- package/dist/commands/auth/index.js.map +1 -0
- package/dist/commands/auth/init.d.ts +0 -1
- package/dist/commands/auth/init.js +73 -245
- package/dist/commands/auth/init.js.map +1 -0
- package/dist/commands/auth/login.d.ts +0 -1
- package/dist/commands/auth/login.js +9 -34
- package/dist/commands/auth/login.js.map +1 -0
- package/dist/commands/auth/logout.js +7 -19
- package/dist/commands/auth/logout.js.map +1 -0
- package/dist/commands/auth/sessions.js +4 -7
- package/dist/commands/auth/sessions.js.map +1 -0
- package/dist/commands/auth/status.js +12 -33
- package/dist/commands/auth/status.js.map +1 -0
- package/dist/commands/credits/index.js +3 -2
- package/dist/commands/credits/index.js.map +1 -0
- package/dist/commands/mcp/index.js +14 -81
- package/dist/commands/mcp/index.js.map +1 -0
- package/dist/commands/open/index.d.ts +8 -0
- package/dist/commands/open/index.js +11 -0
- package/dist/commands/open/index.js.map +1 -0
- package/dist/commands/scan/index.js +8 -2
- package/dist/commands/scan/index.js.map +1 -0
- package/dist/commands/target/index.js +3 -0
- package/dist/commands/target/index.js.map +1 -0
- package/dist/commands/tm/index.js +10 -3
- package/dist/commands/tm/index.js.map +1 -0
- package/dist/index.js +1 -13
- package/dist/index.js.map +1 -0
- package/dist/internal/extractor/adapters/go/index.d.ts +4 -0
- package/dist/internal/extractor/adapters/go/index.js +6 -0
- package/dist/internal/extractor/adapters/go/index.js.map +1 -0
- package/dist/internal/extractor/adapters/go/manifest.d.ts +17 -0
- package/dist/internal/extractor/adapters/go/manifest.js +25 -19
- package/dist/internal/extractor/adapters/go/manifest.js.map +1 -0
- package/dist/internal/extractor/adapters/go/parser.d.ts +4 -0
- package/dist/internal/extractor/adapters/go/parser.js +5 -0
- package/dist/internal/extractor/adapters/go/parser.js.map +1 -0
- package/dist/internal/extractor/adapters/go/scanner.d.ts +20 -0
- package/dist/internal/extractor/adapters/go/scanner.js +43 -0
- package/dist/internal/extractor/adapters/go/scanner.js.map +1 -0
- package/dist/internal/extractor/adapters/python/index.d.ts +3 -0
- package/dist/internal/extractor/adapters/python/index.js +4 -0
- package/dist/internal/extractor/adapters/python/index.js.map +1 -0
- package/dist/internal/extractor/adapters/python/manifest.d.ts +8 -0
- package/dist/internal/extractor/adapters/python/manifest.js +18 -21
- package/dist/internal/extractor/adapters/python/manifest.js.map +1 -0
- package/dist/internal/extractor/adapters/python/parser.d.ts +3 -0
- package/dist/internal/extractor/adapters/python/parser.js +4 -0
- package/dist/internal/extractor/adapters/python/parser.js.map +1 -0
- package/dist/internal/extractor/adapters/python/scanner.d.ts +18 -0
- package/dist/internal/extractor/adapters/python/scanner.js +54 -1
- package/dist/internal/extractor/adapters/python/scanner.js.map +1 -0
- package/dist/internal/extractor/adapters/typescript/index.d.ts +7 -0
- package/dist/internal/extractor/adapters/typescript/index.js +21 -1
- package/dist/internal/extractor/adapters/typescript/index.js.map +1 -0
- package/dist/internal/extractor/adapters/typescript/manifest.d.ts +14 -0
- package/dist/internal/extractor/adapters/typescript/manifest.js +17 -26
- package/dist/internal/extractor/adapters/typescript/manifest.js.map +1 -0
- package/dist/internal/extractor/adapters/typescript/parser.d.ts +8 -0
- package/dist/internal/extractor/adapters/typescript/parser.js +9 -0
- package/dist/internal/extractor/adapters/typescript/parser.js.map +1 -0
- package/dist/internal/extractor/adapters/typescript/scanner.d.ts +24 -0
- package/dist/internal/extractor/adapters/typescript/scanner.js +43 -0
- package/dist/internal/extractor/adapters/typescript/scanner.js.map +1 -0
- package/dist/internal/extractor/index.d.ts +14 -0
- package/dist/internal/extractor/index.js +13 -0
- package/dist/internal/extractor/index.js.map +1 -0
- package/dist/internal/extractor/types.d.ts +45 -0
- package/dist/internal/extractor/types.js +27 -0
- package/dist/internal/extractor/types.js.map +1 -0
- package/dist/internal/extractor/walker.d.ts +18 -0
- package/dist/internal/extractor/walker.js +18 -0
- package/dist/internal/extractor/walker.js.map +1 -0
- package/dist/internal/ide-setup.d.ts +7 -16
- package/dist/internal/ide-setup.js +46 -320
- package/dist/internal/ide-setup.js.map +1 -0
- package/dist/internal/orgs.js +1 -0
- package/dist/internal/orgs.js.map +1 -0
- package/dist/internal/project-binding.d.ts +0 -1
- package/dist/internal/project-binding.js +9 -14
- package/dist/internal/project-binding.js.map +1 -0
- package/dist/internal/target-auth/api-config.d.ts +17 -0
- package/dist/internal/target-auth/api-config.js +18 -0
- package/dist/internal/target-auth/api-config.js.map +1 -0
- package/dist/internal/target-auth/config.d.ts +12 -0
- package/dist/internal/target-auth/config.js +13 -0
- package/dist/internal/target-auth/config.js.map +1 -0
- package/dist/internal/target-auth/index.d.ts +18 -0
- package/dist/internal/target-auth/index.js +19 -0
- package/dist/internal/target-auth/index.js.map +1 -0
- package/dist/internal/target-auth/keychain.d.ts +18 -0
- package/dist/internal/target-auth/keychain.js +23 -3
- package/dist/internal/target-auth/keychain.js.map +1 -0
- package/dist/internal/target-auth/types.d.ts +26 -0
- package/dist/internal/target-auth/types.js +10 -0
- package/dist/internal/target-auth/types.js.map +1 -0
- package/dist/internal/target-auth/workos.d.ts +15 -0
- package/dist/internal/target-auth/workos.js +17 -0
- package/dist/internal/target-auth/workos.js.map +1 -0
- package/package.json +4 -18
- package/LICENSE +0 -201
- package/README.md +0 -31
- package/dist/commands/auth/bind.d.ts +0 -4
- package/dist/commands/auth/bind.js +0 -97
- package/dist/commands/auth/token.d.ts +0 -4
- package/dist/commands/auth/token.js +0 -73
- package/dist/commands/check/index.d.ts +0 -2
- package/dist/commands/check/index.js +0 -463
- package/dist/commands/inspect/index.d.ts +0 -2
- package/dist/commands/inspect/index.js +0 -136
- package/dist/internal/binding-token-mint.d.ts +0 -33
- package/dist/internal/binding-token-mint.js +0 -174
- package/dist/internal/extractor/graph-builder.d.ts +0 -10
- package/dist/internal/extractor/graph-builder.js +0 -245
- package/dist/internal/extractor/graph-types.d.ts +0 -89
- package/dist/internal/extractor/graph-types.js +0 -1
- package/dist/internal/github-linkage-prompt.d.ts +0 -7
- package/dist/internal/github-linkage-prompt.js +0 -95
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import * as crypto from "node:crypto";
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import { exec } from "node:child_process";
|
|
6
|
-
import { promisify } from "node:util";
|
|
7
|
-
import { detectProjectBinding } from "@zivis/mcp/project-binding";
|
|
8
|
-
import { runExtractor } from "../../internal/extractor/index.js";
|
|
9
|
-
import { buildRepoGraphArtifact } from "../../internal/extractor/graph-builder.js";
|
|
10
|
-
const execAsync = promisify(exec);
|
|
11
|
-
function collect(value, previous) {
|
|
12
|
-
return previous.concat([value]);
|
|
13
|
-
}
|
|
14
|
-
export function registerInspectCommands(program) {
|
|
15
|
-
program
|
|
16
|
-
.command("inspect")
|
|
17
|
-
.description("build a local repo graph artifact (services, endpoints, external deps) — local-only, no network")
|
|
18
|
-
.option("--root <dir>", "repo root to inspect (default: cwd)")
|
|
19
|
-
.option("--out <file>", "write artifact JSON to a file (default: <user-cache>/repos/<repo-id>/inspect/runs/<run-id>.json; '-' for stdout)")
|
|
20
|
-
.option("--privacy-audit", "print exactly what would be sent without writing to cache or sending — never makes network calls")
|
|
21
|
-
.option("--lang <list>", "comma-separated subset of adapters: typescript,go,python")
|
|
22
|
-
.option("--ignore <pattern>", "extra ignore pattern (repeatable)", collect, [])
|
|
23
|
-
.option("--application <id>", "set provenance.application_id (default: from .zivis/project.json if present)")
|
|
24
|
-
.option("--repo-id <id>", "override repo identifier (default: hash of git remote, fallback to hash of root path)")
|
|
25
|
-
.option("--commit <sha>", "override commit SHA (default: from `git rev-parse HEAD`)")
|
|
26
|
-
.option("--branch <name>", "override branch (default: from `git rev-parse --abbrev-ref HEAD`)")
|
|
27
|
-
.action(async (opts) => {
|
|
28
|
-
const rootDir = path.resolve(opts.root ?? process.cwd());
|
|
29
|
-
const cliPkg = await readCliPackage();
|
|
30
|
-
const langs = opts.lang
|
|
31
|
-
? String(opts.lang).split(",").map((s) => s.trim()).filter(Boolean)
|
|
32
|
-
: undefined;
|
|
33
|
-
const result = await runExtractor({
|
|
34
|
-
rootDir,
|
|
35
|
-
languages: langs,
|
|
36
|
-
extraIgnores: opts.ignore,
|
|
37
|
-
extractorVersion: cliPkg.version,
|
|
38
|
-
});
|
|
39
|
-
const gitMeta = await readGitMetadata(rootDir);
|
|
40
|
-
const applicationId = opts.application ?? detectProjectBinding(rootDir)?.binding.applicationId;
|
|
41
|
-
const repoId = opts.repoId ?? deriveRepoId(gitMeta.remoteUrl, rootDir);
|
|
42
|
-
const commitSha = opts.commit ?? gitMeta.commitSha;
|
|
43
|
-
const branch = opts.branch ?? gitMeta.branch;
|
|
44
|
-
const artifact = buildRepoGraphArtifact(result, {
|
|
45
|
-
applicationId,
|
|
46
|
-
repoId,
|
|
47
|
-
commitSha,
|
|
48
|
-
branch,
|
|
49
|
-
analysisMode: "local",
|
|
50
|
-
});
|
|
51
|
-
if (opts.privacyAudit) {
|
|
52
|
-
process.stdout.write(JSON.stringify(artifact, null, 2) + "\n");
|
|
53
|
-
printSummary(artifact, "PRIVACY AUDIT — nothing was written or sent");
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (opts.out === "-") {
|
|
57
|
-
process.stdout.write(JSON.stringify(artifact, null, 2) + "\n");
|
|
58
|
-
printSummary(artifact);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
const outPath = opts.out
|
|
62
|
-
? path.isAbsolute(opts.out)
|
|
63
|
-
? opts.out
|
|
64
|
-
: path.join(rootDir, opts.out)
|
|
65
|
-
: defaultCacheOutPath(repoId, commitSha, artifact.extracted_at);
|
|
66
|
-
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
67
|
-
await fs.writeFile(outPath, JSON.stringify(artifact, null, 2) + "\n");
|
|
68
|
-
printSummary(artifact, `wrote ${outPath}`);
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
async function readCliPackage() {
|
|
72
|
-
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
73
|
-
const pkgPath = path.join(here, "../../../package.json");
|
|
74
|
-
const raw = await fs.readFile(pkgPath, "utf8");
|
|
75
|
-
return JSON.parse(raw);
|
|
76
|
-
}
|
|
77
|
-
async function readGitMetadata(rootDir) {
|
|
78
|
-
const out = {};
|
|
79
|
-
out.remoteUrl = await tryGit(rootDir, "remote get-url origin");
|
|
80
|
-
out.commitSha = await tryGit(rootDir, "rev-parse HEAD");
|
|
81
|
-
out.branch = await tryGit(rootDir, "rev-parse --abbrev-ref HEAD");
|
|
82
|
-
return out;
|
|
83
|
-
}
|
|
84
|
-
async function tryGit(cwd, args) {
|
|
85
|
-
try {
|
|
86
|
-
const { stdout } = await execAsync(`git ${args}`, { cwd });
|
|
87
|
-
const value = stdout.trim();
|
|
88
|
-
return value || undefined;
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function deriveRepoId(remoteUrl, rootDir) {
|
|
95
|
-
if (remoteUrl) {
|
|
96
|
-
return "remote-" + sha256Short(remoteUrl);
|
|
97
|
-
}
|
|
98
|
-
return "path-" + sha256Short(rootDir);
|
|
99
|
-
}
|
|
100
|
-
function sha256Short(value) {
|
|
101
|
-
return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
102
|
-
}
|
|
103
|
-
function userCacheRoot() {
|
|
104
|
-
const home = os.homedir();
|
|
105
|
-
if (process.platform === "darwin") {
|
|
106
|
-
return path.join(home, "Library", "Caches", "zivis");
|
|
107
|
-
}
|
|
108
|
-
if (process.platform === "win32") {
|
|
109
|
-
const localAppData = process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local");
|
|
110
|
-
return path.join(localAppData, "zivis", "Cache");
|
|
111
|
-
}
|
|
112
|
-
const xdg = process.env.XDG_CACHE_HOME;
|
|
113
|
-
return path.join(xdg ?? path.join(home, ".cache"), "zivis");
|
|
114
|
-
}
|
|
115
|
-
function defaultCacheOutPath(repoId, commitSha, extractedAt) {
|
|
116
|
-
const runId = formatRunId(commitSha, extractedAt);
|
|
117
|
-
return path.join(userCacheRoot(), "repos", repoId, "inspect", "runs", `${runId}.json`);
|
|
118
|
-
}
|
|
119
|
-
function formatRunId(commitSha, extractedAt) {
|
|
120
|
-
const compactTs = extractedAt.replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
121
|
-
if (!commitSha)
|
|
122
|
-
return `nogit-${compactTs}`;
|
|
123
|
-
return `${commitSha.slice(0, 7)}-${compactTs}`;
|
|
124
|
-
}
|
|
125
|
-
function printSummary(artifact, suffix) {
|
|
126
|
-
const services = artifact.nodes.filter((n) => n.type === "service").length;
|
|
127
|
-
const endpoints = artifact.nodes.filter((n) => n.type === "endpoint").length;
|
|
128
|
-
const deps = artifact.nodes.filter((n) => n.type === "external_dependency").length;
|
|
129
|
-
const exposes = artifact.edges.filter((e) => e.type === "exposes").length;
|
|
130
|
-
const dependsOn = artifact.edges.filter((e) => e.type === "depends_on").length;
|
|
131
|
-
const langs = artifact.scope.languages?.join(",") ?? "(none)";
|
|
132
|
-
process.stderr.write(`\n[zivis inspect] ${artifact.scope.files_scanned} files scanned in ${artifact.scope.duration_ms}ms (langs: ${langs})\n` +
|
|
133
|
-
` ${artifact.nodes.length} nodes (${services} services, ${endpoints} endpoints, ${deps} external deps)\n` +
|
|
134
|
-
` ${artifact.edges.length} edges (${exposes} exposes, ${dependsOn} depends_on)\n` +
|
|
135
|
-
(suffix ? ` ${suffix}\n` : ""));
|
|
136
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { type ZivisConfig } from "@zivis/mcp/types";
|
|
2
|
-
export type MintBindingTokenResult = {
|
|
3
|
-
ok: true;
|
|
4
|
-
keyName: string;
|
|
5
|
-
key: string;
|
|
6
|
-
orgName: string;
|
|
7
|
-
workosOrgId: string;
|
|
8
|
-
} | {
|
|
9
|
-
ok: false;
|
|
10
|
-
error: string;
|
|
11
|
-
};
|
|
12
|
-
export declare function mintLocalProjectBindingToken(params: {
|
|
13
|
-
config: ZivisConfig;
|
|
14
|
-
session: string;
|
|
15
|
-
accessToken: string;
|
|
16
|
-
cwd: string;
|
|
17
|
-
}): Promise<MintBindingTokenResult>;
|
|
18
|
-
export interface TryAutoMintOptions {
|
|
19
|
-
cwd: string;
|
|
20
|
-
sessionName: string;
|
|
21
|
-
accessToken: string;
|
|
22
|
-
skip?: boolean;
|
|
23
|
-
warnOnFailure: boolean;
|
|
24
|
-
}
|
|
25
|
-
export declare function tryAutoMintBindingTokenAfterLogin(opts: TryAutoMintOptions): Promise<{
|
|
26
|
-
ran: boolean;
|
|
27
|
-
ok: boolean;
|
|
28
|
-
message?: string;
|
|
29
|
-
}>;
|
|
30
|
-
export declare function discardOauthAfterLogin(params: {
|
|
31
|
-
session: string | undefined;
|
|
32
|
-
config: ZivisConfig;
|
|
33
|
-
}): Promise<void>;
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import * as os from "node:os";
|
|
2
|
-
import * as crypto from "node:crypto";
|
|
3
|
-
import * as child_process from "node:child_process";
|
|
4
|
-
import { getSessionAccount, keychainClearSession, keychainRead, keychainStore, } from "@zivis/mcp/auth";
|
|
5
|
-
import { decodeJwtPayload } from "@zivis/mcp/auth/jwt";
|
|
6
|
-
import { DEFAULT_CONFIG, credentialStorageFromConfig } from "@zivis/mcp/types";
|
|
7
|
-
import { detectProjectBinding, resolveConfigFromBinding } from "@zivis/mcp/project-binding";
|
|
8
|
-
function getRepoFingerprint(cwd) {
|
|
9
|
-
try {
|
|
10
|
-
const remote = child_process
|
|
11
|
-
.execSync("git remote get-url origin 2>/dev/null", { cwd, encoding: "utf8" })
|
|
12
|
-
.trim();
|
|
13
|
-
if (remote) {
|
|
14
|
-
return crypto.createHash("sha256").update(remote).digest("hex").slice(0, 12);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
}
|
|
19
|
-
return crypto.createHash("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
20
|
-
}
|
|
21
|
-
export async function mintLocalProjectBindingToken(params) {
|
|
22
|
-
const { config, session, accessToken, cwd } = params;
|
|
23
|
-
const credentialStorage = credentialStorageFromConfig(config);
|
|
24
|
-
const repoFingerprint = getRepoFingerprint(cwd);
|
|
25
|
-
const hostname = os.hostname();
|
|
26
|
-
let bindResult;
|
|
27
|
-
try {
|
|
28
|
-
const response = await fetch(`${config.apiBaseUrl}/api/auth/bind`, {
|
|
29
|
-
method: "POST",
|
|
30
|
-
headers: {
|
|
31
|
-
Authorization: `Bearer ${accessToken}`,
|
|
32
|
-
"Content-Type": "application/json",
|
|
33
|
-
"User-Agent": "@zivis/cli",
|
|
34
|
-
},
|
|
35
|
-
body: JSON.stringify({ repoFingerprint, hostname }),
|
|
36
|
-
});
|
|
37
|
-
if (!response.ok) {
|
|
38
|
-
const body = (await response.json().catch(() => ({})));
|
|
39
|
-
return {
|
|
40
|
-
ok: false,
|
|
41
|
-
error: body.message ?? body.error ?? `HTTP ${response.status}`,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
bindResult = (await response.json());
|
|
45
|
-
}
|
|
46
|
-
catch (err) {
|
|
47
|
-
return {
|
|
48
|
-
ok: false,
|
|
49
|
-
error: err instanceof Error ? err.message : String(err),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
const apiKeyAccount = `apikey.${session}`;
|
|
53
|
-
try {
|
|
54
|
-
await keychainStore(apiKeyAccount, bindResult.key, credentialStorage);
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
return {
|
|
58
|
-
ok: false,
|
|
59
|
-
error: err instanceof Error ? err.message : "Failed to store key in OS keychain",
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
ok: true,
|
|
64
|
-
keyName: bindResult.keyName,
|
|
65
|
-
key: bindResult.key,
|
|
66
|
-
orgName: bindResult.orgName,
|
|
67
|
-
workosOrgId: bindResult.workosOrgId,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
export async function tryAutoMintBindingTokenAfterLogin(opts) {
|
|
71
|
-
if (opts.skip) {
|
|
72
|
-
return { ran: false, ok: true };
|
|
73
|
-
}
|
|
74
|
-
const fullConfig = { ...DEFAULT_CONFIG, ...resolveConfigFromBinding(opts.cwd) };
|
|
75
|
-
const claims = decodeJwtPayload(opts.accessToken);
|
|
76
|
-
const jwtOrgId = claims?.org_id;
|
|
77
|
-
if (!jwtOrgId) {
|
|
78
|
-
return {
|
|
79
|
-
ran: false,
|
|
80
|
-
ok: true,
|
|
81
|
-
message: "JWT has no org_id — skipped MCP binding-key mint.",
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
const detected = detectProjectBinding(opts.cwd);
|
|
85
|
-
const bindingOrg = detected?.binding.workosOrgId;
|
|
86
|
-
const bindingSession = detected?.binding.session;
|
|
87
|
-
if (!bindingOrg || !bindingSession) {
|
|
88
|
-
return {
|
|
89
|
-
ran: false,
|
|
90
|
-
ok: true,
|
|
91
|
-
message: "No project binding with workosOrgId — skipped MCP binding-key mint.",
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
if (bindingOrg !== jwtOrgId) {
|
|
95
|
-
const msg = `JWT org does not match project binding — skipped MCP binding-key mint.`;
|
|
96
|
-
if (opts.warnOnFailure) {
|
|
97
|
-
console.log("");
|
|
98
|
-
console.log(` WARNING: ${msg}`);
|
|
99
|
-
}
|
|
100
|
-
return { ran: false, ok: false, message: msg };
|
|
101
|
-
}
|
|
102
|
-
if (bindingSession !== opts.sessionName) {
|
|
103
|
-
const msg = `Session "${opts.sessionName}" does not match binding session "${bindingSession}" — skipped MCP binding-key mint.`;
|
|
104
|
-
if (opts.warnOnFailure) {
|
|
105
|
-
console.log("");
|
|
106
|
-
console.log(` WARNING: ${msg}`);
|
|
107
|
-
}
|
|
108
|
-
return { ran: false, ok: false, message: msg };
|
|
109
|
-
}
|
|
110
|
-
const st = credentialStorageFromConfig(fullConfig);
|
|
111
|
-
const existing = await keychainRead(`apikey.${bindingSession}`, st).catch(() => null);
|
|
112
|
-
if (existing) {
|
|
113
|
-
console.log("");
|
|
114
|
-
console.log(`✓ MCP API key already present for session "${bindingSession}" — skipped mint.`);
|
|
115
|
-
return { ran: false, ok: true };
|
|
116
|
-
}
|
|
117
|
-
console.log("");
|
|
118
|
-
console.log("Minting Local Project Binding Token for MCP (one-time per machine/session)…");
|
|
119
|
-
const result = await mintLocalProjectBindingToken({
|
|
120
|
-
config: fullConfig,
|
|
121
|
-
session: bindingSession,
|
|
122
|
-
accessToken: opts.accessToken,
|
|
123
|
-
cwd: opts.cwd,
|
|
124
|
-
});
|
|
125
|
-
if (!result.ok) {
|
|
126
|
-
const msg = result.error;
|
|
127
|
-
if (opts.warnOnFailure) {
|
|
128
|
-
printMintFailureBanner(msg);
|
|
129
|
-
}
|
|
130
|
-
return { ran: true, ok: false, message: msg };
|
|
131
|
-
}
|
|
132
|
-
console.log(`✓ MCP API key stored (${result.keyName}; org: ${result.orgName})`);
|
|
133
|
-
return { ran: true, ok: true };
|
|
134
|
-
}
|
|
135
|
-
export async function discardOauthAfterLogin(params) {
|
|
136
|
-
const storage = credentialStorageFromConfig(params.config);
|
|
137
|
-
try {
|
|
138
|
-
await keychainClearSession(params.session, storage);
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
}
|
|
142
|
-
const leftover = await keychainRead(getSessionAccount(params.session), storage).catch(() => null);
|
|
143
|
-
if (leftover) {
|
|
144
|
-
const account = getSessionAccount(params.session);
|
|
145
|
-
const service = `${storage.keychainPrefix}.${account}`;
|
|
146
|
-
console.error("");
|
|
147
|
-
console.error(` WARNING: OAuth tokens are still present in the keychain (${service}).`);
|
|
148
|
-
console.error(" Possible cause: keychain access was denied, or older builds left");
|
|
149
|
-
console.error(" duplicate entries that the OS doesn't surface in a single delete.");
|
|
150
|
-
console.error(" Force-clean with:");
|
|
151
|
-
console.error(` while security delete-generic-password -a zivis-mcp -s '${service}' 2>/dev/null; do :; done`);
|
|
152
|
-
console.error("");
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function printMintFailureBanner(message) {
|
|
156
|
-
const bar = "═".repeat(70);
|
|
157
|
-
console.error("");
|
|
158
|
-
console.error(bar);
|
|
159
|
-
console.error(" ACTION REQUIRED — MCP API KEY WAS NOT MINTED");
|
|
160
|
-
console.error(bar);
|
|
161
|
-
console.error("");
|
|
162
|
-
console.error(` Reason: ${message}`);
|
|
163
|
-
console.error("");
|
|
164
|
-
console.error(" Why this matters:");
|
|
165
|
-
console.error(" The MCP server in your IDE authenticates with an org-scoped");
|
|
166
|
-
console.error(" API key. Without it, every zivis_* tool call will fail with");
|
|
167
|
-
console.error(" \"Not authenticated\" until you mint one.");
|
|
168
|
-
console.error("");
|
|
169
|
-
console.error(" To fix, run from this repo:");
|
|
170
|
-
console.error(" zivis auth bind");
|
|
171
|
-
console.error("");
|
|
172
|
-
console.error(bar);
|
|
173
|
-
console.error("");
|
|
174
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ExtractionResult } from "./types.js";
|
|
2
|
-
import type { RepoGraphArtifactV1, AnalysisMode } from "./graph-types.js";
|
|
3
|
-
export interface BuildOptions {
|
|
4
|
-
applicationId?: string;
|
|
5
|
-
repoId?: string;
|
|
6
|
-
commitSha?: string;
|
|
7
|
-
branch?: string;
|
|
8
|
-
analysisMode?: AnalysisMode;
|
|
9
|
-
}
|
|
10
|
-
export declare function buildRepoGraphArtifact(result: ExtractionResult, opts?: BuildOptions): RepoGraphArtifactV1;
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
const TRUSTED_LANE = "trusted";
|
|
2
|
-
const TRUSTED_SOURCE = "inspect.trusted";
|
|
3
|
-
export function buildRepoGraphArtifact(result, opts = {}) {
|
|
4
|
-
const nodes = [];
|
|
5
|
-
const edges = [];
|
|
6
|
-
const serviceById = new Map();
|
|
7
|
-
for (const component of result.components) {
|
|
8
|
-
if (component.kind === "external_service" || component.kind === "data_store") {
|
|
9
|
-
continue;
|
|
10
|
-
}
|
|
11
|
-
if (component.evidence.length === 0)
|
|
12
|
-
continue;
|
|
13
|
-
const node = buildServiceNode(component);
|
|
14
|
-
serviceById.set(node.id, node);
|
|
15
|
-
nodes.push(node);
|
|
16
|
-
}
|
|
17
|
-
for (const component of result.components) {
|
|
18
|
-
const owningServiceId = serviceIdFor(component);
|
|
19
|
-
if (!serviceById.has(owningServiceId))
|
|
20
|
-
continue;
|
|
21
|
-
const seenEndpoints = new Set();
|
|
22
|
-
for (const ev of component.evidence) {
|
|
23
|
-
if (ev.kind !== "http_route")
|
|
24
|
-
continue;
|
|
25
|
-
const method = canonicalMethod(ev.method);
|
|
26
|
-
if (!method)
|
|
27
|
-
continue;
|
|
28
|
-
const endpointNode = buildEndpointNode(owningServiceId, component.language, ev, method);
|
|
29
|
-
if (seenEndpoints.has(endpointNode.id))
|
|
30
|
-
continue;
|
|
31
|
-
seenEndpoints.add(endpointNode.id);
|
|
32
|
-
nodes.push(endpointNode);
|
|
33
|
-
edges.push(buildExposesEdge(owningServiceId, endpointNode.id, ev));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const manifestEvidenceByPackage = indexManifestsByPackage(result);
|
|
37
|
-
const depById = new Map();
|
|
38
|
-
for (const ext of result.externalServices) {
|
|
39
|
-
const node = buildExternalDependencyNode(ext, manifestEvidenceByPackage.get(ext.package) ?? []);
|
|
40
|
-
if (depById.has(node.id))
|
|
41
|
-
continue;
|
|
42
|
-
if (node.evidence.length === 0)
|
|
43
|
-
continue;
|
|
44
|
-
depById.set(node.id, node);
|
|
45
|
-
nodes.push(node);
|
|
46
|
-
}
|
|
47
|
-
const fileToServiceId = buildFileToServiceIndex(result.components, serviceById);
|
|
48
|
-
for (const ext of result.externalServices) {
|
|
49
|
-
const depId = externalDepId(ext);
|
|
50
|
-
const seenServiceIds = new Set();
|
|
51
|
-
const evidenceByService = new Map();
|
|
52
|
-
for (const callSite of ext.callSites) {
|
|
53
|
-
const serviceId = fileToServiceId.get(callSite.file);
|
|
54
|
-
if (!serviceId)
|
|
55
|
-
continue;
|
|
56
|
-
seenServiceIds.add(serviceId);
|
|
57
|
-
const list = evidenceByService.get(serviceId) ?? [];
|
|
58
|
-
list.push(toImportEvidence(callSite, ext.package));
|
|
59
|
-
evidenceByService.set(serviceId, list);
|
|
60
|
-
}
|
|
61
|
-
for (const serviceId of seenServiceIds) {
|
|
62
|
-
edges.push(buildDependsOnEdge(serviceId, depId, evidenceByService.get(serviceId) ?? []));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
const provenance = {
|
|
66
|
-
root_dir: result.scope.rootDir,
|
|
67
|
-
analysis_mode: opts.analysisMode ?? "local",
|
|
68
|
-
...(opts.repoId ? { repo_id: opts.repoId } : {}),
|
|
69
|
-
...(opts.commitSha ? { commit_sha: opts.commitSha } : {}),
|
|
70
|
-
...(opts.branch ? { branch: opts.branch } : {}),
|
|
71
|
-
...(opts.applicationId ? { application_id: opts.applicationId } : {}),
|
|
72
|
-
};
|
|
73
|
-
const languagesFromManifests = new Set(result.manifests.map((m) => normalizeLanguage(m.language)).filter(Boolean));
|
|
74
|
-
return {
|
|
75
|
-
version: "1.0.0",
|
|
76
|
-
kind: "repo_graph_artifact",
|
|
77
|
-
extracted_at: result.extractedAt,
|
|
78
|
-
extractor: result.extractor,
|
|
79
|
-
provenance,
|
|
80
|
-
scope: {
|
|
81
|
-
files_scanned: result.scope.filesScanned,
|
|
82
|
-
files_skipped: result.scope.filesSkipped,
|
|
83
|
-
duration_ms: result.scope.durationMs,
|
|
84
|
-
languages: [...languagesFromManifests],
|
|
85
|
-
},
|
|
86
|
-
nodes,
|
|
87
|
-
edges,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
function buildServiceNode(component) {
|
|
91
|
-
const id = serviceIdFor(component);
|
|
92
|
-
return {
|
|
93
|
-
id,
|
|
94
|
-
type: "service",
|
|
95
|
-
name: component.name,
|
|
96
|
-
lane: TRUSTED_LANE,
|
|
97
|
-
source: TRUSTED_SOURCE,
|
|
98
|
-
confidence: 1,
|
|
99
|
-
language: normalizeLanguage(component.language),
|
|
100
|
-
evidence: component.evidence.map(toEvidenceSpan),
|
|
101
|
-
attributes: {
|
|
102
|
-
...(component.framework ? { framework: component.framework } : {}),
|
|
103
|
-
source_files: component.sourceFiles,
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
function buildEndpointNode(serviceId, language, ev, method) {
|
|
108
|
-
const id = `endpoint::${stripPrefix(serviceId, "service::")}::${method}::${ev.path}`;
|
|
109
|
-
return {
|
|
110
|
-
id,
|
|
111
|
-
type: "endpoint",
|
|
112
|
-
name: `${method} ${ev.path}`,
|
|
113
|
-
lane: TRUSTED_LANE,
|
|
114
|
-
source: TRUSTED_SOURCE,
|
|
115
|
-
confidence: 1,
|
|
116
|
-
language: normalizeLanguage(language),
|
|
117
|
-
evidence: [toEvidenceSpan(ev)],
|
|
118
|
-
attributes: { method, path: ev.path },
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function buildExternalDependencyNode(ext, manifestFallback) {
|
|
122
|
-
const registry = registryFor(ext);
|
|
123
|
-
const evidence = ext.callSites.length > 0
|
|
124
|
-
? ext.callSites.map((cs) => toImportEvidence(cs, ext.package))
|
|
125
|
-
: manifestFallback;
|
|
126
|
-
return {
|
|
127
|
-
id: externalDepId(ext),
|
|
128
|
-
type: "external_dependency",
|
|
129
|
-
name: ext.name,
|
|
130
|
-
lane: TRUSTED_LANE,
|
|
131
|
-
source: TRUSTED_SOURCE,
|
|
132
|
-
confidence: 1,
|
|
133
|
-
evidence,
|
|
134
|
-
attributes: {
|
|
135
|
-
package: ext.package,
|
|
136
|
-
registry,
|
|
137
|
-
...(ext.version ? { version: ext.version } : {}),
|
|
138
|
-
...(ext.kind ? { kind: ext.kind } : {}),
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
function indexManifestsByPackage(result) {
|
|
143
|
-
const idx = new Map();
|
|
144
|
-
for (const m of result.manifests) {
|
|
145
|
-
for (const pkg of Object.keys(m.dependencies)) {
|
|
146
|
-
const span = { file: m.file, kind: "manifest", detail: { package: pkg } };
|
|
147
|
-
const list = idx.get(pkg) ?? [];
|
|
148
|
-
list.push(span);
|
|
149
|
-
idx.set(pkg, list);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return idx;
|
|
153
|
-
}
|
|
154
|
-
function buildExposesEdge(serviceId, endpointId, ev) {
|
|
155
|
-
return {
|
|
156
|
-
id: `exposes::${serviceId}::${endpointId}`,
|
|
157
|
-
type: "exposes",
|
|
158
|
-
source_id: serviceId,
|
|
159
|
-
target_id: endpointId,
|
|
160
|
-
lane: TRUSTED_LANE,
|
|
161
|
-
source: TRUSTED_SOURCE,
|
|
162
|
-
confidence: 1,
|
|
163
|
-
evidence: [toEvidenceSpan(ev)],
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function buildDependsOnEdge(serviceId, depId, evidence) {
|
|
167
|
-
return {
|
|
168
|
-
id: `depends_on::${serviceId}::${depId}`,
|
|
169
|
-
type: "depends_on",
|
|
170
|
-
source_id: serviceId,
|
|
171
|
-
target_id: depId,
|
|
172
|
-
lane: TRUSTED_LANE,
|
|
173
|
-
source: TRUSTED_SOURCE,
|
|
174
|
-
confidence: 1,
|
|
175
|
-
evidence,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
function serviceIdFor(component) {
|
|
179
|
-
return `service::${component.language}::${component.name}`;
|
|
180
|
-
}
|
|
181
|
-
function externalDepId(ext) {
|
|
182
|
-
const registry = registryFor(ext);
|
|
183
|
-
return `external_dependency::${registry}::${ext.package}`;
|
|
184
|
-
}
|
|
185
|
-
function registryFor(ext) {
|
|
186
|
-
if (ext.package.startsWith("github.com/"))
|
|
187
|
-
return "go";
|
|
188
|
-
if (ext.package.startsWith("@") || /^[a-z0-9-]+$/.test(ext.package)) {
|
|
189
|
-
return "npm";
|
|
190
|
-
}
|
|
191
|
-
return "npm";
|
|
192
|
-
}
|
|
193
|
-
const CANONICAL_METHODS = new Set([
|
|
194
|
-
"GET",
|
|
195
|
-
"POST",
|
|
196
|
-
"PUT",
|
|
197
|
-
"PATCH",
|
|
198
|
-
"DELETE",
|
|
199
|
-
"HEAD",
|
|
200
|
-
"OPTIONS",
|
|
201
|
-
"WS",
|
|
202
|
-
"SSE",
|
|
203
|
-
"RPC",
|
|
204
|
-
]);
|
|
205
|
-
function canonicalMethod(method) {
|
|
206
|
-
const upper = method.toUpperCase();
|
|
207
|
-
return CANONICAL_METHODS.has(upper) ? upper : undefined;
|
|
208
|
-
}
|
|
209
|
-
function normalizeLanguage(lang) {
|
|
210
|
-
return lang;
|
|
211
|
-
}
|
|
212
|
-
function buildFileToServiceIndex(components, servicesById) {
|
|
213
|
-
const idx = new Map();
|
|
214
|
-
for (const component of components) {
|
|
215
|
-
const id = serviceIdFor(component);
|
|
216
|
-
if (!servicesById.has(id))
|
|
217
|
-
continue;
|
|
218
|
-
for (const file of component.sourceFiles) {
|
|
219
|
-
if (!idx.has(file))
|
|
220
|
-
idx.set(file, id);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return idx;
|
|
224
|
-
}
|
|
225
|
-
function toEvidenceSpan(ev) {
|
|
226
|
-
const base = { file: ev.file, start_line: ev.line, kind: ev.kind };
|
|
227
|
-
if (ev.kind === "http_route") {
|
|
228
|
-
return { ...base, detail: { method: ev.method, path: ev.path } };
|
|
229
|
-
}
|
|
230
|
-
if (ev.kind === "import") {
|
|
231
|
-
return { ...base, detail: { package: ev.package } };
|
|
232
|
-
}
|
|
233
|
-
return base;
|
|
234
|
-
}
|
|
235
|
-
function toImportEvidence(loc, pkg) {
|
|
236
|
-
return {
|
|
237
|
-
file: loc.file,
|
|
238
|
-
start_line: loc.line,
|
|
239
|
-
kind: "import",
|
|
240
|
-
detail: { package: pkg },
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
function stripPrefix(value, prefix) {
|
|
244
|
-
return value.startsWith(prefix) ? value.slice(prefix.length) : value;
|
|
245
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
export type Lane = "trusted" | "inferred";
|
|
2
|
-
export type Source = "inspect.trusted" | "inspect.inferred";
|
|
3
|
-
export type GraphLanguage = "typescript" | "javascript" | "python" | "go";
|
|
4
|
-
export type AnalysisMode = "local" | "cloud";
|
|
5
|
-
export type EvidenceKind = "import" | "http_route" | "config" | "manifest" | "decorator" | "annotation";
|
|
6
|
-
export type NodeType = "service" | "endpoint" | "external_dependency";
|
|
7
|
-
export type EdgeType = "exposes" | "depends_on";
|
|
8
|
-
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "WS" | "SSE" | "RPC";
|
|
9
|
-
export type DependencyRegistry = "npm" | "pypi" | "go";
|
|
10
|
-
export interface EvidenceSpan {
|
|
11
|
-
file: string;
|
|
12
|
-
start_line?: number;
|
|
13
|
-
end_line?: number;
|
|
14
|
-
kind: EvidenceKind;
|
|
15
|
-
detail?: Record<string, unknown>;
|
|
16
|
-
}
|
|
17
|
-
export interface ServiceAttributes {
|
|
18
|
-
framework?: string;
|
|
19
|
-
source_files?: string[];
|
|
20
|
-
}
|
|
21
|
-
export interface EndpointAttributes {
|
|
22
|
-
method: HttpMethod;
|
|
23
|
-
path: string;
|
|
24
|
-
}
|
|
25
|
-
export interface ExternalDependencyAttributes {
|
|
26
|
-
package: string;
|
|
27
|
-
version?: string;
|
|
28
|
-
registry: DependencyRegistry;
|
|
29
|
-
kind?: string;
|
|
30
|
-
}
|
|
31
|
-
export type NodeAttributes = ServiceAttributes | EndpointAttributes | ExternalDependencyAttributes;
|
|
32
|
-
interface BaseNode {
|
|
33
|
-
id: string;
|
|
34
|
-
name: string;
|
|
35
|
-
lane: Lane;
|
|
36
|
-
source: Source;
|
|
37
|
-
confidence?: number;
|
|
38
|
-
language?: GraphLanguage;
|
|
39
|
-
evidence: EvidenceSpan[];
|
|
40
|
-
}
|
|
41
|
-
export interface ServiceNode extends BaseNode {
|
|
42
|
-
type: "service";
|
|
43
|
-
attributes?: ServiceAttributes;
|
|
44
|
-
}
|
|
45
|
-
export interface EndpointNode extends BaseNode {
|
|
46
|
-
type: "endpoint";
|
|
47
|
-
attributes: EndpointAttributes;
|
|
48
|
-
}
|
|
49
|
-
export interface ExternalDependencyNode extends BaseNode {
|
|
50
|
-
type: "external_dependency";
|
|
51
|
-
attributes: ExternalDependencyAttributes;
|
|
52
|
-
}
|
|
53
|
-
export type GraphNode = ServiceNode | EndpointNode | ExternalDependencyNode;
|
|
54
|
-
export interface GraphEdge {
|
|
55
|
-
id: string;
|
|
56
|
-
type: EdgeType;
|
|
57
|
-
source_id: string;
|
|
58
|
-
target_id: string;
|
|
59
|
-
lane: Lane;
|
|
60
|
-
source: Source;
|
|
61
|
-
confidence?: number;
|
|
62
|
-
evidence?: EvidenceSpan[];
|
|
63
|
-
attributes?: Record<string, unknown>;
|
|
64
|
-
}
|
|
65
|
-
export interface GraphProvenance {
|
|
66
|
-
root_dir: string;
|
|
67
|
-
repo_id?: string;
|
|
68
|
-
commit_sha?: string;
|
|
69
|
-
branch?: string;
|
|
70
|
-
application_id?: string;
|
|
71
|
-
analysis_mode: AnalysisMode;
|
|
72
|
-
}
|
|
73
|
-
export interface GraphScope {
|
|
74
|
-
files_scanned: number;
|
|
75
|
-
files_skipped: number;
|
|
76
|
-
duration_ms: number;
|
|
77
|
-
languages?: GraphLanguage[];
|
|
78
|
-
}
|
|
79
|
-
export interface RepoGraphArtifactV1 {
|
|
80
|
-
version: "1.0.0";
|
|
81
|
-
kind: "repo_graph_artifact";
|
|
82
|
-
extracted_at: string;
|
|
83
|
-
extractor: string;
|
|
84
|
-
provenance: GraphProvenance;
|
|
85
|
-
scope: GraphScope;
|
|
86
|
-
nodes: GraphNode[];
|
|
87
|
-
edges: GraphEdge[];
|
|
88
|
-
}
|
|
89
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|