@lota-sdk/core 0.1.5
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/infrastructure/schema/00_workstream.surql +55 -0
- package/infrastructure/schema/01_memory.surql +47 -0
- package/infrastructure/schema/02_execution_plan.surql +62 -0
- package/infrastructure/schema/03_learned_skill.surql +32 -0
- package/infrastructure/schema/04_runtime_bootstrap.surql +8 -0
- package/package.json +128 -0
- package/src/ai/definitions.ts +308 -0
- package/src/bifrost/bifrost.ts +256 -0
- package/src/config/agent-defaults.ts +99 -0
- package/src/config/constants.ts +33 -0
- package/src/config/env-shapes.ts +122 -0
- package/src/config/logger.ts +29 -0
- package/src/config/model-constants.ts +31 -0
- package/src/config/search.ts +17 -0
- package/src/config/workstream-defaults.ts +68 -0
- package/src/db/base.service.ts +55 -0
- package/src/db/cursor-pagination.ts +73 -0
- package/src/db/memory-query-builder.ts +207 -0
- package/src/db/memory-store.helpers.ts +118 -0
- package/src/db/memory-store.rows.ts +29 -0
- package/src/db/memory-store.ts +974 -0
- package/src/db/memory-types.ts +193 -0
- package/src/db/memory.ts +505 -0
- package/src/db/record-id.ts +78 -0
- package/src/db/service.ts +932 -0
- package/src/db/startup.ts +152 -0
- package/src/db/tables.ts +20 -0
- package/src/document/org-document-chunking.ts +224 -0
- package/src/document/parsing.ts +40 -0
- package/src/embeddings/provider.ts +76 -0
- package/src/index.ts +302 -0
- package/src/queues/context-compaction.queue.ts +82 -0
- package/src/queues/document-processor.queue.ts +118 -0
- package/src/queues/memory-consolidation.queue.ts +65 -0
- package/src/queues/post-chat-memory.queue.ts +128 -0
- package/src/queues/recent-activity-title-refinement.queue.ts +69 -0
- package/src/queues/regular-chat-memory-digest.config.ts +12 -0
- package/src/queues/regular-chat-memory-digest.queue.ts +73 -0
- package/src/queues/skill-extraction.config.ts +9 -0
- package/src/queues/skill-extraction.queue.ts +62 -0
- package/src/redis/connection.ts +176 -0
- package/src/redis/index.ts +30 -0
- package/src/redis/org-memory-lock.ts +43 -0
- package/src/redis/redis-lease-lock.ts +158 -0
- package/src/runtime/agent-contract.ts +1 -0
- package/src/runtime/agent-prompt-context.ts +119 -0
- package/src/runtime/agent-runtime-policy.ts +192 -0
- package/src/runtime/agent-stream-helpers.ts +117 -0
- package/src/runtime/agent-types.ts +22 -0
- package/src/runtime/approval-continuation.ts +16 -0
- package/src/runtime/chat-attachments.ts +46 -0
- package/src/runtime/chat-message.ts +10 -0
- package/src/runtime/chat-request-routing.ts +21 -0
- package/src/runtime/chat-run-orchestration.ts +25 -0
- package/src/runtime/chat-run-registry.ts +20 -0
- package/src/runtime/chat-types.ts +18 -0
- package/src/runtime/context-compaction-constants.ts +11 -0
- package/src/runtime/context-compaction-runtime.ts +86 -0
- package/src/runtime/context-compaction.ts +909 -0
- package/src/runtime/execution-plan.ts +59 -0
- package/src/runtime/helper-model.ts +405 -0
- package/src/runtime/indexed-repositories-policy.ts +28 -0
- package/src/runtime/instruction-sections.ts +8 -0
- package/src/runtime/llm-content.ts +71 -0
- package/src/runtime/memory-block.ts +264 -0
- package/src/runtime/memory-digest-policy.ts +14 -0
- package/src/runtime/memory-format.ts +8 -0
- package/src/runtime/memory-pipeline.ts +570 -0
- package/src/runtime/memory-prompts-fact.ts +47 -0
- package/src/runtime/memory-prompts-parse.ts +3 -0
- package/src/runtime/memory-prompts-update.ts +37 -0
- package/src/runtime/memory-scope.ts +43 -0
- package/src/runtime/plugin-types.ts +10 -0
- package/src/runtime/retrieval-adapters.ts +25 -0
- package/src/runtime/retrieval-pipeline.ts +3 -0
- package/src/runtime/runtime-extensions.ts +154 -0
- package/src/runtime/skill-extraction-policy.ts +3 -0
- package/src/runtime/team-consultation-orchestrator.ts +245 -0
- package/src/runtime/team-consultation-prompts.ts +32 -0
- package/src/runtime/title-helpers.ts +12 -0
- package/src/runtime/turn-lifecycle.ts +28 -0
- package/src/runtime/workstream-chat-helpers.ts +187 -0
- package/src/runtime/workstream-routing-policy.ts +301 -0
- package/src/runtime/workstream-state.ts +261 -0
- package/src/services/attachment.service.ts +159 -0
- package/src/services/chat-attachments.service.ts +17 -0
- package/src/services/chat-run-registry.service.ts +3 -0
- package/src/services/context-compaction-runtime.ts +13 -0
- package/src/services/context-compaction.service.ts +115 -0
- package/src/services/document-chunk.service.ts +141 -0
- package/src/services/execution-plan.service.ts +890 -0
- package/src/services/learned-skill.service.ts +328 -0
- package/src/services/memory-assessment.service.ts +43 -0
- package/src/services/memory.service.ts +807 -0
- package/src/services/memory.utils.ts +84 -0
- package/src/services/mutating-approval.service.ts +110 -0
- package/src/services/recent-activity-title.service.ts +74 -0
- package/src/services/recent-activity.service.ts +397 -0
- package/src/services/workstream-change-tracker.service.ts +313 -0
- package/src/services/workstream-message.service.ts +283 -0
- package/src/services/workstream-title.service.ts +58 -0
- package/src/services/workstream-turn-preparation.ts +1340 -0
- package/src/services/workstream-turn.ts +37 -0
- package/src/services/workstream.service.ts +854 -0
- package/src/services/workstream.types.ts +118 -0
- package/src/storage/attachment-parser.ts +101 -0
- package/src/storage/attachment-storage.service.ts +391 -0
- package/src/storage/attachments.types.ts +11 -0
- package/src/storage/attachments.utils.ts +58 -0
- package/src/storage/generated-document-storage.service.ts +55 -0
- package/src/system-agents/agent-result.ts +27 -0
- package/src/system-agents/context-compacter.agent.ts +46 -0
- package/src/system-agents/delegated-agent-factory.ts +177 -0
- package/src/system-agents/helper-agent-options.ts +20 -0
- package/src/system-agents/memory-reranker.agent.ts +38 -0
- package/src/system-agents/memory.agent.ts +58 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +53 -0
- package/src/system-agents/regular-chat-memory-digest.agent.ts +75 -0
- package/src/system-agents/researcher.agent.ts +34 -0
- package/src/system-agents/skill-extractor.agent.ts +88 -0
- package/src/system-agents/skill-manager.agent.ts +80 -0
- package/src/system-agents/title-generator.agent.ts +42 -0
- package/src/system-agents/workstream-tracker.agent.ts +58 -0
- package/src/tools/execution-plan.tool.ts +163 -0
- package/src/tools/fetch-webpage.tool.ts +132 -0
- package/src/tools/firecrawl-client.ts +12 -0
- package/src/tools/memory-block.tool.ts +55 -0
- package/src/tools/read-file-parts.tool.ts +80 -0
- package/src/tools/remember-memory.tool.ts +85 -0
- package/src/tools/research-topic.tool.ts +15 -0
- package/src/tools/search-tools.ts +55 -0
- package/src/tools/search-web.tool.ts +175 -0
- package/src/tools/team-think.tool.ts +125 -0
- package/src/tools/tool-contract.ts +21 -0
- package/src/tools/user-questions.tool.ts +18 -0
- package/src/utils/async.ts +50 -0
- package/src/utils/date-time.ts +34 -0
- package/src/utils/error.ts +10 -0
- package/src/utils/errors.ts +28 -0
- package/src/utils/hono-error-handler.ts +71 -0
- package/src/utils/string.ts +51 -0
- package/src/workers/bootstrap.ts +44 -0
- package/src/workers/memory-consolidation.worker.ts +318 -0
- package/src/workers/regular-chat-memory-digest.helpers.ts +100 -0
- package/src/workers/regular-chat-memory-digest.runner.ts +363 -0
- package/src/workers/regular-chat-memory-digest.worker.ts +22 -0
- package/src/workers/skill-extraction.runner.ts +331 -0
- package/src/workers/skill-extraction.worker.ts +22 -0
- package/src/workers/utils/repo-indexer-chunker.ts +331 -0
- package/src/workers/utils/repo-structure-extractor.ts +645 -0
- package/src/workers/utils/repomix-process-concurrency.ts +65 -0
- package/src/workers/utils/sandbox-error.ts +5 -0
- package/src/workers/worker-utils.ts +182 -0
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
RepositoryStructureArtifactSchema,
|
|
6
|
+
RepositoryStructureSummarySchema,
|
|
7
|
+
} from '@lota-sdk/shared/schemas/repository-structure'
|
|
8
|
+
import type {
|
|
9
|
+
RepositoryStructureArtifact,
|
|
10
|
+
RepositoryStructureComponent,
|
|
11
|
+
RepositoryStructureShape,
|
|
12
|
+
RepositoryStructureSignal,
|
|
13
|
+
RepositoryStructureSummary,
|
|
14
|
+
} from '@lota-sdk/shared/schemas/repository-structure'
|
|
15
|
+
|
|
16
|
+
const EXTRACTOR_VERSION = 'repository-structure-extractor.v1'
|
|
17
|
+
const IGNORED_DIR_NAMES = new Set([
|
|
18
|
+
'.git',
|
|
19
|
+
'.idea',
|
|
20
|
+
'.next',
|
|
21
|
+
'.turbo',
|
|
22
|
+
'.vscode',
|
|
23
|
+
'build',
|
|
24
|
+
'coverage',
|
|
25
|
+
'dist',
|
|
26
|
+
'node_modules',
|
|
27
|
+
'target',
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
const LANGUAGE_BY_EXTENSION: Record<string, string> = {
|
|
31
|
+
'.cjs': 'JavaScript',
|
|
32
|
+
'.cpp': 'C++',
|
|
33
|
+
'.cs': 'C#',
|
|
34
|
+
'.go': 'Go',
|
|
35
|
+
'.java': 'Java',
|
|
36
|
+
'.js': 'JavaScript',
|
|
37
|
+
'.jsx': 'React JSX',
|
|
38
|
+
'.kt': 'Kotlin',
|
|
39
|
+
'.md': 'Markdown',
|
|
40
|
+
'.mjs': 'JavaScript',
|
|
41
|
+
'.php': 'PHP',
|
|
42
|
+
'.py': 'Python',
|
|
43
|
+
'.rb': 'Ruby',
|
|
44
|
+
'.rs': 'Rust',
|
|
45
|
+
'.sql': 'SQL',
|
|
46
|
+
'.swift': 'Swift',
|
|
47
|
+
'.ts': 'TypeScript',
|
|
48
|
+
'.tsx': 'React TSX',
|
|
49
|
+
'.vue': 'Vue',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type PackageJson = {
|
|
53
|
+
name?: string
|
|
54
|
+
private?: boolean
|
|
55
|
+
scripts?: Record<string, string>
|
|
56
|
+
dependencies?: Record<string, string>
|
|
57
|
+
devDependencies?: Record<string, string>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type ExtractParams = {
|
|
61
|
+
rootDir: string
|
|
62
|
+
repositoryKey: string
|
|
63
|
+
repositoryFullName: string
|
|
64
|
+
defaultBranch: string
|
|
65
|
+
indexBranch: string
|
|
66
|
+
commitSha: string | undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type ExtractResult = {
|
|
70
|
+
repoStructure: RepositoryStructureArtifact
|
|
71
|
+
structureSummary: RepositoryStructureSummary
|
|
72
|
+
structureMarkdown: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function normalizePath(value: string): string {
|
|
76
|
+
return value.replaceAll(path.sep, '/')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function unique<T>(values: T[]): T[] {
|
|
80
|
+
return [...new Set(values)]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function truncate<T>(values: T[], limit: number): T[] {
|
|
84
|
+
return values.slice(0, limit)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function normalizeEvidencePaths(values: string[]): string[] {
|
|
88
|
+
return truncate(unique(values), 20)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function createSignal(params: {
|
|
92
|
+
key: string
|
|
93
|
+
label: string
|
|
94
|
+
reason: string
|
|
95
|
+
evidencePaths?: string[]
|
|
96
|
+
}): RepositoryStructureSignal {
|
|
97
|
+
return {
|
|
98
|
+
key: params.key,
|
|
99
|
+
label: params.label,
|
|
100
|
+
reason: params.reason,
|
|
101
|
+
evidencePaths: normalizeEvidencePaths(params.evidencePaths ?? []),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function collectRelativeFilePaths(rootDir: string, currentDir = ''): Promise<string[]> {
|
|
106
|
+
const absoluteDir = path.join(rootDir, currentDir)
|
|
107
|
+
const entries = await readdir(absoluteDir, { withFileTypes: true })
|
|
108
|
+
const filePaths: string[] = []
|
|
109
|
+
|
|
110
|
+
for (const entry of entries) {
|
|
111
|
+
const relativePath = normalizePath(path.join(currentDir, entry.name))
|
|
112
|
+
if (entry.isDirectory()) {
|
|
113
|
+
if (IGNORED_DIR_NAMES.has(entry.name)) continue
|
|
114
|
+
filePaths.push(...(await collectRelativeFilePaths(rootDir, relativePath)))
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
filePaths.push(relativePath)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return filePaths
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function readPackageJson(rootDir: string, relativePath: string): Promise<PackageJson | null> {
|
|
124
|
+
try {
|
|
125
|
+
const raw = await readFile(path.join(rootDir, relativePath), 'utf8')
|
|
126
|
+
return JSON.parse(raw) as PackageJson
|
|
127
|
+
} catch {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function gatherTopLevelEntries(filePaths: string[]): string[] {
|
|
133
|
+
return truncate(
|
|
134
|
+
[...new Set(filePaths.map((filePath) => filePath.split('/')[0]).filter((entry) => entry && entry !== ''))].sort(),
|
|
135
|
+
200,
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function gatherLanguageSignals(filePaths: string[]): RepositoryStructureSignal[] {
|
|
140
|
+
const languageEvidence = new Map<string, string[]>()
|
|
141
|
+
|
|
142
|
+
for (const filePath of filePaths) {
|
|
143
|
+
const language = LANGUAGE_BY_EXTENSION[path.extname(filePath).toLowerCase()]
|
|
144
|
+
if (!language) continue
|
|
145
|
+
const entries = languageEvidence.get(language) ?? []
|
|
146
|
+
entries.push(filePath)
|
|
147
|
+
languageEvidence.set(language, entries)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return [...languageEvidence.entries()].map(([language, evidencePaths]) =>
|
|
151
|
+
createSignal({
|
|
152
|
+
key: language.toLowerCase().replaceAll(/\s+/g, '-'),
|
|
153
|
+
label: language,
|
|
154
|
+
reason: `Detected ${language} source files in the repository.`,
|
|
155
|
+
evidencePaths,
|
|
156
|
+
}),
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function findPaths(filePaths: string[], matcher: (filePath: string) => boolean): string[] {
|
|
161
|
+
return filePaths.filter(matcher)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildPathSignals(params: {
|
|
165
|
+
keyPrefix: string
|
|
166
|
+
label: string
|
|
167
|
+
reason: string
|
|
168
|
+
filePaths: string[]
|
|
169
|
+
}): RepositoryStructureSignal[] {
|
|
170
|
+
return params.filePaths.map((filePath, index) =>
|
|
171
|
+
createSignal({
|
|
172
|
+
key: `${params.keyPrefix}-${index + 1}`,
|
|
173
|
+
label: filePath,
|
|
174
|
+
reason: params.reason,
|
|
175
|
+
evidencePaths: [filePath],
|
|
176
|
+
}),
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function inferFrameworkSignals(packageJsons: Array<{ path: string; data: PackageJson }>): RepositoryStructureSignal[] {
|
|
181
|
+
const dependencyMap = new Map<string, { label: string; reason: string; evidencePaths: string[] }>()
|
|
182
|
+
|
|
183
|
+
const register = (dependencyName: string, label: string, reason: string, evidencePath: string) => {
|
|
184
|
+
const current = dependencyMap.get(dependencyName) ?? { label, reason, evidencePaths: [] }
|
|
185
|
+
current.evidencePaths.push(evidencePath)
|
|
186
|
+
dependencyMap.set(dependencyName, current)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const packageJson of packageJsons) {
|
|
190
|
+
const dependencyNames = [
|
|
191
|
+
...Object.keys(packageJson.data.dependencies ?? {}),
|
|
192
|
+
...Object.keys(packageJson.data.devDependencies ?? {}),
|
|
193
|
+
]
|
|
194
|
+
for (const dependencyName of dependencyNames) {
|
|
195
|
+
if (dependencyName === 'next') {
|
|
196
|
+
register('next', 'Next.js', 'Detected the Next.js framework dependency.', packageJson.path)
|
|
197
|
+
}
|
|
198
|
+
if (dependencyName === 'react') {
|
|
199
|
+
register('react', 'React', 'Detected the React dependency.', packageJson.path)
|
|
200
|
+
}
|
|
201
|
+
if (dependencyName === 'vite') {
|
|
202
|
+
register('vite', 'Vite', 'Detected the Vite build tool dependency.', packageJson.path)
|
|
203
|
+
}
|
|
204
|
+
if (dependencyName === 'hono') {
|
|
205
|
+
register('hono', 'Hono', 'Detected the Hono server framework dependency.', packageJson.path)
|
|
206
|
+
}
|
|
207
|
+
if (dependencyName === 'express') {
|
|
208
|
+
register('express', 'Express', 'Detected the Express server framework dependency.', packageJson.path)
|
|
209
|
+
}
|
|
210
|
+
if (dependencyName === '@tanstack/react-router') {
|
|
211
|
+
register(
|
|
212
|
+
'tanstack-router',
|
|
213
|
+
'TanStack Router',
|
|
214
|
+
'Detected TanStack Router in package dependencies.',
|
|
215
|
+
packageJson.path,
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
if (dependencyName === 'tailwindcss') {
|
|
219
|
+
register('tailwindcss', 'Tailwind CSS', 'Detected Tailwind CSS in package dependencies.', packageJson.path)
|
|
220
|
+
}
|
|
221
|
+
if (dependencyName === '@better-auth/better-auth' || dependencyName === 'better-auth') {
|
|
222
|
+
register('better-auth', 'Better Auth', 'Detected Better Auth in package dependencies.', packageJson.path)
|
|
223
|
+
}
|
|
224
|
+
if (dependencyName === 'bullmq') {
|
|
225
|
+
register('bullmq', 'BullMQ', 'Detected BullMQ job processing.', packageJson.path)
|
|
226
|
+
}
|
|
227
|
+
if (dependencyName === 'prisma') {
|
|
228
|
+
register('prisma', 'Prisma', 'Detected Prisma ORM.', packageJson.path)
|
|
229
|
+
}
|
|
230
|
+
if (dependencyName === 'surrealdb') {
|
|
231
|
+
register('surrealdb', 'SurrealDB', 'Detected SurrealDB client usage.', packageJson.path)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return [...dependencyMap.entries()].map(([dependencyName, value]) =>
|
|
237
|
+
createSignal({ key: dependencyName, label: value.label, reason: value.reason, evidencePaths: value.evidencePaths }),
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function inferPackageManagerSignals(filePaths: string[]): RepositoryStructureSignal[] {
|
|
242
|
+
const signals: RepositoryStructureSignal[] = []
|
|
243
|
+
if (filePaths.includes('bun.lock') || filePaths.includes('bun.lockb')) {
|
|
244
|
+
signals.push(
|
|
245
|
+
createSignal({ key: 'bun', label: 'Bun', reason: 'Detected Bun lockfile.', evidencePaths: ['bun.lock'] }),
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
if (filePaths.includes('package-lock.json')) {
|
|
249
|
+
signals.push(
|
|
250
|
+
createSignal({
|
|
251
|
+
key: 'npm',
|
|
252
|
+
label: 'npm',
|
|
253
|
+
reason: 'Detected npm lockfile.',
|
|
254
|
+
evidencePaths: ['package-lock.json'],
|
|
255
|
+
}),
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
if (filePaths.includes('pnpm-lock.yaml')) {
|
|
259
|
+
signals.push(
|
|
260
|
+
createSignal({
|
|
261
|
+
key: 'pnpm',
|
|
262
|
+
label: 'pnpm',
|
|
263
|
+
reason: 'Detected pnpm lockfile.',
|
|
264
|
+
evidencePaths: ['pnpm-lock.yaml'],
|
|
265
|
+
}),
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
if (filePaths.includes('yarn.lock')) {
|
|
269
|
+
signals.push(
|
|
270
|
+
createSignal({ key: 'yarn', label: 'Yarn', reason: 'Detected Yarn lockfile.', evidencePaths: ['yarn.lock'] }),
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
return signals
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function inferRepoShape(params: {
|
|
277
|
+
topLevelEntries: string[]
|
|
278
|
+
components: RepositoryStructureComponent[]
|
|
279
|
+
frameworks: RepositoryStructureSignal[]
|
|
280
|
+
filePaths: string[]
|
|
281
|
+
}): { kind: RepositoryStructureShape; reason: string; evidencePaths: string[] } {
|
|
282
|
+
if (
|
|
283
|
+
params.topLevelEntries.includes('apps') ||
|
|
284
|
+
params.topLevelEntries.includes('packages') ||
|
|
285
|
+
params.components.length >= 3
|
|
286
|
+
) {
|
|
287
|
+
return {
|
|
288
|
+
kind: 'monorepo',
|
|
289
|
+
reason: 'Multiple package or app roots were detected, which indicates a monorepo-style repository.',
|
|
290
|
+
evidencePaths: normalizeEvidencePaths(params.components.map((component) => component.path)),
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (
|
|
295
|
+
params.topLevelEntries.every((entry) => ['docs', 'documentation', 'website'].includes(entry)) ||
|
|
296
|
+
params.filePaths.every((filePath) => filePath.endsWith('.md') || filePath.startsWith('docs/'))
|
|
297
|
+
) {
|
|
298
|
+
return {
|
|
299
|
+
kind: 'docs-only',
|
|
300
|
+
reason: 'The repository appears to be documentation-centric with little or no application source.',
|
|
301
|
+
evidencePaths: normalizeEvidencePaths(params.topLevelEntries),
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (params.frameworks.some((framework) => ['React', 'Next.js', 'Vite'].includes(framework.label))) {
|
|
306
|
+
return {
|
|
307
|
+
kind: 'single-project',
|
|
308
|
+
reason: 'A single primary web application framework was detected without strong monorepo signals.',
|
|
309
|
+
evidencePaths: normalizeEvidencePaths(params.frameworks.flatMap((framework) => framework.evidencePaths)),
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
kind: 'uncertain',
|
|
315
|
+
reason: 'The repository structure did not clearly match a single canonical shape.',
|
|
316
|
+
evidencePaths: normalizeEvidencePaths(params.topLevelEntries),
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function inferComponentKind(componentPath: string, packageJson: PackageJson): RepositoryStructureComponent['kind'] {
|
|
321
|
+
const dependencyNames = [
|
|
322
|
+
...Object.keys(packageJson.dependencies ?? {}),
|
|
323
|
+
...Object.keys(packageJson.devDependencies ?? {}),
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
if (componentPath.startsWith('docs/') || componentPath === 'docs') return 'docs'
|
|
327
|
+
if (componentPath.startsWith('apps/') || dependencyNames.includes('next') || dependencyNames.includes('react')) {
|
|
328
|
+
return 'app'
|
|
329
|
+
}
|
|
330
|
+
if (
|
|
331
|
+
componentPath.startsWith('services/') ||
|
|
332
|
+
dependencyNames.includes('hono') ||
|
|
333
|
+
dependencyNames.includes('express')
|
|
334
|
+
) {
|
|
335
|
+
return 'service'
|
|
336
|
+
}
|
|
337
|
+
if (componentPath.startsWith('packages/')) return 'package'
|
|
338
|
+
if (packageJson.private === false) return 'library'
|
|
339
|
+
return 'unknown'
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function inferComponentFrameworks(packageJson: PackageJson): string[] {
|
|
343
|
+
return unique(
|
|
344
|
+
[
|
|
345
|
+
packageJson.dependencies?.next ? 'Next.js' : null,
|
|
346
|
+
packageJson.dependencies?.react ? 'React' : null,
|
|
347
|
+
packageJson.dependencies?.hono ? 'Hono' : null,
|
|
348
|
+
packageJson.dependencies?.express ? 'Express' : null,
|
|
349
|
+
packageJson.dependencies?.tailwindcss ? 'Tailwind CSS' : null,
|
|
350
|
+
].filter((value): value is string => Boolean(value)),
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function inferComponentLanguages(filePaths: string[], componentPath: string): string[] {
|
|
355
|
+
const componentFiles = filePaths.filter(
|
|
356
|
+
(filePath) => filePath === componentPath || filePath.startsWith(`${componentPath}/`),
|
|
357
|
+
)
|
|
358
|
+
return unique(
|
|
359
|
+
componentFiles
|
|
360
|
+
.map((filePath) => LANGUAGE_BY_EXTENSION[path.extname(filePath).toLowerCase()])
|
|
361
|
+
.filter((value): value is string => Boolean(value)),
|
|
362
|
+
).slice(0, 20)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function summarizeSignals(signals: RepositoryStructureSignal[], limit = 20): string[] {
|
|
366
|
+
return truncate(unique(signals.map((signal) => signal.label)), limit)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function buildStructureMarkdown(params: {
|
|
370
|
+
repoStructure: RepositoryStructureArtifact
|
|
371
|
+
structureSummary: RepositoryStructureSummary
|
|
372
|
+
}): string {
|
|
373
|
+
const summary = params.structureSummary
|
|
374
|
+
return [
|
|
375
|
+
'# Repository Structure',
|
|
376
|
+
'',
|
|
377
|
+
`- Shape: ${summary.repoShape}`,
|
|
378
|
+
`- Package managers: ${summary.packageManagers.join(', ') || 'none detected'}`,
|
|
379
|
+
`- Languages: ${summary.languages.join(', ') || 'none detected'}`,
|
|
380
|
+
`- Frameworks: ${summary.frameworks.join(', ') || 'none detected'}`,
|
|
381
|
+
`- Components: ${
|
|
382
|
+
summary.components.length > 0
|
|
383
|
+
? summary.components.map((component) => `${component.kind}:${component.name}@${component.path}`).join(', ')
|
|
384
|
+
: 'none detected'
|
|
385
|
+
}`,
|
|
386
|
+
`- Entry points: ${summary.entryPoints.join(', ') || 'none detected'}`,
|
|
387
|
+
`- Routes: ${summary.routeSignals.join(', ') || 'none detected'}`,
|
|
388
|
+
`- APIs: ${summary.apiSignals.join(', ') || 'none detected'}`,
|
|
389
|
+
`- Database: ${summary.databaseSignals.join(', ') || 'none detected'}`,
|
|
390
|
+
`- Auth: ${summary.authSignals.join(', ') || 'none detected'}`,
|
|
391
|
+
`- Jobs: ${summary.jobSignals.join(', ') || 'none detected'}`,
|
|
392
|
+
`- Environment files: ${summary.envFiles.join(', ') || 'none detected'}`,
|
|
393
|
+
`- Docker: ${summary.dockerFiles.join(', ') || 'none detected'}`,
|
|
394
|
+
`- CI/CD: ${summary.ciCdFiles.join(', ') || 'none detected'}`,
|
|
395
|
+
`- Deployment: ${summary.deploymentSignals.join(', ') || 'none detected'}`,
|
|
396
|
+
`- Tests: ${summary.testSignals.join(', ') || 'none detected'}`,
|
|
397
|
+
`- Linting: ${summary.lintSignals.join(', ') || 'none detected'}`,
|
|
398
|
+
`- Formatting: ${summary.formatSignals.join(', ') || 'none detected'}`,
|
|
399
|
+
`- Typechecking: ${summary.typecheckSignals.join(', ') || 'none detected'}`,
|
|
400
|
+
`- Unknowns: ${summary.unknowns.join(' | ') || 'none'}`,
|
|
401
|
+
].join('\n')
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export async function extractRepositoryStructure(params: ExtractParams): Promise<ExtractResult> {
|
|
405
|
+
const filePaths = await collectRelativeFilePaths(params.rootDir)
|
|
406
|
+
const topLevelEntries = gatherTopLevelEntries(filePaths)
|
|
407
|
+
const packageJsonPaths = filePaths.filter((filePath) => filePath.endsWith('package.json')).slice(0, 200)
|
|
408
|
+
const packageJsons = (
|
|
409
|
+
await Promise.all(
|
|
410
|
+
packageJsonPaths.map(async (packageJsonPath) => {
|
|
411
|
+
const data = await readPackageJson(params.rootDir, packageJsonPath)
|
|
412
|
+
return data ? { path: packageJsonPath, data } : null
|
|
413
|
+
}),
|
|
414
|
+
)
|
|
415
|
+
).filter((value): value is { path: string; data: PackageJson } => value !== null)
|
|
416
|
+
|
|
417
|
+
const packageManagers = inferPackageManagerSignals(filePaths)
|
|
418
|
+
const components: RepositoryStructureComponent[] = packageJsons.map((packageJson) => {
|
|
419
|
+
const componentPath = packageJson.path === 'package.json' ? '.' : normalizePath(path.dirname(packageJson.path))
|
|
420
|
+
return {
|
|
421
|
+
kind: inferComponentKind(componentPath, packageJson.data),
|
|
422
|
+
name: packageJson.data.name?.trim() || path.basename(componentPath === '.' ? params.rootDir : componentPath),
|
|
423
|
+
path: componentPath,
|
|
424
|
+
packageManager: packageManagers[0]?.label,
|
|
425
|
+
languages: inferComponentLanguages(filePaths, componentPath),
|
|
426
|
+
frameworks: inferComponentFrameworks(packageJson.data),
|
|
427
|
+
reason: 'Detected a package manifest in this directory.',
|
|
428
|
+
evidencePaths: [packageJson.path],
|
|
429
|
+
}
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
const languages = gatherLanguageSignals(filePaths)
|
|
433
|
+
const frameworks = inferFrameworkSignals(packageJsons)
|
|
434
|
+
const routeSignals = buildPathSignals({
|
|
435
|
+
keyPrefix: 'route',
|
|
436
|
+
label: 'Routes',
|
|
437
|
+
reason: 'Detected route-related directories or files.',
|
|
438
|
+
filePaths: truncate(
|
|
439
|
+
findPaths(filePaths, (filePath) => /(^|\/)(routes|pages|app\/routes)\//.test(filePath)),
|
|
440
|
+
20,
|
|
441
|
+
),
|
|
442
|
+
})
|
|
443
|
+
const apiSignals = buildPathSignals({
|
|
444
|
+
keyPrefix: 'api',
|
|
445
|
+
label: 'API',
|
|
446
|
+
reason: 'Detected API or server route surfaces.',
|
|
447
|
+
filePaths: truncate(
|
|
448
|
+
findPaths(filePaths, (filePath) => /(^|\/)(api|server|openapi|rpc)\//.test(filePath)),
|
|
449
|
+
20,
|
|
450
|
+
),
|
|
451
|
+
})
|
|
452
|
+
const databaseSignals = buildPathSignals({
|
|
453
|
+
keyPrefix: 'database',
|
|
454
|
+
label: 'Database',
|
|
455
|
+
reason: 'Detected database schemas, migrations, or data-access directories.',
|
|
456
|
+
filePaths: truncate(
|
|
457
|
+
findPaths(filePaths, (filePath) => /(^|\/)(db|database|migrations|prisma|drizzle|schema)\b/.test(filePath)),
|
|
458
|
+
20,
|
|
459
|
+
),
|
|
460
|
+
})
|
|
461
|
+
const authSignals = buildPathSignals({
|
|
462
|
+
keyPrefix: 'auth',
|
|
463
|
+
label: 'Auth',
|
|
464
|
+
reason: 'Detected authentication-related code or configuration.',
|
|
465
|
+
filePaths: truncate(
|
|
466
|
+
findPaths(filePaths, (filePath) => /(^|\/)(auth|better-auth|clerk|next-auth)\b/.test(filePath)),
|
|
467
|
+
20,
|
|
468
|
+
),
|
|
469
|
+
})
|
|
470
|
+
const jobSignals = buildPathSignals({
|
|
471
|
+
keyPrefix: 'job',
|
|
472
|
+
label: 'Jobs',
|
|
473
|
+
reason: 'Detected jobs, queues, or worker execution surfaces.',
|
|
474
|
+
filePaths: truncate(
|
|
475
|
+
findPaths(filePaths, (filePath) => /(^|\/)(jobs|queues|workers?)\b/.test(filePath)),
|
|
476
|
+
20,
|
|
477
|
+
),
|
|
478
|
+
})
|
|
479
|
+
const envFiles = buildPathSignals({
|
|
480
|
+
keyPrefix: 'env',
|
|
481
|
+
label: 'Environment',
|
|
482
|
+
reason: 'Detected environment variable files.',
|
|
483
|
+
filePaths: truncate(
|
|
484
|
+
findPaths(filePaths, (filePath) => /(^|\/)\.env(\.|$)/.test(filePath)),
|
|
485
|
+
20,
|
|
486
|
+
),
|
|
487
|
+
})
|
|
488
|
+
const dockerFiles = buildPathSignals({
|
|
489
|
+
keyPrefix: 'docker',
|
|
490
|
+
label: 'Docker',
|
|
491
|
+
reason: 'Detected Docker build or compose files.',
|
|
492
|
+
filePaths: truncate(
|
|
493
|
+
findPaths(filePaths, (filePath) => /(Dockerfile|docker-compose|compose\.ya?ml|\.dockerignore)/i.test(filePath)),
|
|
494
|
+
20,
|
|
495
|
+
),
|
|
496
|
+
})
|
|
497
|
+
const ciCdFiles = buildPathSignals({
|
|
498
|
+
keyPrefix: 'cicd',
|
|
499
|
+
label: 'CI/CD',
|
|
500
|
+
reason: 'Detected continuous integration or delivery workflow files.',
|
|
501
|
+
filePaths: truncate(
|
|
502
|
+
findPaths(filePaths, (filePath) => /^\.github\/workflows\/|\.gitlab-ci\.yml$|circleci\//.test(filePath)),
|
|
503
|
+
20,
|
|
504
|
+
),
|
|
505
|
+
})
|
|
506
|
+
const deploymentFiles = buildPathSignals({
|
|
507
|
+
keyPrefix: 'deploy',
|
|
508
|
+
label: 'Deployment',
|
|
509
|
+
reason: 'Detected deployment platform or infrastructure descriptors.',
|
|
510
|
+
filePaths: truncate(
|
|
511
|
+
findPaths(filePaths, (filePath) =>
|
|
512
|
+
/(vercel\.json|fly\.toml|render\.ya?ml|netlify\.toml|railway\.json|k8s|helm)/i.test(filePath),
|
|
513
|
+
),
|
|
514
|
+
20,
|
|
515
|
+
),
|
|
516
|
+
})
|
|
517
|
+
const testSignals = buildPathSignals({
|
|
518
|
+
keyPrefix: 'test',
|
|
519
|
+
label: 'Tests',
|
|
520
|
+
reason: 'Detected repository test files.',
|
|
521
|
+
filePaths: truncate(
|
|
522
|
+
findPaths(filePaths, (filePath) => /(^|\/)(__tests__|tests?)\/|(\.test\.|\.(spec)\.)/.test(filePath)),
|
|
523
|
+
20,
|
|
524
|
+
),
|
|
525
|
+
})
|
|
526
|
+
const lintSignals = buildPathSignals({
|
|
527
|
+
keyPrefix: 'lint',
|
|
528
|
+
label: 'Linting',
|
|
529
|
+
reason: 'Detected linting configuration files.',
|
|
530
|
+
filePaths: truncate(
|
|
531
|
+
findPaths(
|
|
532
|
+
filePaths,
|
|
533
|
+
(filePath) => /(eslint|oxlint|biome|lint)/i.test(filePath) && /\.(json|js|ts|mjs|cjs|yaml|yml)$/.test(filePath),
|
|
534
|
+
),
|
|
535
|
+
20,
|
|
536
|
+
),
|
|
537
|
+
})
|
|
538
|
+
const formatSignals = buildPathSignals({
|
|
539
|
+
keyPrefix: 'format',
|
|
540
|
+
label: 'Formatting',
|
|
541
|
+
reason: 'Detected code-formatting configuration files.',
|
|
542
|
+
filePaths: truncate(
|
|
543
|
+
findPaths(
|
|
544
|
+
filePaths,
|
|
545
|
+
(filePath) => /(prettier|oxfmt|biome)/i.test(filePath) && /\.(json|js|ts|mjs|cjs|yaml|yml)$/.test(filePath),
|
|
546
|
+
),
|
|
547
|
+
20,
|
|
548
|
+
),
|
|
549
|
+
})
|
|
550
|
+
const typecheckSignals = buildPathSignals({
|
|
551
|
+
keyPrefix: 'typecheck',
|
|
552
|
+
label: 'Typechecking',
|
|
553
|
+
reason: 'Detected typechecking configuration files.',
|
|
554
|
+
filePaths: truncate(
|
|
555
|
+
findPaths(filePaths, (filePath) => /(tsconfig|mypy|pyright)/i.test(filePath)),
|
|
556
|
+
20,
|
|
557
|
+
),
|
|
558
|
+
})
|
|
559
|
+
const entryPoints = buildPathSignals({
|
|
560
|
+
keyPrefix: 'entry',
|
|
561
|
+
label: 'Entry point',
|
|
562
|
+
reason: 'Detected likely application entry points.',
|
|
563
|
+
filePaths: truncate(
|
|
564
|
+
findPaths(filePaths, (filePath) => /(^|\/)(main|index|app|server|worker)\.(ts|tsx|js|jsx|py|go)$/.test(filePath)),
|
|
565
|
+
20,
|
|
566
|
+
),
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
const repoShape = inferRepoShape({ topLevelEntries, components, frameworks, filePaths })
|
|
570
|
+
|
|
571
|
+
const repoStructure = RepositoryStructureArtifactSchema.parse({
|
|
572
|
+
schemaVersion: 'repository-structure.v1',
|
|
573
|
+
extractorVersion: EXTRACTOR_VERSION,
|
|
574
|
+
generatedAt: new Date().toISOString(),
|
|
575
|
+
repository: {
|
|
576
|
+
repositoryKey: params.repositoryKey,
|
|
577
|
+
repositoryFullName: params.repositoryFullName,
|
|
578
|
+
defaultBranch: params.defaultBranch,
|
|
579
|
+
indexBranch: params.indexBranch,
|
|
580
|
+
commitSha: params.commitSha,
|
|
581
|
+
},
|
|
582
|
+
topLevelEntries,
|
|
583
|
+
repoShape,
|
|
584
|
+
packageManagers,
|
|
585
|
+
languages,
|
|
586
|
+
frameworks,
|
|
587
|
+
components: truncate(components, 200),
|
|
588
|
+
entryPoints,
|
|
589
|
+
routeSignals,
|
|
590
|
+
apiSignals,
|
|
591
|
+
databaseSignals,
|
|
592
|
+
authSignals,
|
|
593
|
+
jobSignals,
|
|
594
|
+
envFiles,
|
|
595
|
+
dockerFiles,
|
|
596
|
+
ciCdFiles,
|
|
597
|
+
deploymentFiles,
|
|
598
|
+
testSignals,
|
|
599
|
+
lintSignals,
|
|
600
|
+
formatSignals,
|
|
601
|
+
typecheckSignals,
|
|
602
|
+
unknowns: repoShape.kind === 'uncertain' ? ['Repository shape could not be confidently classified.'] : [],
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
const structureSummary = RepositoryStructureSummarySchema.parse({
|
|
606
|
+
schemaVersion: 'repository-structure-summary.v1',
|
|
607
|
+
extractorVersion: EXTRACTOR_VERSION,
|
|
608
|
+
generatedAt: repoStructure.generatedAt,
|
|
609
|
+
repository: repoStructure.repository,
|
|
610
|
+
repoShape: repoStructure.repoShape.kind,
|
|
611
|
+
topLevelEntries: truncate(repoStructure.topLevelEntries, 20),
|
|
612
|
+
packageManagers: summarizeSignals(repoStructure.packageManagers),
|
|
613
|
+
languages: summarizeSignals(repoStructure.languages),
|
|
614
|
+
frameworks: summarizeSignals(repoStructure.frameworks),
|
|
615
|
+
components: truncate(
|
|
616
|
+
repoStructure.components.map((component) => ({
|
|
617
|
+
kind: component.kind,
|
|
618
|
+
name: component.name,
|
|
619
|
+
path: component.path,
|
|
620
|
+
})),
|
|
621
|
+
40,
|
|
622
|
+
),
|
|
623
|
+
entryPoints: summarizeSignals(repoStructure.entryPoints),
|
|
624
|
+
routeSignals: summarizeSignals(repoStructure.routeSignals),
|
|
625
|
+
apiSignals: summarizeSignals(repoStructure.apiSignals),
|
|
626
|
+
databaseSignals: summarizeSignals(repoStructure.databaseSignals),
|
|
627
|
+
authSignals: summarizeSignals(repoStructure.authSignals),
|
|
628
|
+
jobSignals: summarizeSignals(repoStructure.jobSignals),
|
|
629
|
+
envFiles: summarizeSignals(repoStructure.envFiles),
|
|
630
|
+
dockerFiles: summarizeSignals(repoStructure.dockerFiles),
|
|
631
|
+
ciCdFiles: summarizeSignals(repoStructure.ciCdFiles),
|
|
632
|
+
testSignals: summarizeSignals(repoStructure.testSignals),
|
|
633
|
+
lintSignals: summarizeSignals(repoStructure.lintSignals),
|
|
634
|
+
formatSignals: summarizeSignals(repoStructure.formatSignals),
|
|
635
|
+
typecheckSignals: summarizeSignals(repoStructure.typecheckSignals),
|
|
636
|
+
deploymentSignals: summarizeSignals(repoStructure.deploymentFiles),
|
|
637
|
+
unknowns: repoStructure.unknowns.slice(0, 20),
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
return {
|
|
641
|
+
repoStructure,
|
|
642
|
+
structureSummary,
|
|
643
|
+
structureMarkdown: buildStructureMarkdown({ repoStructure, structureSummary }),
|
|
644
|
+
}
|
|
645
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os from 'node:os'
|
|
2
|
+
|
|
3
|
+
type MutableOsModule = Omit<typeof os, 'availableParallelism'> & { availableParallelism?: () => number }
|
|
4
|
+
|
|
5
|
+
const mutableOs = os as MutableOsModule
|
|
6
|
+
|
|
7
|
+
function toPositiveSafeInteger(value: unknown): number | null {
|
|
8
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function readAvailableParallelism(read?: () => unknown): number | null {
|
|
12
|
+
if (!read) return null
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
return toPositiveSafeInteger(read())
|
|
16
|
+
} catch {
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolveSafeRepomixProcessConcurrency(params?: {
|
|
22
|
+
availableParallelism?: () => unknown
|
|
23
|
+
getCpuCount?: () => unknown
|
|
24
|
+
}): number {
|
|
25
|
+
const availableParallelism = readAvailableParallelism(params?.availableParallelism ?? mutableOs.availableParallelism)
|
|
26
|
+
if (availableParallelism) {
|
|
27
|
+
return availableParallelism
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const cpuCount = toPositiveSafeInteger(params?.getCpuCount?.() ?? os.cpus().length)
|
|
31
|
+
if (cpuCount) {
|
|
32
|
+
return cpuCount
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function installSafeRepomixAvailableParallelism(): {
|
|
39
|
+
processConcurrency: number
|
|
40
|
+
patched: boolean
|
|
41
|
+
restore: () => void
|
|
42
|
+
} {
|
|
43
|
+
const originalAvailableParallelism = mutableOs.availableParallelism
|
|
44
|
+
const processConcurrency = resolveSafeRepomixProcessConcurrency()
|
|
45
|
+
const currentValue = readAvailableParallelism(originalAvailableParallelism)
|
|
46
|
+
|
|
47
|
+
if (currentValue === processConcurrency) {
|
|
48
|
+
return { processConcurrency, patched: false, restore: () => {} }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
mutableOs.availableParallelism = () => processConcurrency
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
processConcurrency,
|
|
55
|
+
patched: true,
|
|
56
|
+
restore: () => {
|
|
57
|
+
if (originalAvailableParallelism) {
|
|
58
|
+
mutableOs.availableParallelism = originalAvailableParallelism
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
delete mutableOs.availableParallelism
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
}
|