docorbit 0.1.0
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 +21 -0
- package/README.md +660 -0
- package/apps/cli/bin/docorbit.js +8 -0
- package/apps/cli/src/commands/add.ts +44 -0
- package/apps/cli/src/commands/api.ts +38 -0
- package/apps/cli/src/commands/context.ts +47 -0
- package/apps/cli/src/commands/dashboard.ts +55 -0
- package/apps/cli/src/commands/diff.ts +30 -0
- package/apps/cli/src/commands/evaluate.ts +133 -0
- package/apps/cli/src/commands/examples.ts +39 -0
- package/apps/cli/src/commands/export.ts +89 -0
- package/apps/cli/src/commands/impact.ts +31 -0
- package/apps/cli/src/commands/init.ts +69 -0
- package/apps/cli/src/commands/inspect.ts +30 -0
- package/apps/cli/src/commands/mcp.ts +72 -0
- package/apps/cli/src/commands/pitfalls.ts +38 -0
- package/apps/cli/src/commands/recipes.ts +35 -0
- package/apps/cli/src/commands/search.ts +48 -0
- package/apps/cli/src/commands/update.ts +73 -0
- package/apps/cli/src/commands/verify.ts +48 -0
- package/apps/cli/src/formatters/colors.ts +23 -0
- package/apps/cli/src/formatters/inspection.ts +102 -0
- package/apps/cli/src/formatters/knowledge.ts +272 -0
- package/apps/cli/src/formatters/retrieval.ts +74 -0
- package/apps/cli/src/formatters/terminal.ts +6 -0
- package/apps/cli/src/formatters/verification.ts +126 -0
- package/apps/cli/src/index.ts +409 -0
- package/bin/docorbit.js +8 -0
- package/package.json +46 -0
- package/packages/core/src/dashboard/server.ts +314 -0
- package/packages/core/src/dashboard/ui.ts +586 -0
- package/packages/core/src/implementation-service.ts +451 -0
- package/packages/core/src/index.ts +7 -0
- package/packages/core/src/inspector.ts +71 -0
- package/packages/core/src/pipeline.ts +331 -0
- package/packages/crawler/src/config.ts +12 -0
- package/packages/crawler/src/fetcher.ts +185 -0
- package/packages/crawler/src/index.ts +2 -0
- package/packages/discovery/src/index.ts +31 -0
- package/packages/discovery/src/provider.ts +47 -0
- package/packages/discovery/src/providers/generic.ts +98 -0
- package/packages/discovery/src/providers/github.ts +61 -0
- package/packages/discovery/src/providers/llms-txt.ts +73 -0
- package/packages/discovery/src/providers/markdown.ts +48 -0
- package/packages/discovery/src/providers/openapi.ts +91 -0
- package/packages/discovery/src/providers/sitemap.ts +62 -0
- package/packages/discovery/src/providers/skill.ts +54 -0
- package/packages/discovery/src/ranker.ts +123 -0
- package/packages/evaluation/src/dataset.ts +963 -0
- package/packages/evaluation/src/index.ts +8 -0
- package/packages/evaluation/src/runner.ts +241 -0
- package/packages/evaluation/src/strategies/context7-runner.ts +269 -0
- package/packages/evaluation/src/strategies/docorbit-runner.ts +228 -0
- package/packages/evaluation/src/strategies/firecrawl-runner.ts +172 -0
- package/packages/evaluation/src/strategies/web-search-runner.ts +194 -0
- package/packages/evaluation/src/types.ts +34 -0
- package/packages/evaluation/src/version-matcher.ts +73 -0
- package/packages/export/src/agents-md.ts +200 -0
- package/packages/export/src/claude-md.ts +141 -0
- package/packages/export/src/docs-map.ts +150 -0
- package/packages/export/src/index.ts +6 -0
- package/packages/export/src/llms-txt.ts +96 -0
- package/packages/export/src/service.ts +250 -0
- package/packages/export/src/skill-md.ts +128 -0
- package/packages/mcp/src/index.ts +46 -0
- package/packages/mcp/src/resources/index.ts +189 -0
- package/packages/mcp/src/server.ts +278 -0
- package/packages/mcp/src/tools/analyze-impact.ts +74 -0
- package/packages/mcp/src/tools/check-api.ts +86 -0
- package/packages/mcp/src/tools/diff-docs.ts +68 -0
- package/packages/mcp/src/tools/export-context.ts +73 -0
- package/packages/mcp/src/tools/find-api.ts +99 -0
- package/packages/mcp/src/tools/find-example.ts +100 -0
- package/packages/mcp/src/tools/find-pitfall.ts +94 -0
- package/packages/mcp/src/tools/find-recipe.ts +98 -0
- package/packages/mcp/src/tools/get-doc.ts +130 -0
- package/packages/mcp/src/tools/get-docs-map.ts +64 -0
- package/packages/mcp/src/tools/get-version.ts +118 -0
- package/packages/mcp/src/tools/implementation-context.ts +88 -0
- package/packages/mcp/src/tools/index.ts +59 -0
- package/packages/mcp/src/tools/list-sources.ts +85 -0
- package/packages/mcp/src/tools/search-docs.ts +123 -0
- package/packages/mcp/src/tools/types.ts +28 -0
- package/packages/mcp/src/transports/http.ts +256 -0
- package/packages/mcp/src/transports/stdio.ts +105 -0
- package/packages/mcp/src/transports/types.ts +6 -0
- package/packages/mcp/src/types.ts +102 -0
- package/packages/normalizer/src/example-indexer.ts +240 -0
- package/packages/normalizer/src/html.ts +253 -0
- package/packages/normalizer/src/index.ts +8 -0
- package/packages/normalizer/src/llms.ts +83 -0
- package/packages/normalizer/src/openapi/endpoint-parser.ts +406 -0
- package/packages/normalizer/src/openapi/schema-resolver.ts +111 -0
- package/packages/normalizer/src/openapi.ts +2 -0
- package/packages/normalizer/src/page.ts +184 -0
- package/packages/normalizer/src/pitfall-extractor.ts +190 -0
- package/packages/normalizer/src/slicer.ts +455 -0
- package/packages/retrieval/src/engine.ts +120 -0
- package/packages/retrieval/src/index.ts +7 -0
- package/packages/retrieval/src/intent.ts +43 -0
- package/packages/retrieval/src/packer.ts +145 -0
- package/packages/retrieval/src/recipe-engine.ts +313 -0
- package/packages/retrieval/src/scorer.ts +139 -0
- package/packages/retrieval/src/weights.ts +31 -0
- package/packages/security/src/annotations.ts +112 -0
- package/packages/security/src/index.ts +2 -0
- package/packages/security/src/ssrf.ts +153 -0
- package/packages/shared/src/errors.ts +53 -0
- package/packages/shared/src/hashing.ts +23 -0
- package/packages/shared/src/index.ts +3 -0
- package/packages/shared/src/types.ts +881 -0
- package/packages/storage/src/db.ts +72 -0
- package/packages/storage/src/index.ts +11 -0
- package/packages/storage/src/interfaces.ts +115 -0
- package/packages/storage/src/repositories/api-repository.ts +219 -0
- package/packages/storage/src/repositories/chunk-repository.ts +316 -0
- package/packages/storage/src/repositories/example-repository.ts +206 -0
- package/packages/storage/src/repositories/page-repository.ts +205 -0
- package/packages/storage/src/repositories/pitfall-repository.ts +188 -0
- package/packages/storage/src/repositories/source-repository.ts +205 -0
- package/packages/storage/src/repository.ts +256 -0
- package/packages/storage/src/schema.ts +269 -0
- package/packages/storage/src/search-tokens.ts +28 -0
- package/packages/verification/src/diff-engine.ts +258 -0
- package/packages/verification/src/extractor.ts +339 -0
- package/packages/verification/src/impact-scanner.ts +203 -0
- package/packages/verification/src/index.ts +5 -0
- package/packages/verification/src/services.ts +238 -0
- package/packages/verification/src/verifier.ts +375 -0
- package/packages/workspace/src/detector.ts +143 -0
- package/packages/workspace/src/ecosystems/cargo.ts +84 -0
- package/packages/workspace/src/ecosystems/composer.ts +42 -0
- package/packages/workspace/src/ecosystems/go.ts +54 -0
- package/packages/workspace/src/ecosystems/index.ts +34 -0
- package/packages/workspace/src/ecosystems/maven.ts +34 -0
- package/packages/workspace/src/ecosystems/npm.ts +83 -0
- package/packages/workspace/src/ecosystems/pub.ts +40 -0
- package/packages/workspace/src/ecosystems/pypi.ts +100 -0
- package/packages/workspace/src/ecosystems/rubygems.ts +30 -0
- package/packages/workspace/src/ecosystems/types.ts +18 -0
- package/packages/workspace/src/index.ts +5 -0
- package/packages/workspace/src/lockfile.ts +194 -0
- package/packages/workspace/src/resolver.ts +234 -0
- package/packages/workspace/src/semver.ts +259 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DocOrbitRepository,
|
|
3
|
+
} from '../../storage/src/index.ts';
|
|
4
|
+
import {
|
|
5
|
+
RetrievalEngine,
|
|
6
|
+
RecipeEngine,
|
|
7
|
+
detectQueryIntent,
|
|
8
|
+
} from '../../retrieval/src/index.ts';
|
|
9
|
+
import {
|
|
10
|
+
detectWorkspaceDependencies,
|
|
11
|
+
WorkspaceResolver,
|
|
12
|
+
} from '../../workspace/src/index.ts';
|
|
13
|
+
import {
|
|
14
|
+
estimateTokenCount,
|
|
15
|
+
} from '../../shared/src/index.ts';
|
|
16
|
+
import type {
|
|
17
|
+
QueryIntent,
|
|
18
|
+
Recipe,
|
|
19
|
+
ApiEndpoint,
|
|
20
|
+
IndexedExample,
|
|
21
|
+
Pitfall,
|
|
22
|
+
DocumentChunk,
|
|
23
|
+
SearchResult,
|
|
24
|
+
DocVersionMatch,
|
|
25
|
+
} from '../../shared/src/index.ts';
|
|
26
|
+
|
|
27
|
+
export interface ImplementationContextRequest {
|
|
28
|
+
task: string;
|
|
29
|
+
projectPath?: string;
|
|
30
|
+
library?: string;
|
|
31
|
+
version?: string;
|
|
32
|
+
tokenBudget?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ProjectResolutionInfo {
|
|
36
|
+
root: string;
|
|
37
|
+
matchedDependency?: {
|
|
38
|
+
name: string;
|
|
39
|
+
ecosystem: string;
|
|
40
|
+
requestedVersion: string;
|
|
41
|
+
resolvedVersion?: string;
|
|
42
|
+
};
|
|
43
|
+
versionResolution?: {
|
|
44
|
+
targetVersion: string;
|
|
45
|
+
confidence: number;
|
|
46
|
+
snapshotId?: string;
|
|
47
|
+
matchedBy?: string;
|
|
48
|
+
};
|
|
49
|
+
resolvedDependency?: string;
|
|
50
|
+
projectVersion?: string;
|
|
51
|
+
docVersion?: string;
|
|
52
|
+
confidence?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ProvenanceItem {
|
|
56
|
+
sourceUrl: string;
|
|
57
|
+
authority: string;
|
|
58
|
+
retrievedAt?: string;
|
|
59
|
+
untrusted: true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface VerificationHint {
|
|
63
|
+
endpoint: string;
|
|
64
|
+
method: string;
|
|
65
|
+
requiredParameters: string[];
|
|
66
|
+
requiredBodyFields: string[];
|
|
67
|
+
deprecated?: boolean;
|
|
68
|
+
warnings?: string[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ImplementationContextResult {
|
|
72
|
+
task: string;
|
|
73
|
+
detectedIntent: QueryIntent;
|
|
74
|
+
project?: ProjectResolutionInfo;
|
|
75
|
+
recipe: Recipe;
|
|
76
|
+
apiEndpoints: ApiEndpoint[];
|
|
77
|
+
examples: IndexedExample[];
|
|
78
|
+
pitfalls: Pitfall[];
|
|
79
|
+
chunks: DocumentChunk[];
|
|
80
|
+
totalEstimatedTokens: number;
|
|
81
|
+
tokenBudget: number;
|
|
82
|
+
markdown: string;
|
|
83
|
+
provenance: ProvenanceItem[];
|
|
84
|
+
verificationHints?: VerificationHint[];
|
|
85
|
+
untrusted: true;
|
|
86
|
+
untrustedContentNotice?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class ImplementationContextService {
|
|
90
|
+
private repo: DocOrbitRepository;
|
|
91
|
+
private retrievalEngine: RetrievalEngine;
|
|
92
|
+
private recipeEngine: RecipeEngine;
|
|
93
|
+
private workspaceResolver: WorkspaceResolver;
|
|
94
|
+
|
|
95
|
+
constructor(repo: DocOrbitRepository) {
|
|
96
|
+
this.repo = repo;
|
|
97
|
+
this.retrievalEngine = new RetrievalEngine(repo);
|
|
98
|
+
this.recipeEngine = new RecipeEngine(repo);
|
|
99
|
+
this.workspaceResolver = new WorkspaceResolver(repo);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Orchestrates the complete documentation intelligence pipeline for an agent task:
|
|
104
|
+
* task → project/dependency detection → version resolution → intent → retrieval →
|
|
105
|
+
* APIs → examples → pitfalls → recipe → token-budgeted context → provenance.
|
|
106
|
+
*/
|
|
107
|
+
async getContext(request: ImplementationContextRequest): Promise<ImplementationContextResult> {
|
|
108
|
+
const task = request.task.trim();
|
|
109
|
+
const tokenBudget = request.tokenBudget || 4000;
|
|
110
|
+
let targetVersion = request.version;
|
|
111
|
+
|
|
112
|
+
// 1. Intent Detection
|
|
113
|
+
const detectedIntent = detectQueryIntent(task);
|
|
114
|
+
|
|
115
|
+
// 2. Project Awareness & Version Resolution
|
|
116
|
+
let projectInfo: ProjectResolutionInfo | undefined;
|
|
117
|
+
|
|
118
|
+
if (request.projectPath) {
|
|
119
|
+
try {
|
|
120
|
+
const scan = detectWorkspaceDependencies(request.projectPath);
|
|
121
|
+
const resolution = this.workspaceResolver.resolveWorkspace(scan);
|
|
122
|
+
|
|
123
|
+
let matchedDep = undefined;
|
|
124
|
+
let versionRes = undefined;
|
|
125
|
+
|
|
126
|
+
// If library was specified, look for it specifically
|
|
127
|
+
if (request.library) {
|
|
128
|
+
const libLower = request.library.toLowerCase();
|
|
129
|
+
matchedDep = scan.dependencies.find(d => d.name.toLowerCase() === libLower);
|
|
130
|
+
const match = resolution.matches.find(m => m.dependency.name.toLowerCase() === libLower);
|
|
131
|
+
if (match && match.targetVersion) {
|
|
132
|
+
targetVersion = targetVersion || match.targetVersion;
|
|
133
|
+
versionRes = {
|
|
134
|
+
targetVersion: match.targetVersion,
|
|
135
|
+
confidence: match.confidence,
|
|
136
|
+
snapshotId: match.snapshotId,
|
|
137
|
+
matchedBy: match.matchedBy,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
// Find any relevant dependency mentioned in the task
|
|
142
|
+
const taskLower = task.toLowerCase();
|
|
143
|
+
for (const m of resolution.matches) {
|
|
144
|
+
if (taskLower.includes(m.dependency.name.toLowerCase()) && m.targetVersion) {
|
|
145
|
+
matchedDep = m.dependency;
|
|
146
|
+
targetVersion = targetVersion || m.targetVersion;
|
|
147
|
+
versionRes = {
|
|
148
|
+
targetVersion: m.targetVersion,
|
|
149
|
+
confidence: m.confidence,
|
|
150
|
+
snapshotId: m.snapshotId,
|
|
151
|
+
matchedBy: m.matchedBy,
|
|
152
|
+
};
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
projectInfo = {
|
|
159
|
+
root: request.projectPath,
|
|
160
|
+
matchedDependency: matchedDep ? {
|
|
161
|
+
name: matchedDep.name,
|
|
162
|
+
ecosystem: matchedDep.ecosystem,
|
|
163
|
+
requestedVersion: matchedDep.requestedVersion,
|
|
164
|
+
resolvedVersion: matchedDep.resolvedVersion,
|
|
165
|
+
} : undefined,
|
|
166
|
+
versionResolution: versionRes,
|
|
167
|
+
resolvedDependency: matchedDep?.name,
|
|
168
|
+
projectVersion: matchedDep?.requestedVersion,
|
|
169
|
+
docVersion: versionRes?.targetVersion,
|
|
170
|
+
confidence: versionRes?.confidence,
|
|
171
|
+
};
|
|
172
|
+
} catch {
|
|
173
|
+
// Continue if workspace scan fails
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 3. Structured Recipe Generation
|
|
178
|
+
const recipe = await this.recipeEngine.assembleRecipe(task, {
|
|
179
|
+
docVersion: targetVersion,
|
|
180
|
+
projectDir: request.projectPath,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// 4. API Endpoints
|
|
184
|
+
const apiEndpoints = this.repo.searchApiEndpoints(task, {
|
|
185
|
+
docVersion: targetVersion,
|
|
186
|
+
limit: 5,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// 5. Verified Examples
|
|
190
|
+
const examples = this.repo.searchIndexedExamples(task, {
|
|
191
|
+
docVersion: targetVersion,
|
|
192
|
+
limit: 5,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// 6. Pitfalls & Deprecations
|
|
196
|
+
const pitfalls = this.repo.searchPitfalls(task, {
|
|
197
|
+
docVersion: targetVersion,
|
|
198
|
+
limit: 5,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// 7. Targeted Chunk Retrieval
|
|
202
|
+
const searchResults = await this.retrievalEngine.search(task, {
|
|
203
|
+
docVersion: targetVersion,
|
|
204
|
+
limit: 8,
|
|
205
|
+
projectDir: request.projectPath,
|
|
206
|
+
});
|
|
207
|
+
const chunks = searchResults.map(r => r.chunk);
|
|
208
|
+
|
|
209
|
+
// 8. Compile Provenance & Untrusted Boundaries
|
|
210
|
+
const provenanceMap = new Map<string, ProvenanceItem>();
|
|
211
|
+
|
|
212
|
+
const recordProvenance = (url?: string, authority?: string, date?: string) => {
|
|
213
|
+
if (url && !provenanceMap.has(url)) {
|
|
214
|
+
provenanceMap.set(url, {
|
|
215
|
+
sourceUrl: url,
|
|
216
|
+
authority: authority || 'official',
|
|
217
|
+
retrievedAt: date,
|
|
218
|
+
untrusted: true,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
for (const c of chunks) {
|
|
224
|
+
if (c.provenance?.sourceUrl) {
|
|
225
|
+
recordProvenance(c.provenance.sourceUrl, undefined, c.provenance.fetchedAt);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
for (const ep of apiEndpoints) {
|
|
229
|
+
if (ep.provenance?.sourceUrl) {
|
|
230
|
+
recordProvenance(ep.provenance.sourceUrl, ep.provenance.sourceAuthority, ep.provenance.retrievedAt);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const ex of examples) {
|
|
234
|
+
if (ex.sourceUrl) {
|
|
235
|
+
recordProvenance(ex.sourceUrl, ex.sourceAuthority);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const pf of pitfalls) {
|
|
239
|
+
if (pf.provenance?.sourceUrl) {
|
|
240
|
+
recordProvenance(pf.provenance.sourceUrl, undefined, pf.provenance.retrievedAt);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (const src of recipe.sources) {
|
|
244
|
+
recordProvenance(src);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const provenanceList = Array.from(provenanceMap.values());
|
|
248
|
+
|
|
249
|
+
// 9. Extract Verification Hints (Schema contracts & required parameters)
|
|
250
|
+
const verificationHints: VerificationHint[] = apiEndpoints.map(ep => {
|
|
251
|
+
const requiredParameters = ep.parameters.filter(p => p.required).map(p => `${p.name} (${p.in})`);
|
|
252
|
+
const requiredBodyFields = ep.requestSchema?.required || [];
|
|
253
|
+
const warnings = pitfalls
|
|
254
|
+
.filter(p => p.relatedApi?.includes(ep.path) || p.content.includes(ep.path))
|
|
255
|
+
.map(p => `[${p.kind}] ${p.title}`);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
endpoint: ep.path,
|
|
259
|
+
method: ep.method.toUpperCase(),
|
|
260
|
+
requiredParameters,
|
|
261
|
+
requiredBodyFields,
|
|
262
|
+
deprecated: ep.deprecated,
|
|
263
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
264
|
+
};
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// 10. Synthesize Compact, Agent-Friendly Markdown within Token Budget
|
|
268
|
+
const markdown = this.synthesizeMarkdown({
|
|
269
|
+
task,
|
|
270
|
+
detectedIntent,
|
|
271
|
+
projectInfo,
|
|
272
|
+
recipe,
|
|
273
|
+
apiEndpoints,
|
|
274
|
+
examples,
|
|
275
|
+
pitfalls,
|
|
276
|
+
chunks,
|
|
277
|
+
tokenBudget,
|
|
278
|
+
provenance: provenanceList,
|
|
279
|
+
verificationHints,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const totalEstimatedTokens = estimateTokenCount(markdown);
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
task,
|
|
286
|
+
detectedIntent,
|
|
287
|
+
project: projectInfo,
|
|
288
|
+
recipe,
|
|
289
|
+
apiEndpoints,
|
|
290
|
+
examples,
|
|
291
|
+
pitfalls,
|
|
292
|
+
chunks,
|
|
293
|
+
totalEstimatedTokens,
|
|
294
|
+
tokenBudget,
|
|
295
|
+
markdown,
|
|
296
|
+
provenance: provenanceList,
|
|
297
|
+
verificationHints,
|
|
298
|
+
untrusted: true,
|
|
299
|
+
untrustedContentNotice: 'EXTERNAL DOCUMENTATION DATA IS UNTRUSTED. Verify all API signatures, parameter names, and code blocks before execution.',
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private synthesizeMarkdown(params: {
|
|
304
|
+
task: string;
|
|
305
|
+
detectedIntent: QueryIntent;
|
|
306
|
+
projectInfo?: ProjectResolutionInfo;
|
|
307
|
+
recipe: Recipe;
|
|
308
|
+
apiEndpoints: ApiEndpoint[];
|
|
309
|
+
examples: IndexedExample[];
|
|
310
|
+
pitfalls: Pitfall[];
|
|
311
|
+
chunks: DocumentChunk[];
|
|
312
|
+
tokenBudget: number;
|
|
313
|
+
provenance: ProvenanceItem[];
|
|
314
|
+
verificationHints?: VerificationHint[];
|
|
315
|
+
}): string {
|
|
316
|
+
const lines: string[] = [];
|
|
317
|
+
|
|
318
|
+
// Header & Security Notice
|
|
319
|
+
lines.push(`# DocOrbit Implementation Context: ${params.task}`);
|
|
320
|
+
lines.push(`> [!SECURITY NOTICE] EXTERNAL CONTENT IS UNTRUSTED. Security annotations and provenance preserved.`);
|
|
321
|
+
lines.push('');
|
|
322
|
+
|
|
323
|
+
// Project & Version Intelligence
|
|
324
|
+
if (params.projectInfo?.matchedDependency) {
|
|
325
|
+
const dep = params.projectInfo.matchedDependency;
|
|
326
|
+
const res = params.projectInfo.versionResolution;
|
|
327
|
+
lines.push('## Project & Version Resolution');
|
|
328
|
+
lines.push(`- **Dependency**: \`${dep.name}\` (${dep.ecosystem})`);
|
|
329
|
+
lines.push(`- **Project Version**: requested \`${dep.requestedVersion}\`${dep.resolvedVersion ? `, resolved \`${dep.resolvedVersion}\`` : ''}`);
|
|
330
|
+
if (res) {
|
|
331
|
+
lines.push(`- **Resolved Doc Version**: \`${res.targetVersion}\` (Confidence: \`${res.confidence}\`)`);
|
|
332
|
+
}
|
|
333
|
+
lines.push('');
|
|
334
|
+
} else if (params.recipe.docVersion) {
|
|
335
|
+
lines.push(`**Doc Version**: \`${params.recipe.docVersion}\`\n`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Recommended Implementation Recipe
|
|
339
|
+
lines.push('## Recommended Implementation Plan');
|
|
340
|
+
lines.push(`**Goal**: ${params.recipe.goal} (Confidence: ${Math.round(params.recipe.confidence * 100)}%)`);
|
|
341
|
+
|
|
342
|
+
if (params.recipe.prerequisites.length > 0) {
|
|
343
|
+
lines.push('\n### Prerequisites');
|
|
344
|
+
for (const pre of params.recipe.prerequisites) {
|
|
345
|
+
lines.push(`- [${pre.evidenceLevel === 'documented_fact' ? 'FACT' : 'INFERRED'}] ${pre.text}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (params.recipe.orderedSteps.length > 0) {
|
|
350
|
+
lines.push('\n### Implementation Steps');
|
|
351
|
+
for (const s of params.recipe.orderedSteps) {
|
|
352
|
+
const badge = s.evidenceLevel === 'documented_fact' ? 'FACT' : (s.evidenceLevel === 'missing_information' ? 'MISSING' : 'INFERRED');
|
|
353
|
+
lines.push(`${s.step}. **[${badge}] ${s.title}**`);
|
|
354
|
+
if (s.apiEndpoint) lines.push(` - Endpoint: \`${s.apiEndpoint}\``);
|
|
355
|
+
lines.push(` - ${s.description}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (params.recipe.validationSteps.length > 0) {
|
|
360
|
+
lines.push('\n### Validation & Verification');
|
|
361
|
+
for (const vs of params.recipe.validationSteps) {
|
|
362
|
+
lines.push(`${vs.step}. **[FACT]** ${vs.description}`);
|
|
363
|
+
if (vs.expectedResponse) lines.push(` - Expected: \`${vs.expectedResponse}\``);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
lines.push('');
|
|
367
|
+
|
|
368
|
+
// Structured Required APIs
|
|
369
|
+
if (params.apiEndpoints.length > 0) {
|
|
370
|
+
lines.push('## Required API Endpoints');
|
|
371
|
+
for (const ep of params.apiEndpoints.slice(0, 3)) {
|
|
372
|
+
lines.push(`### \`${ep.method.toUpperCase()} ${ep.path}\`${ep.deprecated ? ' (DEPRECATED)' : ''}`);
|
|
373
|
+
if (ep.summary) lines.push(`*${ep.summary}*`);
|
|
374
|
+
if (ep.parameters.length > 0) {
|
|
375
|
+
const req = ep.parameters.filter(p => p.required).map(p => `\`${p.name}\` (${p.in}:${p.type || 'any'})`).join(', ');
|
|
376
|
+
const opt = ep.parameters.filter(p => !p.required).map(p => `\`${p.name}\``).join(', ');
|
|
377
|
+
if (req) lines.push(`- **Required Parameters**: ${req}`);
|
|
378
|
+
if (opt) lines.push(`- **Optional Parameters**: ${opt}`);
|
|
379
|
+
}
|
|
380
|
+
if (ep.auth.length > 0) {
|
|
381
|
+
lines.push(`- **Auth**: ${ep.auth.map(a => `${a.type}${a.scheme ? `:${a.scheme}` : ''}`).join(', ')}`);
|
|
382
|
+
}
|
|
383
|
+
if (ep.pagination) {
|
|
384
|
+
lines.push(`- **Pagination**: ${ep.pagination.type} (${ep.pagination.parameters.join(', ')})`);
|
|
385
|
+
}
|
|
386
|
+
lines.push('');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Best Code Examples
|
|
391
|
+
if (params.examples.length > 0) {
|
|
392
|
+
lines.push('## Verified Code Examples');
|
|
393
|
+
for (const ex of params.examples.slice(0, 2)) {
|
|
394
|
+
lines.push(`### ${ex.task} (${ex.language}${ex.framework ? `, ${ex.framework}` : ''})`);
|
|
395
|
+
lines.push('```' + ex.language);
|
|
396
|
+
lines.push(ex.code);
|
|
397
|
+
lines.push('```\n');
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Pitfalls, Gotchas & Deprecations
|
|
402
|
+
if (params.pitfalls.length > 0) {
|
|
403
|
+
lines.push('## Pitfalls & Warnings');
|
|
404
|
+
for (const pf of params.pitfalls.slice(0, 4)) {
|
|
405
|
+
lines.push(`- ⚠️ **[${pf.kind.toUpperCase()}] ${pf.title}**: ${pf.content}`);
|
|
406
|
+
}
|
|
407
|
+
lines.push('');
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Verification Constraints & Schema Contracts
|
|
411
|
+
if (params.verificationHints && params.verificationHints.length > 0) {
|
|
412
|
+
lines.push('## Verification Constraints & Schema Contracts');
|
|
413
|
+
for (const hint of params.verificationHints.slice(0, 3)) {
|
|
414
|
+
lines.push(`- **\`${hint.method} ${hint.endpoint}\`**${hint.deprecated ? ' (DEPRECATED)' : ''}`);
|
|
415
|
+
if (hint.requiredParameters.length > 0) {
|
|
416
|
+
lines.push(` - *Required Query/Path Params*: \`${hint.requiredParameters.join('`, `')}\``);
|
|
417
|
+
}
|
|
418
|
+
if (hint.requiredBodyFields.length > 0) {
|
|
419
|
+
lines.push(` - *Required Body Fields*: \`${hint.requiredBodyFields.join('`, `')}\``);
|
|
420
|
+
}
|
|
421
|
+
if (hint.warnings && hint.warnings.length > 0) {
|
|
422
|
+
for (const w of hint.warnings) {
|
|
423
|
+
lines.push(` - ⚠️ ${w}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
lines.push('');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Sources & Provenance
|
|
431
|
+
if (params.provenance.length > 0) {
|
|
432
|
+
lines.push('## Documentation Sources & Provenance');
|
|
433
|
+
for (const prov of params.provenance) {
|
|
434
|
+
lines.push(`- \`${prov.authority}\`: ${prov.sourceUrl} (untrusted)`);
|
|
435
|
+
}
|
|
436
|
+
lines.push('');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Budget enforcement
|
|
440
|
+
let fullText = lines.join('\n');
|
|
441
|
+
let tokens = estimateTokenCount(fullText);
|
|
442
|
+
|
|
443
|
+
if (tokens > params.tokenBudget) {
|
|
444
|
+
// Trim examples or documentation chunks to fit strictly in budget
|
|
445
|
+
const truncated = fullText.slice(0, Math.floor(params.tokenBudget * 3.5)) + '\n\n... [Context truncated to fit token budget]';
|
|
446
|
+
return truncated;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return fullText;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DiscoveredSource,
|
|
3
|
+
SourcePurpose,
|
|
4
|
+
SourceRankResult,
|
|
5
|
+
Target,
|
|
6
|
+
} from '../../shared/src/index.ts';
|
|
7
|
+
import { validateTargetUrl } from '../../security/src/index.ts';
|
|
8
|
+
import { SecureFetcher } from '../../crawler/src/index.ts';
|
|
9
|
+
import { createDefaultDiscoveryCoordinator, rankSources } from '../../discovery/src/index.ts';
|
|
10
|
+
|
|
11
|
+
export interface InspectionReport {
|
|
12
|
+
targetUrl: string;
|
|
13
|
+
target?: Target;
|
|
14
|
+
sourcesDiscovered: DiscoveredSource[];
|
|
15
|
+
recommendations: Record<SourcePurpose, SourceRankResult>;
|
|
16
|
+
summary: {
|
|
17
|
+
totalFound: number;
|
|
18
|
+
machineReadableCount: number;
|
|
19
|
+
officialCount: number;
|
|
20
|
+
hasOpenApi: boolean;
|
|
21
|
+
hasLlmsTxt: boolean;
|
|
22
|
+
hasLlmsFullTxt: boolean;
|
|
23
|
+
hasSkill: boolean;
|
|
24
|
+
hasSitemap: boolean;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function inspectDocumentation(
|
|
29
|
+
targetUrl: string,
|
|
30
|
+
options: { allowLocalhostForTesting?: boolean; fetcher?: SecureFetcher } = {}
|
|
31
|
+
): Promise<InspectionReport> {
|
|
32
|
+
// Validate target URL against SSRF and illegal protocols before probing
|
|
33
|
+
await validateTargetUrl(targetUrl, {
|
|
34
|
+
allowLocalhostForTesting: options.allowLocalhostForTesting,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fetcher = options.fetcher || new SecureFetcher({ allowLocalhostForTesting: options.allowLocalhostForTesting });
|
|
38
|
+
const coordinator = createDefaultDiscoveryCoordinator();
|
|
39
|
+
|
|
40
|
+
const discovered = await coordinator.discoverAll(targetUrl, fetcher);
|
|
41
|
+
|
|
42
|
+
const purposes: SourcePurpose[] = ['navigation', 'conceptual', 'api', 'examples', 'implementation'];
|
|
43
|
+
const recommendations: Partial<Record<SourcePurpose, SourceRankResult>> = {};
|
|
44
|
+
|
|
45
|
+
for (const p of purposes) {
|
|
46
|
+
recommendations[p] = rankSources(discovered, p);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const validSources = discovered.filter(s => s.status === 'valid');
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
targetUrl,
|
|
53
|
+
target: {
|
|
54
|
+
type: 'url',
|
|
55
|
+
value: targetUrl,
|
|
56
|
+
normalizedUrl: targetUrl,
|
|
57
|
+
},
|
|
58
|
+
sourcesDiscovered: discovered,
|
|
59
|
+
recommendations: recommendations as Record<SourcePurpose, SourceRankResult>,
|
|
60
|
+
summary: {
|
|
61
|
+
totalFound: discovered.length,
|
|
62
|
+
machineReadableCount: validSources.filter(s => s.machineReadable).length,
|
|
63
|
+
officialCount: validSources.filter(s => s.authority === 'official').length,
|
|
64
|
+
hasOpenApi: validSources.some(s => s.type === 'openapi'),
|
|
65
|
+
hasLlmsTxt: validSources.some(s => s.type === 'llms_txt'),
|
|
66
|
+
hasLlmsFullTxt: validSources.some(s => s.type === 'llms_full_txt'),
|
|
67
|
+
hasSkill: validSources.some(s => s.type === 'skill'),
|
|
68
|
+
hasSitemap: validSources.some(s => s.type === 'sitemap'),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|