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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +31 -0
  3. package/dist/commands/app/index.d.ts +0 -8
  4. package/dist/commands/app/index.js +4 -16
  5. package/dist/commands/auth/bind.d.ts +4 -0
  6. package/dist/commands/auth/bind.js +97 -0
  7. package/dist/commands/auth/index.js +20 -2
  8. package/dist/commands/auth/init.d.ts +1 -0
  9. package/dist/commands/auth/init.js +245 -73
  10. package/dist/commands/auth/login.d.ts +2 -0
  11. package/dist/commands/auth/login.js +136 -18
  12. package/dist/commands/auth/logout.js +19 -7
  13. package/dist/commands/auth/sessions.js +7 -4
  14. package/dist/commands/auth/status.js +33 -12
  15. package/dist/commands/auth/token.d.ts +4 -0
  16. package/dist/commands/auth/token.js +73 -0
  17. package/dist/commands/check/index.d.ts +2 -0
  18. package/dist/commands/check/index.js +463 -0
  19. package/dist/commands/credits/index.js +2 -3
  20. package/dist/commands/inspect/index.d.ts +2 -0
  21. package/dist/commands/inspect/index.js +136 -0
  22. package/dist/commands/mcp/index.js +81 -14
  23. package/dist/commands/open/index.d.ts +0 -8
  24. package/dist/commands/open/index.js +0 -11
  25. package/dist/commands/scan/index.js +3 -8
  26. package/dist/commands/target/index.js +0 -3
  27. package/dist/commands/tm/index.js +3 -10
  28. package/dist/index.js +13 -1
  29. package/dist/internal/binding-token-mint.d.ts +33 -0
  30. package/dist/internal/binding-token-mint.js +174 -0
  31. package/dist/internal/extractor/adapters/go/index.d.ts +0 -4
  32. package/dist/internal/extractor/adapters/go/index.js +0 -6
  33. package/dist/internal/extractor/adapters/go/manifest.d.ts +0 -17
  34. package/dist/internal/extractor/adapters/go/manifest.js +19 -25
  35. package/dist/internal/extractor/adapters/go/parser.d.ts +0 -4
  36. package/dist/internal/extractor/adapters/go/parser.js +0 -5
  37. package/dist/internal/extractor/adapters/go/scanner.d.ts +0 -20
  38. package/dist/internal/extractor/adapters/go/scanner.js +0 -43
  39. package/dist/internal/extractor/adapters/python/index.d.ts +0 -3
  40. package/dist/internal/extractor/adapters/python/index.js +0 -4
  41. package/dist/internal/extractor/adapters/python/manifest.d.ts +0 -8
  42. package/dist/internal/extractor/adapters/python/manifest.js +21 -18
  43. package/dist/internal/extractor/adapters/python/parser.d.ts +0 -3
  44. package/dist/internal/extractor/adapters/python/parser.js +0 -4
  45. package/dist/internal/extractor/adapters/python/scanner.d.ts +0 -18
  46. package/dist/internal/extractor/adapters/python/scanner.js +1 -54
  47. package/dist/internal/extractor/adapters/typescript/index.d.ts +0 -7
  48. package/dist/internal/extractor/adapters/typescript/index.js +1 -21
  49. package/dist/internal/extractor/adapters/typescript/manifest.d.ts +0 -14
  50. package/dist/internal/extractor/adapters/typescript/manifest.js +26 -17
  51. package/dist/internal/extractor/adapters/typescript/parser.d.ts +0 -8
  52. package/dist/internal/extractor/adapters/typescript/parser.js +0 -9
  53. package/dist/internal/extractor/adapters/typescript/scanner.d.ts +0 -24
  54. package/dist/internal/extractor/adapters/typescript/scanner.js +0 -43
  55. package/dist/internal/extractor/graph-builder.d.ts +10 -0
  56. package/dist/internal/extractor/graph-builder.js +245 -0
  57. package/dist/internal/extractor/graph-types.d.ts +89 -0
  58. package/dist/internal/extractor/graph-types.js +1 -0
  59. package/dist/internal/extractor/index.d.ts +0 -14
  60. package/dist/internal/extractor/index.js +0 -13
  61. package/dist/internal/extractor/types.d.ts +0 -45
  62. package/dist/internal/extractor/types.js +0 -27
  63. package/dist/internal/extractor/walker.d.ts +0 -18
  64. package/dist/internal/extractor/walker.js +0 -18
  65. package/dist/internal/github-linkage-prompt.d.ts +7 -0
  66. package/dist/internal/github-linkage-prompt.js +95 -0
  67. package/dist/internal/ide-setup.d.ts +16 -7
  68. package/dist/internal/ide-setup.js +320 -46
  69. package/dist/internal/org-picker.d.ts +11 -0
  70. package/dist/internal/org-picker.js +34 -0
  71. package/dist/internal/orgs.js +0 -1
  72. package/dist/internal/project-binding.d.ts +1 -0
  73. package/dist/internal/project-binding.js +14 -9
  74. package/dist/internal/target-auth/api-config.d.ts +0 -17
  75. package/dist/internal/target-auth/api-config.js +0 -18
  76. package/dist/internal/target-auth/config.d.ts +0 -12
  77. package/dist/internal/target-auth/config.js +0 -13
  78. package/dist/internal/target-auth/index.d.ts +0 -18
  79. package/dist/internal/target-auth/index.js +0 -19
  80. package/dist/internal/target-auth/keychain.d.ts +0 -18
  81. package/dist/internal/target-auth/keychain.js +3 -23
  82. package/dist/internal/target-auth/types.d.ts +0 -26
  83. package/dist/internal/target-auth/types.js +0 -10
  84. package/dist/internal/target-auth/workos.d.ts +0 -15
  85. package/dist/internal/target-auth/workos.js +9 -18
  86. package/package.json +19 -5
  87. package/dist/commands/app/index.js.map +0 -1
  88. package/dist/commands/auth/index.js.map +0 -1
  89. package/dist/commands/auth/init.js.map +0 -1
  90. package/dist/commands/auth/login.js.map +0 -1
  91. package/dist/commands/auth/logout.js.map +0 -1
  92. package/dist/commands/auth/sessions.js.map +0 -1
  93. package/dist/commands/auth/status.js.map +0 -1
  94. package/dist/commands/credits/index.js.map +0 -1
  95. package/dist/commands/mcp/index.js.map +0 -1
  96. package/dist/commands/open/index.js.map +0 -1
  97. package/dist/commands/scan/index.js.map +0 -1
  98. package/dist/commands/target/index.js.map +0 -1
  99. package/dist/commands/tm/index.js.map +0 -1
  100. package/dist/index.js.map +0 -1
  101. package/dist/internal/extractor/adapters/go/index.js.map +0 -1
  102. package/dist/internal/extractor/adapters/go/manifest.js.map +0 -1
  103. package/dist/internal/extractor/adapters/go/parser.js.map +0 -1
  104. package/dist/internal/extractor/adapters/go/scanner.js.map +0 -1
  105. package/dist/internal/extractor/adapters/python/index.js.map +0 -1
  106. package/dist/internal/extractor/adapters/python/manifest.js.map +0 -1
  107. package/dist/internal/extractor/adapters/python/parser.js.map +0 -1
  108. package/dist/internal/extractor/adapters/python/scanner.js.map +0 -1
  109. package/dist/internal/extractor/adapters/typescript/index.js.map +0 -1
  110. package/dist/internal/extractor/adapters/typescript/manifest.js.map +0 -1
  111. package/dist/internal/extractor/adapters/typescript/parser.js.map +0 -1
  112. package/dist/internal/extractor/adapters/typescript/scanner.js.map +0 -1
  113. package/dist/internal/extractor/index.js.map +0 -1
  114. package/dist/internal/extractor/types.js.map +0 -1
  115. package/dist/internal/extractor/walker.js.map +0 -1
  116. package/dist/internal/ide-setup.js.map +0 -1
  117. package/dist/internal/orgs.js.map +0 -1
  118. package/dist/internal/project-binding.js.map +0 -1
  119. package/dist/internal/target-auth/api-config.js.map +0 -1
  120. package/dist/internal/target-auth/config.js.map +0 -1
  121. package/dist/internal/target-auth/index.js.map +0 -1
  122. package/dist/internal/target-auth/keychain.js.map +0 -1
  123. package/dist/internal/target-auth/types.js.map +0 -1
  124. package/dist/internal/target-auth/workos.js.map +0 -1
@@ -1,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" }, // dialect varies; gorm itself is most often pg
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,6 +1,2 @@
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 { Parser } from "web-tree-sitter";
6
2
  export declare function getGoParser(): Promise<Parser>;
@@ -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,5 +1,2 @@
1
- /**
2
- * Python language adapter. Same shape as TS/Go.
3
- */
4
1
  import type { LanguageAdapter } from "../../types.js";
5
2
  export declare const pythonAdapter: LanguageAdapter;
@@ -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" }, // dialect varies; default to pg
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)", // bedrock typically uses 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; // -e, -r, -c, --hash, etc.
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,5 +1,2 @@
1
- /**
2
- * tree-sitter-python loader.
3
- */
4
1
  import { Parser } from "web-tree-sitter";
5
2
  export declare function getPythonParser(): Promise<Parser>;
@@ -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"; // Flask default; verbs in methods= kwarg not yet parsed
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;