@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.
- package/LICENSE +201 -0
- package/README.md +31 -0
- package/dist/commands/app/index.d.ts +0 -8
- package/dist/commands/app/index.js +4 -16
- package/dist/commands/auth/bind.d.ts +4 -0
- package/dist/commands/auth/bind.js +97 -0
- package/dist/commands/auth/index.js +20 -2
- package/dist/commands/auth/init.d.ts +1 -0
- package/dist/commands/auth/init.js +245 -73
- package/dist/commands/auth/login.d.ts +2 -0
- package/dist/commands/auth/login.js +136 -18
- package/dist/commands/auth/logout.js +19 -7
- package/dist/commands/auth/sessions.js +7 -4
- package/dist/commands/auth/status.js +33 -12
- package/dist/commands/auth/token.d.ts +4 -0
- package/dist/commands/auth/token.js +73 -0
- package/dist/commands/check/index.d.ts +2 -0
- package/dist/commands/check/index.js +463 -0
- package/dist/commands/credits/index.js +2 -3
- package/dist/commands/inspect/index.d.ts +2 -0
- package/dist/commands/inspect/index.js +136 -0
- package/dist/commands/mcp/index.js +81 -14
- package/dist/commands/open/index.d.ts +0 -8
- package/dist/commands/open/index.js +0 -11
- package/dist/commands/scan/index.js +3 -8
- package/dist/commands/target/index.js +0 -3
- package/dist/commands/tm/index.js +3 -10
- package/dist/index.js +13 -1
- package/dist/internal/binding-token-mint.d.ts +33 -0
- package/dist/internal/binding-token-mint.js +174 -0
- package/dist/internal/extractor/adapters/go/index.d.ts +0 -4
- package/dist/internal/extractor/adapters/go/index.js +0 -6
- package/dist/internal/extractor/adapters/go/manifest.d.ts +0 -17
- package/dist/internal/extractor/adapters/go/manifest.js +19 -25
- package/dist/internal/extractor/adapters/go/parser.d.ts +0 -4
- package/dist/internal/extractor/adapters/go/parser.js +0 -5
- package/dist/internal/extractor/adapters/go/scanner.d.ts +0 -20
- package/dist/internal/extractor/adapters/go/scanner.js +0 -43
- package/dist/internal/extractor/adapters/python/index.d.ts +0 -3
- package/dist/internal/extractor/adapters/python/index.js +0 -4
- package/dist/internal/extractor/adapters/python/manifest.d.ts +0 -8
- package/dist/internal/extractor/adapters/python/manifest.js +21 -18
- package/dist/internal/extractor/adapters/python/parser.d.ts +0 -3
- package/dist/internal/extractor/adapters/python/parser.js +0 -4
- package/dist/internal/extractor/adapters/python/scanner.d.ts +0 -18
- package/dist/internal/extractor/adapters/python/scanner.js +1 -54
- package/dist/internal/extractor/adapters/typescript/index.d.ts +0 -7
- package/dist/internal/extractor/adapters/typescript/index.js +1 -21
- package/dist/internal/extractor/adapters/typescript/manifest.d.ts +0 -14
- package/dist/internal/extractor/adapters/typescript/manifest.js +26 -17
- package/dist/internal/extractor/adapters/typescript/parser.d.ts +0 -8
- package/dist/internal/extractor/adapters/typescript/parser.js +0 -9
- package/dist/internal/extractor/adapters/typescript/scanner.d.ts +0 -24
- package/dist/internal/extractor/adapters/typescript/scanner.js +0 -43
- package/dist/internal/extractor/graph-builder.d.ts +10 -0
- package/dist/internal/extractor/graph-builder.js +245 -0
- package/dist/internal/extractor/graph-types.d.ts +89 -0
- package/dist/internal/extractor/graph-types.js +1 -0
- package/dist/internal/extractor/index.d.ts +0 -14
- package/dist/internal/extractor/index.js +0 -13
- package/dist/internal/extractor/types.d.ts +0 -45
- package/dist/internal/extractor/types.js +0 -27
- package/dist/internal/extractor/walker.d.ts +0 -18
- package/dist/internal/extractor/walker.js +0 -18
- package/dist/internal/github-linkage-prompt.d.ts +7 -0
- package/dist/internal/github-linkage-prompt.js +95 -0
- package/dist/internal/ide-setup.d.ts +16 -7
- package/dist/internal/ide-setup.js +320 -46
- package/dist/internal/org-picker.d.ts +11 -0
- package/dist/internal/org-picker.js +34 -0
- package/dist/internal/orgs.js +0 -1
- package/dist/internal/project-binding.d.ts +1 -0
- package/dist/internal/project-binding.js +14 -9
- package/dist/internal/target-auth/api-config.d.ts +0 -17
- package/dist/internal/target-auth/api-config.js +0 -18
- package/dist/internal/target-auth/config.d.ts +0 -12
- package/dist/internal/target-auth/config.js +0 -13
- package/dist/internal/target-auth/index.d.ts +0 -18
- package/dist/internal/target-auth/index.js +0 -19
- package/dist/internal/target-auth/keychain.d.ts +0 -18
- package/dist/internal/target-auth/keychain.js +3 -23
- package/dist/internal/target-auth/types.d.ts +0 -26
- package/dist/internal/target-auth/types.js +0 -10
- package/dist/internal/target-auth/workos.d.ts +0 -15
- package/dist/internal/target-auth/workos.js +9 -18
- package/package.json +19 -5
- package/dist/commands/app/index.js.map +0 -1
- package/dist/commands/auth/index.js.map +0 -1
- package/dist/commands/auth/init.js.map +0 -1
- package/dist/commands/auth/login.js.map +0 -1
- package/dist/commands/auth/logout.js.map +0 -1
- package/dist/commands/auth/sessions.js.map +0 -1
- package/dist/commands/auth/status.js.map +0 -1
- package/dist/commands/credits/index.js.map +0 -1
- package/dist/commands/mcp/index.js.map +0 -1
- package/dist/commands/open/index.js.map +0 -1
- package/dist/commands/scan/index.js.map +0 -1
- package/dist/commands/target/index.js.map +0 -1
- package/dist/commands/tm/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal/extractor/adapters/go/index.js.map +0 -1
- package/dist/internal/extractor/adapters/go/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/go/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/go/scanner.js.map +0 -1
- package/dist/internal/extractor/adapters/python/index.js.map +0 -1
- package/dist/internal/extractor/adapters/python/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/python/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/python/scanner.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/index.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/manifest.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/parser.js.map +0 -1
- package/dist/internal/extractor/adapters/typescript/scanner.js.map +0 -1
- package/dist/internal/extractor/index.js.map +0 -1
- package/dist/internal/extractor/types.js.map +0 -1
- package/dist/internal/extractor/walker.js.map +0 -1
- package/dist/internal/ide-setup.js.map +0 -1
- package/dist/internal/orgs.js.map +0 -1
- package/dist/internal/project-binding.js.map +0 -1
- package/dist/internal/target-auth/api-config.js.map +0 -1
- package/dist/internal/target-auth/config.js.map +0 -1
- package/dist/internal/target-auth/index.js.map +0 -1
- package/dist/internal/target-auth/keychain.js.map +0 -1
- package/dist/internal/target-auth/types.js.map +0 -1
- package/dist/internal/target-auth/workos.js.map +0 -1
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Go language adapter. Same shape as the TypeScript adapter — manifest
|
|
3
|
-
* pass + AST pass — different parser + framework table.
|
|
4
|
-
*/
|
|
5
1
|
import * as path from "node:path";
|
|
6
2
|
import { readManifests, summarizeManifests, findExternalServices, findDataStores, detectFrameworkFromManifest, STDLIB_FRAMEWORKS, } from "./manifest.js";
|
|
7
3
|
import { scanFile, aggregate } from "./scanner.js";
|
|
@@ -66,6 +62,4 @@ function frameworkFromOwningManifest(sourceFile, manifests) {
|
|
|
66
62
|
.sort((a, b) => b.dir.length - a.dir.length);
|
|
67
63
|
return candidates[0] ? detectFrameworkFromManifest(candidates[0].m) : undefined;
|
|
68
64
|
}
|
|
69
|
-
// Used by orchestrator-side typing to widen the language union if needed.
|
|
70
65
|
export const _stdlibFrameworkSet = STDLIB_FRAMEWORKS;
|
|
71
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Go manifest analysis.
|
|
3
|
-
*
|
|
4
|
-
* Parses go.mod (line-based, no real grammar needed) to build the
|
|
5
|
-
* dependency map. Doesn't read go.sum — that's a hash list, not version
|
|
6
|
-
* authority. Direct vs indirect deps are both in the require block;
|
|
7
|
-
* we keep the // indirect annotation as a hint but don't filter on it.
|
|
8
|
-
*/
|
|
9
1
|
import type { ExtractedExternalService, ExtractedDataStore, ManifestSummary } from "../../types.js";
|
|
10
2
|
export declare const WEB_FRAMEWORK_MODULES: Record<string, string>;
|
|
11
|
-
/** stdlib router lives at "net/http"; not in go.mod, only detected via imports. */
|
|
12
3
|
export declare const STDLIB_FRAMEWORKS: Set<string>;
|
|
13
4
|
export declare const DATA_STORE_MODULES: Record<string, {
|
|
14
5
|
kind: ExtractedDataStore["kind"];
|
|
@@ -19,17 +10,9 @@ export interface ParsedGoMod {
|
|
|
19
10
|
filePath: string;
|
|
20
11
|
module?: string;
|
|
21
12
|
goVersion?: string;
|
|
22
|
-
/** module path → version. */
|
|
23
13
|
deps: Record<string, string>;
|
|
24
14
|
}
|
|
25
15
|
export declare function readManifests(rootDir: string, manifestFiles: string[]): Promise<ParsedGoMod[]>;
|
|
26
|
-
/**
|
|
27
|
-
* Minimal go.mod parser. Supports the two block forms:
|
|
28
|
-
* require modulePath v1.2.3
|
|
29
|
-
* require ( modulePath v1.2.3 \n other v0.0.1 )
|
|
30
|
-
* Strips trailing "// indirect" markers. Does NOT handle replace/exclude
|
|
31
|
-
* directives (out of scope for inventory).
|
|
32
|
-
*/
|
|
33
16
|
export declare function parseGoMod(filePath: string, rootDir: string, raw: string): ParsedGoMod;
|
|
34
17
|
export declare function summarizeManifests(parsed: ParsedGoMod[]): ManifestSummary[];
|
|
35
18
|
export declare function findExternalServices(parsed: ParsedGoMod[]): ExtractedExternalService[];
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Go manifest analysis.
|
|
3
|
-
*
|
|
4
|
-
* Parses go.mod (line-based, no real grammar needed) to build the
|
|
5
|
-
* dependency map. Doesn't read go.sum — that's a hash list, not version
|
|
6
|
-
* authority. Direct vs indirect deps are both in the require block;
|
|
7
|
-
* we keep the // indirect annotation as a hint but don't filter on it.
|
|
8
|
-
*/
|
|
9
1
|
import * as fs from "node:fs/promises";
|
|
10
2
|
import * as path from "node:path";
|
|
11
3
|
export const WEB_FRAMEWORK_MODULES = {
|
|
@@ -18,14 +10,13 @@ export const WEB_FRAMEWORK_MODULES = {
|
|
|
18
10
|
"github.com/gofiber/fiber/v2": "fiber",
|
|
19
11
|
"github.com/gorilla/mux": "gorilla-mux",
|
|
20
12
|
};
|
|
21
|
-
/** stdlib router lives at "net/http"; not in go.mod, only detected via imports. */
|
|
22
13
|
export const STDLIB_FRAMEWORKS = new Set(["net/http"]);
|
|
23
14
|
export const DATA_STORE_MODULES = {
|
|
24
15
|
"github.com/jackc/pgx": { kind: "postgres", client: "pgx" },
|
|
25
16
|
"github.com/jackc/pgx/v5": { kind: "postgres", client: "pgx" },
|
|
26
17
|
"github.com/jackc/pgx/v4": { kind: "postgres", client: "pgx" },
|
|
27
18
|
"github.com/lib/pq": { kind: "postgres", client: "pq" },
|
|
28
|
-
"gorm.io/gorm": { kind: "postgres", client: "gorm" },
|
|
19
|
+
"gorm.io/gorm": { kind: "postgres", client: "gorm" },
|
|
29
20
|
"go.mongodb.org/mongo-driver": { kind: "mongo", client: "mongo-driver" },
|
|
30
21
|
"github.com/redis/go-redis": { kind: "redis", client: "go-redis" },
|
|
31
22
|
"github.com/redis/go-redis/v9": { kind: "redis", client: "go-redis" },
|
|
@@ -48,18 +39,10 @@ export async function readManifests(rootDir, manifestFiles) {
|
|
|
48
39
|
out.push(parseGoMod(file, rootDir, raw));
|
|
49
40
|
}
|
|
50
41
|
catch {
|
|
51
|
-
// skip unreadable
|
|
52
42
|
}
|
|
53
43
|
}
|
|
54
44
|
return out;
|
|
55
45
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Minimal go.mod parser. Supports the two block forms:
|
|
58
|
-
* require modulePath v1.2.3
|
|
59
|
-
* require ( modulePath v1.2.3 \n other v0.0.1 )
|
|
60
|
-
* Strips trailing "// indirect" markers. Does NOT handle replace/exclude
|
|
61
|
-
* directives (out of scope for inventory).
|
|
62
|
-
*/
|
|
63
46
|
export function parseGoMod(filePath, rootDir, raw) {
|
|
64
47
|
const lines = raw.split("\n");
|
|
65
48
|
const result = {
|
|
@@ -92,7 +75,6 @@ export function parseGoMod(filePath, rootDir, raw) {
|
|
|
92
75
|
body = body.slice("require ".length).trim();
|
|
93
76
|
else if (!inBlock)
|
|
94
77
|
continue;
|
|
95
|
-
// Strip "// indirect" / other line comments
|
|
96
78
|
const commentAt = body.indexOf("//");
|
|
97
79
|
if (commentAt >= 0)
|
|
98
80
|
body = body.slice(0, commentAt).trim();
|
|
@@ -132,6 +114,24 @@ export function findExternalServices(parsed) {
|
|
|
132
114
|
callSites: [{ file: m.filePath, line: 1 }],
|
|
133
115
|
});
|
|
134
116
|
}
|
|
117
|
+
for (const [mod, vendor] of Object.entries(WEB_FRAMEWORK_MODULES)) {
|
|
118
|
+
const ver = matchModuleVersion(m.deps, mod);
|
|
119
|
+
if (!ver)
|
|
120
|
+
continue;
|
|
121
|
+
const key = `web_framework::${mod}`;
|
|
122
|
+
const existing = seen.get(key);
|
|
123
|
+
if (existing) {
|
|
124
|
+
existing.callSites.push({ file: m.filePath, line: 1 });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
seen.set(key, {
|
|
128
|
+
name: vendor,
|
|
129
|
+
kind: "web_framework",
|
|
130
|
+
package: mod,
|
|
131
|
+
version: ver,
|
|
132
|
+
callSites: [{ file: m.filePath, line: 1 }],
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
135
|
}
|
|
136
136
|
return [...seen.values()];
|
|
137
137
|
}
|
|
@@ -166,14 +166,9 @@ export function detectFrameworkFromManifest(m) {
|
|
|
166
166
|
}
|
|
167
167
|
return undefined;
|
|
168
168
|
}
|
|
169
|
-
/**
|
|
170
|
-
* Match a module path including its versioned variants (mod, mod/v2, etc.).
|
|
171
|
-
* Returns the version of the first match.
|
|
172
|
-
*/
|
|
173
169
|
function matchModuleVersion(deps, modulePath) {
|
|
174
170
|
if (deps[modulePath])
|
|
175
171
|
return deps[modulePath];
|
|
176
|
-
// Try versioned variants
|
|
177
172
|
for (const [k, v] of Object.entries(deps)) {
|
|
178
173
|
if (k === modulePath)
|
|
179
174
|
return v;
|
|
@@ -196,4 +191,3 @@ function relPosix(rootDir, abs) {
|
|
|
196
191
|
const rel = path.relative(rootDir, abs);
|
|
197
192
|
return rel.split(path.sep).join("/");
|
|
198
193
|
}
|
|
199
|
-
//# sourceMappingURL=manifest.js.map
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tree-sitter-go loader. Lazy-init, reuses the same WASM directory as
|
|
3
|
-
* the TypeScript adapter (both come from @vscode/tree-sitter-wasm).
|
|
4
|
-
*/
|
|
5
1
|
import { createRequire } from "node:module";
|
|
6
2
|
import * as path from "node:path";
|
|
7
3
|
import { Parser, Language } from "web-tree-sitter";
|
|
@@ -20,4 +16,3 @@ export function getGoParser() {
|
|
|
20
16
|
}
|
|
21
17
|
return parserPromise;
|
|
22
18
|
}
|
|
23
|
-
//# sourceMappingURL=parser.js.map
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AST scanner for Go source files.
|
|
3
|
-
*
|
|
4
|
-
* Walks tree-sitter-go for two things:
|
|
5
|
-
*
|
|
6
|
-
* 1. import declarations naming any tracked module (AI SDKs, web
|
|
7
|
-
* frameworks, DB clients) → ImportEvidence
|
|
8
|
-
* 2. HTTP route calls. Three flavors:
|
|
9
|
-
* net/http : http.HandleFunc("/path", h)
|
|
10
|
-
* chi : r.Get("/path", h), r.Post(…) (TitleCase)
|
|
11
|
-
* echo/gin : e.GET("/path", h), r.POST(…) (UPPER)
|
|
12
|
-
* fiber : app.Get("/path", h), app.Post(…) (TitleCase)
|
|
13
|
-
*
|
|
14
|
-
* Method-name normalization happens after we identify the call-shape;
|
|
15
|
-
* output is always UPPERCASE in the JSON.
|
|
16
|
-
*
|
|
17
|
-
* Privacy invariant: same as the TS adapter — only identifier names,
|
|
18
|
-
* file paths, line numbers, package paths, and route method/path strings.
|
|
19
|
-
* Function bodies are never read.
|
|
20
|
-
*/
|
|
21
1
|
import type { EvidenceLocation, Evidence, HttpRouteEvidence, ImportEvidence } from "../../types.js";
|
|
22
2
|
export interface FileScanResult {
|
|
23
3
|
file: string;
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AST scanner for Go source files.
|
|
3
|
-
*
|
|
4
|
-
* Walks tree-sitter-go for two things:
|
|
5
|
-
*
|
|
6
|
-
* 1. import declarations naming any tracked module (AI SDKs, web
|
|
7
|
-
* frameworks, DB clients) → ImportEvidence
|
|
8
|
-
* 2. HTTP route calls. Three flavors:
|
|
9
|
-
* net/http : http.HandleFunc("/path", h)
|
|
10
|
-
* chi : r.Get("/path", h), r.Post(…) (TitleCase)
|
|
11
|
-
* echo/gin : e.GET("/path", h), r.POST(…) (UPPER)
|
|
12
|
-
* fiber : app.Get("/path", h), app.Post(…) (TitleCase)
|
|
13
|
-
*
|
|
14
|
-
* Method-name normalization happens after we identify the call-shape;
|
|
15
|
-
* output is always UPPERCASE in the JSON.
|
|
16
|
-
*
|
|
17
|
-
* Privacy invariant: same as the TS adapter — only identifier names,
|
|
18
|
-
* file paths, line numbers, package paths, and route method/path strings.
|
|
19
|
-
* Function bodies are never read.
|
|
20
|
-
*/
|
|
21
1
|
import * as fs from "node:fs/promises";
|
|
22
2
|
import { getGoParser } from "./parser.js";
|
|
23
3
|
import { AI_SDK_MODULES, DATA_STORE_MODULES, WEB_FRAMEWORK_MODULES, STDLIB_FRAMEWORKS, } from "./manifest.js";
|
|
@@ -74,64 +54,45 @@ function walk(node, visit) {
|
|
|
74
54
|
walk(child, visit);
|
|
75
55
|
}
|
|
76
56
|
}
|
|
77
|
-
/** Pull the quoted module path out of an import_spec node. */
|
|
78
57
|
function extractImportPath(node) {
|
|
79
|
-
// import_spec → optional package_identifier + interpreted_string_literal
|
|
80
58
|
const stringNode = node.namedChildren.find((c) => c?.type === "interpreted_string_literal");
|
|
81
59
|
if (!stringNode)
|
|
82
60
|
return null;
|
|
83
|
-
// text is the quoted form; strip the surrounding quotes.
|
|
84
61
|
const text = stringNode.text;
|
|
85
62
|
return text.length >= 2 ? text.slice(1, -1) : null;
|
|
86
63
|
}
|
|
87
64
|
function trackedModuleMatch(pkg) {
|
|
88
65
|
if (TRACKED_MODULES.has(pkg))
|
|
89
66
|
return pkg;
|
|
90
|
-
// Sub-package import — e.g., "github.com/jackc/pgx/v5/pgxpool"
|
|
91
67
|
for (const tracked of TRACKED_MODULES) {
|
|
92
68
|
if (pkg.startsWith(tracked + "/"))
|
|
93
69
|
return tracked;
|
|
94
70
|
}
|
|
95
71
|
return null;
|
|
96
72
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Match an HTTP-route call by inspecting the call-expression shape.
|
|
99
|
-
* Two AST patterns to handle:
|
|
100
|
-
* - selector_expression(operand=identifier, field=field_identifier) called with args
|
|
101
|
-
* ⇒ http.HandleFunc, app.Get, e.GET, r.Post …
|
|
102
|
-
* - identifier called with args (rare for routes; ignored)
|
|
103
|
-
*/
|
|
104
73
|
function extractRouteCall(node) {
|
|
105
|
-
// call_expression has: function (selector_expression | identifier) + argument_list
|
|
106
74
|
const fn = node.namedChildren.find((c) => c?.type === "selector_expression");
|
|
107
75
|
const args = node.namedChildren.find((c) => c?.type === "argument_list");
|
|
108
76
|
if (!fn || !args)
|
|
109
77
|
return null;
|
|
110
|
-
// selector_expression: operand (any) . field (field_identifier)
|
|
111
78
|
const operand = fn.namedChildren[0];
|
|
112
79
|
const field = fn.namedChildren.find((c) => c?.type === "field_identifier");
|
|
113
80
|
if (!operand || !field)
|
|
114
81
|
return null;
|
|
115
82
|
const operandText = operand.text;
|
|
116
83
|
const fieldName = field.text;
|
|
117
|
-
// Determine the HTTP method from the field name + operand shape.
|
|
118
84
|
let method = null;
|
|
119
85
|
if (HTTP_METHODS_UPPER.has(fieldName)) {
|
|
120
|
-
// echo/gin: e.GET, r.POST … — operand is a router-like local var
|
|
121
86
|
method = fieldName === "ANY" ? "ALL" : fieldName;
|
|
122
87
|
}
|
|
123
88
|
else if (HTTP_METHODS_TITLE.has(fieldName)) {
|
|
124
|
-
// chi/fiber: r.Get, app.Post …
|
|
125
89
|
method = fieldName.toUpperCase();
|
|
126
90
|
}
|
|
127
91
|
else if (STDLIB_HANDLER_FNS.has(fieldName) && operandText === "http") {
|
|
128
|
-
// net/http: http.HandleFunc("/x", h) / http.Handle("/x", h)
|
|
129
|
-
// We don't know the HTTP verb statically — emit "ALL".
|
|
130
92
|
method = "ALL";
|
|
131
93
|
}
|
|
132
94
|
if (!method)
|
|
133
95
|
return null;
|
|
134
|
-
// First arg = string literal route path.
|
|
135
96
|
const firstArg = args.namedChildren[0];
|
|
136
97
|
if (!firstArg)
|
|
137
98
|
return null;
|
|
@@ -150,7 +111,6 @@ export function aggregate(scans) {
|
|
|
150
111
|
if (s.routes.length === 0 && s.imports.length === 0)
|
|
151
112
|
continue;
|
|
152
113
|
for (const imp of s.imports) {
|
|
153
|
-
// Match against the FULL tracked module (allowing sub-paths).
|
|
154
114
|
const matched = matchTracked(imp.package);
|
|
155
115
|
if (!matched)
|
|
156
116
|
continue;
|
|
@@ -191,8 +151,6 @@ function matchTracked(pkg) {
|
|
|
191
151
|
}
|
|
192
152
|
function deriveComponentName(filePath) {
|
|
193
153
|
const segs = filePath.split("/");
|
|
194
|
-
// Go convention: cmd/foo/main.go → "foo"; internal/auth/handler.go → "auth"
|
|
195
|
-
// Use the deepest directory that isn't a stdlib-ish folder name.
|
|
196
154
|
const skipPrefixes = new Set(["cmd", "internal", "pkg", "src", "app"]);
|
|
197
155
|
const start = skipPrefixes.has(segs[0]) ? 1 : 0;
|
|
198
156
|
if (segs.length - start >= 2)
|
|
@@ -213,4 +171,3 @@ function detectFrameworkFromImports(imports) {
|
|
|
213
171
|
}
|
|
214
172
|
return undefined;
|
|
215
173
|
}
|
|
216
|
-
//# sourceMappingURL=scanner.js.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Python language adapter. Same shape as TS/Go.
|
|
3
|
-
*/
|
|
4
1
|
import * as path from "node:path";
|
|
5
2
|
import { readManifests, summarizeManifests, findExternalServices, findDataStores, detectFrameworkFromManifest, } from "./manifest.js";
|
|
6
3
|
import { scanFile, aggregate } from "./scanner.js";
|
|
@@ -65,4 +62,3 @@ function frameworkFromOwningManifest(sourceFile, manifests) {
|
|
|
65
62
|
.sort((a, b) => b.dir.length - a.dir.length);
|
|
66
63
|
return candidates[0] ? detectFrameworkFromManifest(candidates[0].m) : undefined;
|
|
67
64
|
}
|
|
68
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Python manifest analysis.
|
|
3
|
-
*
|
|
4
|
-
* Reads pyproject.toml (PEP 621 + Poetry sections) and requirements.txt.
|
|
5
|
-
* Other formats (Pipfile, setup.py) deferred — they're either rare in
|
|
6
|
-
* modern projects (setup.py) or layer on top of these two.
|
|
7
|
-
*/
|
|
8
1
|
import type { ExtractedExternalService, ExtractedDataStore, ManifestSummary } from "../../types.js";
|
|
9
2
|
export declare const WEB_FRAMEWORK_PACKAGES: Record<string, string>;
|
|
10
3
|
export declare const DATA_STORE_PACKAGES: Record<string, {
|
|
@@ -15,7 +8,6 @@ export declare const AI_SDK_PACKAGES: Record<string, string>;
|
|
|
15
8
|
export interface ParsedPyManifest {
|
|
16
9
|
filePath: string;
|
|
17
10
|
format: "pyproject.toml" | "requirements.txt";
|
|
18
|
-
/** package name → version specifier (verbatim, e.g. "^0.30.0", ">=2.0,<3"). */
|
|
19
11
|
deps: Record<string, string>;
|
|
20
12
|
}
|
|
21
13
|
export declare function readManifests(rootDir: string, manifestFiles: string[]): Promise<ParsedPyManifest[]>;
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Python manifest analysis.
|
|
3
|
-
*
|
|
4
|
-
* Reads pyproject.toml (PEP 621 + Poetry sections) and requirements.txt.
|
|
5
|
-
* Other formats (Pipfile, setup.py) deferred — they're either rare in
|
|
6
|
-
* modern projects (setup.py) or layer on top of these two.
|
|
7
|
-
*/
|
|
8
1
|
import * as fs from "node:fs/promises";
|
|
9
2
|
import * as path from "node:path";
|
|
10
3
|
import TOML from "@iarna/toml";
|
|
@@ -20,7 +13,7 @@ export const WEB_FRAMEWORK_PACKAGES = {
|
|
|
20
13
|
quart: "quart",
|
|
21
14
|
};
|
|
22
15
|
export const DATA_STORE_PACKAGES = {
|
|
23
|
-
sqlalchemy: { kind: "postgres", client: "sqlalchemy" },
|
|
16
|
+
sqlalchemy: { kind: "postgres", client: "sqlalchemy" },
|
|
24
17
|
asyncpg: { kind: "postgres", client: "asyncpg" },
|
|
25
18
|
psycopg: { kind: "postgres", client: "psycopg" },
|
|
26
19
|
psycopg2: { kind: "postgres", client: "psycopg2" },
|
|
@@ -45,7 +38,7 @@ export const AI_SDK_PACKAGES = {
|
|
|
45
38
|
vertexai: "Vertex AI",
|
|
46
39
|
"google-generativeai": "Google Generative AI",
|
|
47
40
|
cohere: "Cohere",
|
|
48
|
-
"boto3": "AWS (boto3)",
|
|
41
|
+
"boto3": "AWS (boto3)",
|
|
49
42
|
};
|
|
50
43
|
export async function readManifests(rootDir, manifestFiles) {
|
|
51
44
|
const out = [];
|
|
@@ -61,7 +54,6 @@ export async function readManifests(rootDir, manifestFiles) {
|
|
|
61
54
|
}
|
|
62
55
|
}
|
|
63
56
|
catch {
|
|
64
|
-
// skip unreadable / unparsable
|
|
65
57
|
}
|
|
66
58
|
}
|
|
67
59
|
return out;
|
|
@@ -79,7 +71,6 @@ function parsePyproject(filePath, rootDir, raw) {
|
|
|
79
71
|
catch {
|
|
80
72
|
return result;
|
|
81
73
|
}
|
|
82
|
-
// PEP 621: project.dependencies = ["fastapi >=0.100", ...]
|
|
83
74
|
const projectDeps = parsed?.project?.dependencies;
|
|
84
75
|
if (Array.isArray(projectDeps)) {
|
|
85
76
|
for (const spec of projectDeps) {
|
|
@@ -90,7 +81,6 @@ function parsePyproject(filePath, rootDir, raw) {
|
|
|
90
81
|
result.deps[name] = version;
|
|
91
82
|
}
|
|
92
83
|
}
|
|
93
|
-
// PEP 621: optional-dependencies = { test = [...], dev = [...] }
|
|
94
84
|
const opt = parsed?.project?.["optional-dependencies"];
|
|
95
85
|
if (opt && typeof opt === "object") {
|
|
96
86
|
for (const groupSpec of Object.values(opt)) {
|
|
@@ -105,7 +95,6 @@ function parsePyproject(filePath, rootDir, raw) {
|
|
|
105
95
|
}
|
|
106
96
|
}
|
|
107
97
|
}
|
|
108
|
-
// Poetry: tool.poetry.dependencies = { fastapi = "^0.100" } (also dev-dependencies)
|
|
109
98
|
const poetryDeps = parsed?.tool?.poetry?.dependencies;
|
|
110
99
|
if (poetryDeps && typeof poetryDeps === "object") {
|
|
111
100
|
for (const [name, value] of Object.entries(poetryDeps)) {
|
|
@@ -137,8 +126,7 @@ function parseRequirementsTxt(filePath, rootDir, raw) {
|
|
|
137
126
|
if (!line || line.startsWith("#"))
|
|
138
127
|
continue;
|
|
139
128
|
if (line.startsWith("-"))
|
|
140
|
-
continue;
|
|
141
|
-
// strip inline comments
|
|
129
|
+
continue;
|
|
142
130
|
const hashAt = line.indexOf(" #");
|
|
143
131
|
if (hashAt > 0)
|
|
144
132
|
line = line.slice(0, hashAt).trim();
|
|
@@ -148,9 +136,7 @@ function parseRequirementsTxt(filePath, rootDir, raw) {
|
|
|
148
136
|
}
|
|
149
137
|
return result;
|
|
150
138
|
}
|
|
151
|
-
/** "fastapi>=0.100,<0.110" → ["fastapi", ">=0.100,<0.110"] */
|
|
152
139
|
function splitPep508(spec) {
|
|
153
|
-
// strip extras like fastapi[all]
|
|
154
140
|
const cleaned = spec.replace(/\[[^\]]*\]/g, "").trim();
|
|
155
141
|
const m = cleaned.match(/^([A-Za-z0-9_.\-]+)\s*(.*)$/);
|
|
156
142
|
if (!m)
|
|
@@ -185,6 +171,24 @@ export function findExternalServices(parsed) {
|
|
|
185
171
|
callSites: [{ file: m.filePath, line: 1 }],
|
|
186
172
|
});
|
|
187
173
|
}
|
|
174
|
+
for (const [pkg, vendor] of Object.entries(WEB_FRAMEWORK_PACKAGES)) {
|
|
175
|
+
const ver = m.deps[pkg];
|
|
176
|
+
if (!ver)
|
|
177
|
+
continue;
|
|
178
|
+
const key = `web_framework::${pkg}`;
|
|
179
|
+
const existing = seen.get(key);
|
|
180
|
+
if (existing) {
|
|
181
|
+
existing.callSites.push({ file: m.filePath, line: 1 });
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
seen.set(key, {
|
|
185
|
+
name: vendor,
|
|
186
|
+
kind: "web_framework",
|
|
187
|
+
package: pkg,
|
|
188
|
+
version: ver,
|
|
189
|
+
callSites: [{ file: m.filePath, line: 1 }],
|
|
190
|
+
});
|
|
191
|
+
}
|
|
188
192
|
}
|
|
189
193
|
return [...seen.values()];
|
|
190
194
|
}
|
|
@@ -233,4 +237,3 @@ function relPosix(rootDir, abs) {
|
|
|
233
237
|
const rel = path.relative(rootDir, abs);
|
|
234
238
|
return rel.split(path.sep).join("/");
|
|
235
239
|
}
|
|
236
|
-
//# sourceMappingURL=manifest.js.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tree-sitter-python loader.
|
|
3
|
-
*/
|
|
4
1
|
import { createRequire } from "node:module";
|
|
5
2
|
import * as path from "node:path";
|
|
6
3
|
import { Parser, Language } from "web-tree-sitter";
|
|
@@ -19,4 +16,3 @@ export function getPythonParser() {
|
|
|
19
16
|
}
|
|
20
17
|
return parserPromise;
|
|
21
18
|
}
|
|
22
|
-
//# sourceMappingURL=parser.js.map
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AST scanner for Python source files.
|
|
3
|
-
*
|
|
4
|
-
* Patterns covered:
|
|
5
|
-
* 1. import_statement / import_from_statement → tracked-package imports
|
|
6
|
-
* (AI SDKs, web frameworks, DB clients).
|
|
7
|
-
* 2. Decorator-based routes (FastAPI, Flask):
|
|
8
|
-
* @app.get("/path") → method=GET
|
|
9
|
-
* @app.post("/path") → method=POST
|
|
10
|
-
* @app.route("/path", …) → method=ALL (Flask default; verbs in `methods=` kw not parsed yet)
|
|
11
|
-
* @router.get("/x", …) → method=GET (FastAPI APIRouter convention)
|
|
12
|
-
* 3. Django urls.py-style call: path("/route", view, …) / re_path(…) → method=ALL
|
|
13
|
-
* 4. aiohttp: app.router.add_get("/path", h) / add_post(…) / etc.
|
|
14
|
-
*
|
|
15
|
-
* Privacy invariant: identifier names + dotted attribute names + string
|
|
16
|
-
* literals on routes only. Function bodies and decorator args beyond the
|
|
17
|
-
* route path are never inspected.
|
|
18
|
-
*/
|
|
19
1
|
import type { EvidenceLocation, Evidence, HttpRouteEvidence, ImportEvidence } from "../../types.js";
|
|
20
2
|
export interface FileScanResult {
|
|
21
3
|
file: string;
|
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AST scanner for Python source files.
|
|
3
|
-
*
|
|
4
|
-
* Patterns covered:
|
|
5
|
-
* 1. import_statement / import_from_statement → tracked-package imports
|
|
6
|
-
* (AI SDKs, web frameworks, DB clients).
|
|
7
|
-
* 2. Decorator-based routes (FastAPI, Flask):
|
|
8
|
-
* @app.get("/path") → method=GET
|
|
9
|
-
* @app.post("/path") → method=POST
|
|
10
|
-
* @app.route("/path", …) → method=ALL (Flask default; verbs in `methods=` kw not parsed yet)
|
|
11
|
-
* @router.get("/x", …) → method=GET (FastAPI APIRouter convention)
|
|
12
|
-
* 3. Django urls.py-style call: path("/route", view, …) / re_path(…) → method=ALL
|
|
13
|
-
* 4. aiohttp: app.router.add_get("/path", h) / add_post(…) / etc.
|
|
14
|
-
*
|
|
15
|
-
* Privacy invariant: identifier names + dotted attribute names + string
|
|
16
|
-
* literals on routes only. Function bodies and decorator args beyond the
|
|
17
|
-
* route path are never inspected.
|
|
18
|
-
*/
|
|
19
1
|
import * as fs from "node:fs/promises";
|
|
20
2
|
import { getPythonParser } from "./parser.js";
|
|
21
3
|
import { AI_SDK_PACKAGES, DATA_STORE_PACKAGES, WEB_FRAMEWORK_PACKAGES, } from "./manifest.js";
|
|
@@ -87,22 +69,9 @@ function walk(node, visit) {
|
|
|
87
69
|
walk(child, visit);
|
|
88
70
|
}
|
|
89
71
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Pull the top-level package(s) out of an import statement.
|
|
92
|
-
*
|
|
93
|
-
* - `import anthropic` → ["anthropic"]
|
|
94
|
-
* - `import langchain.chat_models` → ["langchain"] (we track the root)
|
|
95
|
-
* - `from fastapi import FastAPI` → ["fastapi"]
|
|
96
|
-
* - `from langchain.schema import HumanMessage` → ["langchain"]
|
|
97
|
-
*
|
|
98
|
-
* Sub-modules collapse to the root because the manifest table is keyed
|
|
99
|
-
* by distribution name, which is also the import root for the packages
|
|
100
|
-
* we care about.
|
|
101
|
-
*/
|
|
102
72
|
function extractImportPackages(node) {
|
|
103
73
|
const out = [];
|
|
104
74
|
if (node.type === "import_from_statement") {
|
|
105
|
-
// import_from_statement → module_name as the "module" field
|
|
106
75
|
const moduleField = node.childForFieldName("module_name");
|
|
107
76
|
if (moduleField) {
|
|
108
77
|
const root = topLevelImportName(moduleField);
|
|
@@ -111,7 +80,6 @@ function extractImportPackages(node) {
|
|
|
111
80
|
}
|
|
112
81
|
return out;
|
|
113
82
|
}
|
|
114
|
-
// import_statement: one or more dotted_name (or aliased_import wrapping it)
|
|
115
83
|
for (const child of node.namedChildren) {
|
|
116
84
|
if (!child)
|
|
117
85
|
continue;
|
|
@@ -141,13 +109,7 @@ function topLevelImportName(node) {
|
|
|
141
109
|
function trackedPackageMatch(pkg) {
|
|
142
110
|
return TRACKED_PACKAGES.has(pkg) ? pkg : null;
|
|
143
111
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Match @app.get("/path"), @router.post(…), @app.route("/x", …).
|
|
146
|
-
* Returns the route + method, or null if it isn't a known shape.
|
|
147
|
-
*/
|
|
148
112
|
function extractDecoratorRoute(node) {
|
|
149
|
-
// decorator → `@` + (call | attribute | identifier)
|
|
150
|
-
// We only care about call shapes: @app.get("/path", …)
|
|
151
113
|
const callNode = node.namedChildren.find((c) => c?.type === "call");
|
|
152
114
|
if (!callNode)
|
|
153
115
|
return null;
|
|
@@ -163,37 +125,28 @@ function extractDecoratorRoute(node) {
|
|
|
163
125
|
return null;
|
|
164
126
|
const objectName = objectNode.text.toLowerCase();
|
|
165
127
|
const attrName = attrNode.text.toLowerCase();
|
|
166
|
-
// Object must be a router-y identifier — keep the false-positive surface
|
|
167
|
-
// tight even though we'd lose decorators on uncommon names like `myapi`.
|
|
168
128
|
if (!ROUTER_IDENTIFIERS.has(objectName))
|
|
169
129
|
return null;
|
|
170
130
|
let method = null;
|
|
171
131
|
if (HTTP_METHODS.has(attrName))
|
|
172
132
|
method = attrName.toUpperCase();
|
|
173
133
|
else if (attrName === "route")
|
|
174
|
-
method = "ALL";
|
|
134
|
+
method = "ALL";
|
|
175
135
|
if (!method)
|
|
176
136
|
return null;
|
|
177
137
|
return readRoutePath(args, method);
|
|
178
138
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Match Django path("/route", view, …) / re_path(…) and aiohttp
|
|
181
|
-
* app.router.add_get("/path", h) / add_post(…) etc.
|
|
182
|
-
*/
|
|
183
139
|
function extractCallRoute(node) {
|
|
184
140
|
const fn = node.childForFieldName("function");
|
|
185
141
|
const args = node.childForFieldName("arguments");
|
|
186
142
|
if (!fn || !args)
|
|
187
143
|
return null;
|
|
188
|
-
// Django: bare identifier function, e.g. `path("/route", view)`.
|
|
189
144
|
if (fn.type === "identifier") {
|
|
190
145
|
const name = fn.text;
|
|
191
146
|
if (!DJANGO_PATH_FNS.has(name))
|
|
192
147
|
return null;
|
|
193
148
|
return readRoutePath(args, "ALL");
|
|
194
149
|
}
|
|
195
|
-
// aiohttp: app.router.add_get("/path", h) — attribute call where the
|
|
196
|
-
// method name is encoded into the attribute (add_get, add_post, …).
|
|
197
150
|
if (fn.type === "attribute") {
|
|
198
151
|
const attrNode = fn.childForFieldName("attribute");
|
|
199
152
|
if (!attrNode)
|
|
@@ -205,20 +158,16 @@ function extractCallRoute(node) {
|
|
|
205
158
|
}
|
|
206
159
|
return null;
|
|
207
160
|
}
|
|
208
|
-
/** Pull the first positional string-literal argument as the route path. */
|
|
209
161
|
function readRoutePath(argsNode, method) {
|
|
210
162
|
for (const arg of argsNode.namedChildren) {
|
|
211
163
|
if (!arg)
|
|
212
164
|
continue;
|
|
213
|
-
// Skip kwargs (we only want the first positional).
|
|
214
165
|
if (arg.type === "keyword_argument")
|
|
215
166
|
continue;
|
|
216
167
|
if (arg.type !== "string")
|
|
217
168
|
return null;
|
|
218
|
-
// string → string_start, string_content, string_end
|
|
219
169
|
const content = arg.namedChildren.find((c) => c?.type === "string_content");
|
|
220
170
|
if (!content) {
|
|
221
|
-
// fall back: strip leading/trailing quote chars from raw text
|
|
222
171
|
const text = arg.text;
|
|
223
172
|
if (text.length >= 2 && (text.startsWith('"') || text.startsWith("'"))) {
|
|
224
173
|
return { method, path: text.slice(1, -1) };
|
|
@@ -265,7 +214,6 @@ export function aggregate(scans) {
|
|
|
265
214
|
}
|
|
266
215
|
function deriveComponentName(filePath) {
|
|
267
216
|
const segs = filePath.split("/");
|
|
268
|
-
// Skip common project-layout prefixes.
|
|
269
217
|
const skipPrefixes = new Set(["src", "app", "apps", "backend", "api"]);
|
|
270
218
|
const start = skipPrefixes.has(segs[0]) ? 1 : 0;
|
|
271
219
|
if (segs.length - start >= 2)
|
|
@@ -281,4 +229,3 @@ function detectFrameworkFromImports(imports) {
|
|
|
281
229
|
}
|
|
282
230
|
return undefined;
|
|
283
231
|
}
|
|
284
|
-
//# sourceMappingURL=scanner.js.map
|
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TypeScript / JavaScript language adapter.
|
|
3
|
-
*
|
|
4
|
-
* Handles .ts/.tsx/.js/.jsx + package.json. Combines manifest analysis
|
|
5
|
-
* (cheap, runs against package.json) with AST scanning (per source file)
|
|
6
|
-
* to produce components, external services, and data stores.
|
|
7
|
-
*/
|
|
8
1
|
import type { LanguageAdapter } from "../../types.js";
|
|
9
2
|
export declare const typescriptAdapter: LanguageAdapter;
|