@vibe-forge/core 0.7.5 → 0.8.3

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 (43) hide show
  1. package/package.json +4 -46
  2. package/src/env.ts +5 -25
  3. package/src/index.ts +0 -5
  4. package/src/types.ts +13 -72
  5. package/src/ws.ts +2 -12
  6. package/src/adapter/index.ts +0 -6
  7. package/src/adapter/loader.ts +0 -11
  8. package/src/adapter/type.ts +0 -117
  9. package/src/config/load.ts +0 -122
  10. package/src/config/types.ts +0 -289
  11. package/src/config.ts +0 -2
  12. package/src/controllers/benchmark/discover.ts +0 -89
  13. package/src/controllers/benchmark/index.ts +0 -24
  14. package/src/controllers/benchmark/result-store.ts +0 -46
  15. package/src/controllers/benchmark/runner.ts +0 -415
  16. package/src/controllers/benchmark/schema.ts +0 -60
  17. package/src/controllers/benchmark/types.ts +0 -80
  18. package/src/controllers/benchmark/utils.ts +0 -144
  19. package/src/controllers/benchmark/workspace.ts +0 -179
  20. package/src/controllers/config/index.ts +0 -214
  21. package/src/controllers/system/assets/completed.mp3 +0 -0
  22. package/src/controllers/system/assets/mcp.png +0 -0
  23. package/src/controllers/system/index.ts +0 -102
  24. package/src/controllers/task/generate-adapter-query-options.ts +0 -25
  25. package/src/controllers/task/index.ts +0 -2
  26. package/src/controllers/task/prepare.ts +0 -74
  27. package/src/controllers/task/run.ts +0 -231
  28. package/src/controllers/task/schema.ts +0 -131
  29. package/src/controllers/task/type.ts +0 -6
  30. package/src/hooks/bridge.ts +0 -368
  31. package/src/hooks/call.ts +0 -74
  32. package/src/hooks/index.ts +0 -41
  33. package/src/hooks/loader.ts +0 -79
  34. package/src/hooks/native.ts +0 -116
  35. package/src/hooks/runtime.ts +0 -139
  36. package/src/hooks/type.ts +0 -145
  37. package/src/utils/cache.ts +0 -58
  38. package/src/utils/create-logger.ts +0 -89
  39. package/src/utils/definition-loader.ts +0 -530
  40. package/src/utils/filter.ts +0 -26
  41. package/src/utils/string-transform.ts +0 -37
  42. package/src/utils/uuid.ts +0 -6
  43. package/src/utils/workspace-assets.ts +0 -919
@@ -1,179 +0,0 @@
1
- import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
2
- import { resolve } from 'node:path'
3
- import process from 'node:process'
4
-
5
- import { execCommand, execShellCommand, linkPreparedNodeModules, pathExists } from './utils'
6
-
7
- interface CategoryWorkspaceInput {
8
- workspaceFolder?: string
9
- category: string
10
- baseCommit: string
11
- setupCommand: string
12
- timeoutSec: number
13
- }
14
-
15
- interface CaseWorkspaceInput {
16
- workspaceFolder?: string
17
- category: string
18
- title: string
19
- runId: string
20
- baseCommit: string
21
- setupCommand: string
22
- timeoutSec: number
23
- }
24
-
25
- export interface CategoryWorkspaceState {
26
- workspacePath: string
27
- gitRoot: string
28
- }
29
-
30
- export interface CaseWorkspaceState extends CategoryWorkspaceState {
31
- caseWorkspacePath: string
32
- }
33
-
34
- const categoryWorkspaceInflight = new Map<string, Promise<CategoryWorkspaceState>>()
35
-
36
- const resolveWorktreeRoot = (workspaceFolder = process.cwd()) =>
37
- resolve(workspaceFolder, '.ai/worktress/benchmark')
38
-
39
- const findGitRoot = async (workspaceFolder: string) => {
40
- const result = await execCommand({
41
- command: 'git',
42
- args: ['rev-parse', '--show-toplevel'],
43
- cwd: workspaceFolder
44
- })
45
- if (result.exitCode !== 0) {
46
- throw new Error(result.stderr || 'Failed to resolve git root')
47
- }
48
- return result.stdout.trim()
49
- }
50
-
51
- const detachWorktree = async (gitRoot: string, worktreePath: string) => {
52
- const result = await execCommand({
53
- command: 'git',
54
- args: ['worktree', 'remove', '--force', worktreePath],
55
- cwd: gitRoot
56
- })
57
- const output = `${result.stdout}\n${result.stderr}`
58
- if (result.exitCode !== 0 && !output.includes('is not a working tree')) {
59
- throw new Error(output.trim() || `Failed to remove worktree: ${worktreePath}`)
60
- }
61
- }
62
-
63
- export const ensureCategoryWorkspace = async (input: CategoryWorkspaceInput): Promise<CategoryWorkspaceState> => {
64
- const workspaceFolder = input.workspaceFolder ?? process.cwd()
65
- const lockKey = `${workspaceFolder}:${input.category}`
66
- const inflight = categoryWorkspaceInflight.get(lockKey)
67
- if (inflight != null) {
68
- return inflight
69
- }
70
-
71
- const promise = (async () => {
72
- const gitRoot = await findGitRoot(workspaceFolder)
73
- const worktreeRoot = resolveWorktreeRoot(workspaceFolder)
74
- const workspacePath = resolve(worktreeRoot, input.category)
75
- const statePath = resolve(workspacePath, '.benchmark-state.json')
76
-
77
- await mkdir(worktreeRoot, { recursive: true })
78
-
79
- const currentState = await pathExists(statePath)
80
- ? JSON.parse(await readFile(statePath, 'utf-8')) as {
81
- baseCommit?: string
82
- setupCommand?: string
83
- }
84
- : null
85
-
86
- const needsRecreate = !await pathExists(workspacePath) ||
87
- currentState?.baseCommit !== input.baseCommit ||
88
- currentState?.setupCommand !== input.setupCommand
89
-
90
- if (needsRecreate) {
91
- if (await pathExists(workspacePath)) {
92
- await detachWorktree(gitRoot, workspacePath)
93
- await rm(workspacePath, { force: true, recursive: true })
94
- }
95
- const addResult = await execCommand({
96
- command: 'git',
97
- args: ['worktree', 'add', '--force', '--detach', workspacePath, input.baseCommit],
98
- cwd: gitRoot
99
- })
100
- if (addResult.exitCode !== 0) {
101
- throw new Error(addResult.stderr || `Failed to create worktree for ${input.category}`)
102
- }
103
- if (input.setupCommand.trim() !== '') {
104
- const setupResult = await execShellCommand({
105
- command: input.setupCommand,
106
- cwd: workspacePath,
107
- timeoutMs: input.timeoutSec * 1000
108
- })
109
- if (setupResult.exitCode !== 0) {
110
- throw new Error(setupResult.stderr || setupResult.stdout || 'Failed to prepare category workspace')
111
- }
112
- }
113
- await writeFile(statePath, `${JSON.stringify({
114
- baseCommit: input.baseCommit,
115
- setupCommand: input.setupCommand
116
- }, null, 2)}\n`, 'utf-8')
117
- }
118
-
119
- return {
120
- workspacePath,
121
- gitRoot
122
- }
123
- })()
124
-
125
- categoryWorkspaceInflight.set(lockKey, promise)
126
-
127
- try {
128
- return await promise
129
- } finally {
130
- categoryWorkspaceInflight.delete(lockKey)
131
- }
132
- }
133
-
134
- export const createCaseWorkspace = async (input: CaseWorkspaceInput): Promise<CaseWorkspaceState> => {
135
- const categoryWorkspace = await ensureCategoryWorkspace(input)
136
- const worktreeRoot = resolveWorktreeRoot(input.workspaceFolder ?? process.cwd())
137
- const caseRoot = resolve(worktreeRoot, '.cases', input.category)
138
- const caseWorkspacePath = resolve(caseRoot, `${input.title}-${input.runId}`)
139
-
140
- await mkdir(caseRoot, { recursive: true })
141
- if (await pathExists(caseWorkspacePath)) {
142
- await detachWorktree(categoryWorkspace.gitRoot, caseWorkspacePath)
143
- await rm(caseWorkspacePath, { force: true, recursive: true })
144
- }
145
-
146
- const addResult = await execCommand({
147
- command: 'git',
148
- args: ['worktree', 'add', '--force', '--detach', caseWorkspacePath, input.baseCommit],
149
- cwd: categoryWorkspace.gitRoot
150
- })
151
- if (addResult.exitCode !== 0) {
152
- throw new Error(addResult.stderr || `Failed to create case workspace for ${input.category}/${input.title}`)
153
- }
154
-
155
- await linkPreparedNodeModules(categoryWorkspace.workspacePath, caseWorkspacePath)
156
-
157
- if (input.setupCommand.trim() !== '') {
158
- const setupResult = await execShellCommand({
159
- command: input.setupCommand,
160
- cwd: caseWorkspacePath,
161
- timeoutMs: input.timeoutSec * 1000
162
- })
163
- if (setupResult.exitCode !== 0) {
164
- throw new Error(setupResult.stderr || setupResult.stdout || 'Failed to prepare case workspace')
165
- }
166
- }
167
-
168
- return {
169
- ...categoryWorkspace,
170
- caseWorkspacePath
171
- }
172
- }
173
-
174
- export const disposeCaseWorkspace = async (state: CaseWorkspaceState) => {
175
- await detachWorktree(state.gitRoot, state.caseWorkspacePath)
176
- if (await pathExists(state.caseWorkspacePath)) {
177
- await rm(state.caseWorkspacePath, { force: true, recursive: true })
178
- }
179
- }
@@ -1,214 +0,0 @@
1
- import { existsSync } from 'node:fs'
2
- import { mkdir, readFile, writeFile } from 'node:fs/promises'
3
- import { dirname, extname, resolve } from 'node:path'
4
- import process from 'node:process'
5
-
6
- import { dump, load } from 'js-yaml'
7
-
8
- import type { Config } from '../../config'
9
- import { resetConfigCache } from '../../config'
10
-
11
- export type ConfigSource = 'project' | 'user'
12
-
13
- export interface UpdateConfigFileOptions {
14
- workspaceFolder?: string
15
- source: ConfigSource
16
- section: string
17
- value: unknown
18
- }
19
-
20
- const shouldMaskKey = (key: string) => /key|token|secret|password/i.test(key)
21
-
22
- const projectConfigPaths = [
23
- './.ai.config.json',
24
- './infra/.ai.config.json',
25
- './.ai.config.yaml',
26
- './.ai.config.yml',
27
- './infra/.ai.config.yaml',
28
- './infra/.ai.config.yml'
29
- ]
30
-
31
- const userConfigPaths = [
32
- './.ai.dev.config.json',
33
- './infra/.ai.dev.config.json',
34
- './.ai.dev.config.yaml',
35
- './.ai.dev.config.yml',
36
- './infra/.ai.dev.config.yaml',
37
- './infra/.ai.dev.config.yml'
38
- ]
39
-
40
- const resolveConfigPath = (workspaceFolder: string, source: ConfigSource) => {
41
- const paths = source === 'project' ? projectConfigPaths : userConfigPaths
42
- for (const path of paths) {
43
- const resolved = resolve(workspaceFolder, path)
44
- if (existsSync(resolved)) {
45
- return resolved
46
- }
47
- }
48
- return resolve(workspaceFolder, paths[0])
49
- }
50
-
51
- const parseConfigContent = (format: string, content: string) => {
52
- if (format === '.yaml' || format === '.yml') {
53
- return (load(content) ?? {}) as Record<string, unknown>
54
- }
55
- return JSON.parse(content) as Record<string, unknown>
56
- }
57
-
58
- const serializeConfigContent = (format: string, value: Record<string, unknown>) => {
59
- if (format === '.yaml' || format === '.yml') {
60
- return `${dump(value, { noRefs: true, lineWidth: 120 })}\n`
61
- }
62
- return `${JSON.stringify(value, null, 2)}\n`
63
- }
64
-
65
- const mergeMaskedValues = (incoming: unknown, existing: unknown): unknown => {
66
- if (Array.isArray(incoming)) return incoming
67
- if (incoming != null && typeof incoming === 'object') {
68
- const incomingRecord = incoming as Record<string, unknown>
69
- const existingRecord = (existing != null && typeof existing === 'object')
70
- ? (existing as Record<string, unknown>)
71
- : {}
72
- return Object.entries(incomingRecord).reduce<Record<string, unknown>>((acc, [key, val]) => {
73
- if (shouldMaskKey(key) && val === '******') {
74
- acc[key] = existingRecord[key]
75
- } else {
76
- acc[key] = mergeMaskedValues(val, existingRecord[key])
77
- }
78
- return acc
79
- }, {})
80
- }
81
- return incoming
82
- }
83
-
84
- const updateConfigSection = (config: Config, section: string, value: unknown): Config => {
85
- const nextConfig: Config = { ...config }
86
- const sectionValue = (value != null && typeof value === 'object')
87
- ? (value as Record<string, unknown>)
88
- : {}
89
-
90
- const updateField = <T extends keyof Config>(key: T, nextValue: Config[T] | undefined) => {
91
- if (nextValue === undefined) {
92
- delete nextConfig[key]
93
- } else {
94
- nextConfig[key] = nextValue
95
- }
96
- }
97
-
98
- switch (section) {
99
- case 'general': {
100
- updateField('baseDir', sectionValue.baseDir as Config['baseDir'])
101
- updateField('defaultAdapter', sectionValue.defaultAdapter as Config['defaultAdapter'])
102
- updateField('defaultModelService', sectionValue.defaultModelService as Config['defaultModelService'])
103
- updateField('defaultModel', sectionValue.defaultModel as Config['defaultModel'])
104
- updateField('recommendedModels', sectionValue.recommendedModels as Config['recommendedModels'])
105
- updateField('interfaceLanguage', sectionValue.interfaceLanguage as Config['interfaceLanguage'])
106
- updateField('modelLanguage', sectionValue.modelLanguage as Config['modelLanguage'])
107
- updateField('announcements', sectionValue.announcements as Config['announcements'])
108
- updateField(
109
- 'permissions',
110
- mergeMaskedValues(sectionValue.permissions, config.permissions) as Config['permissions']
111
- )
112
- updateField(
113
- 'env',
114
- mergeMaskedValues(sectionValue.env, config.env) as Config['env']
115
- )
116
- updateField(
117
- 'notifications',
118
- mergeMaskedValues(sectionValue.notifications, config.notifications) as Config['notifications']
119
- )
120
- updateField(
121
- 'shortcuts',
122
- mergeMaskedValues(sectionValue.shortcuts, config.shortcuts) as Config['shortcuts']
123
- )
124
- return nextConfig
125
- }
126
- case 'conversation': {
127
- updateField('conversation', mergeMaskedValues(sectionValue, config.conversation) as Config['conversation'])
128
- return nextConfig
129
- }
130
- case 'modelServices': {
131
- updateField(
132
- 'modelServices',
133
- mergeMaskedValues(sectionValue, config.modelServices) as Config['modelServices']
134
- )
135
- return nextConfig
136
- }
137
- case 'channels': {
138
- updateField(
139
- 'channels',
140
- mergeMaskedValues(sectionValue, config.channels) as Config['channels']
141
- )
142
- return nextConfig
143
- }
144
- case 'adapters': {
145
- updateField('adapters', mergeMaskedValues(sectionValue, config.adapters) as Config['adapters'])
146
- return nextConfig
147
- }
148
- case 'plugins': {
149
- updateField('plugins', sectionValue.plugins as Config['plugins'])
150
- updateField(
151
- 'enabledPlugins',
152
- mergeMaskedValues(sectionValue.enabledPlugins, config.enabledPlugins) as Config['enabledPlugins']
153
- )
154
- updateField(
155
- 'extraKnownMarketplaces',
156
- mergeMaskedValues(
157
- sectionValue.extraKnownMarketplaces,
158
- config.extraKnownMarketplaces
159
- ) as Config['extraKnownMarketplaces']
160
- )
161
- return nextConfig
162
- }
163
- case 'mcp': {
164
- updateField(
165
- 'mcpServers',
166
- mergeMaskedValues(sectionValue.mcpServers, config.mcpServers) as Config['mcpServers']
167
- )
168
- updateField(
169
- 'defaultIncludeMcpServers',
170
- sectionValue.defaultIncludeMcpServers as Config['defaultIncludeMcpServers']
171
- )
172
- updateField(
173
- 'defaultExcludeMcpServers',
174
- sectionValue.defaultExcludeMcpServers as Config['defaultExcludeMcpServers']
175
- )
176
- updateField(
177
- 'noDefaultVibeForgeMcpServer',
178
- sectionValue.noDefaultVibeForgeMcpServer as Config['noDefaultVibeForgeMcpServer']
179
- )
180
- return nextConfig
181
- }
182
- case 'shortcuts': {
183
- updateField(
184
- 'shortcuts',
185
- mergeMaskedValues(sectionValue, config.shortcuts) as Config['shortcuts']
186
- )
187
- return nextConfig
188
- }
189
- default:
190
- return nextConfig
191
- }
192
- }
193
-
194
- export const updateConfigFile = async (options: UpdateConfigFileOptions) => {
195
- const workspaceFolder = options.workspaceFolder ?? process.cwd()
196
- const configPath = resolveConfigPath(workspaceFolder, options.source)
197
- const format = extname(configPath).toLowerCase()
198
- const hasExisting = existsSync(configPath)
199
- const existingContent = hasExisting ? await readFile(configPath, 'utf-8') : ''
200
- const existingConfig = hasExisting ? parseConfigContent(format, existingContent) : {}
201
- const updatedConfig = updateConfigSection(existingConfig as Config, options.section, options.value)
202
- await mkdir(dirname(configPath), { recursive: true })
203
- await writeFile(
204
- configPath,
205
- serializeConfigContent(format, updatedConfig as Record<string, unknown>),
206
- 'utf-8'
207
- )
208
- resetConfigCache()
209
- return { configPath, updatedConfig }
210
- }
211
-
212
- export const configController = {
213
- updateConfigFile
214
- }
@@ -1,102 +0,0 @@
1
- import { spawn } from 'node:child_process'
2
- import path from 'node:path'
3
- import process from 'node:process'
4
-
5
- import type { NotificationMetadata } from 'node-notifier'
6
- import notifier from 'node-notifier'
7
- import z from 'zod'
8
-
9
- const notifyOptionsSchema = z.object({
10
- title: z.string().optional(),
11
- description: z.string(),
12
- icon: z.string().optional().describe('自定义图标路径'),
13
- sound: z
14
- .union([z.boolean(), z.string()])
15
- .optional()
16
- .describe('是否播放音效或指定音效文件路径'),
17
- volume: z.number().optional().describe('音量,0-1 或 0-100'),
18
- timeout: z
19
- .union([z.number(), z.literal(false)])
20
- .optional()
21
- .describe('通知超时时间'),
22
- actions: z.array(z.string()).optional().describe('通知操作按钮'),
23
- needConfirm: z.boolean().optional().describe('是否需要用户确认')
24
- })
25
-
26
- type NotifyOptions = z.infer<typeof notifyOptionsSchema>
27
-
28
- export const notify = async (options: NotifyOptions) => {
29
- const {
30
- title,
31
- description,
32
- icon,
33
- sound = true,
34
- volume,
35
- timeout = 10 * 60 * 1000,
36
- needConfirm
37
- } = options
38
-
39
- // 默认图标
40
- const defaultIcon = path.resolve(__dirname, './assets/mcp.png')
41
- // 默认音效
42
- const defaultSound = path.resolve(__dirname, './assets/completed.mp3')
43
-
44
- const resolvedSound = typeof sound === 'string'
45
- ? sound
46
- : (sound ? defaultSound : undefined)
47
- const resolvedVolume = typeof volume === 'number'
48
- ? (volume > 1 ? Math.min(volume, 100) / 100 : Math.max(volume, 0))
49
- : undefined
50
- const shouldPlaySound = resolvedSound != null && resolvedVolume !== 0
51
- const shouldUseNotifierSound = !(resolvedVolume != null && resolvedSound != null && process.platform === 'darwin')
52
- if (shouldPlaySound && !shouldUseNotifierSound && resolvedSound != null) {
53
- try {
54
- const args = ['-v', `${resolvedVolume ?? 1}`, resolvedSound]
55
- const proc = spawn('afplay', args, { stdio: 'ignore', detached: true })
56
- proc.unref()
57
- } catch {
58
- }
59
- }
60
-
61
- const [response, metadata] = await new Promise<
62
- [string, NotificationMetadata | undefined]
63
- >((ok, no) => {
64
- notifier.notify(
65
- {
66
- icon: icon || defaultIcon,
67
- title,
68
- sound: shouldUseNotifierSound ? resolvedSound : undefined,
69
- message: description,
70
- wait: true,
71
- reply: true,
72
- timeout
73
- },
74
- (err, response, metadata) => {
75
- if (err) {
76
- no(err)
77
- return
78
- }
79
- if (!needConfirm) {
80
- return
81
- }
82
- ok([response, metadata])
83
- }
84
- )
85
- if (!needConfirm) {
86
- ok([
87
- 'default',
88
- {
89
- activationType: 'default',
90
- activationAt: Date.now().toLocaleString(),
91
- deliveredAt: Date.now().toLocaleString()
92
- }
93
- ])
94
- }
95
- })
96
- return { response, metadata }
97
- }
98
-
99
- export const systemController = {
100
- notify,
101
- notifyOptionsSchema
102
- }
@@ -1,25 +0,0 @@
1
- import process from 'node:process'
2
-
3
- import type { AdapterQueryOptions } from '#~/adapter/type.js'
4
- import { resolvePromptAssetSelection, resolveWorkspaceAssetBundle } from '#~/utils/workspace-assets.js'
5
-
6
- export async function generateAdapterQueryOptions(
7
- type: 'spec' | 'entity' | undefined,
8
- name?: string,
9
- cwd: string = process.cwd(),
10
- input?: {
11
- skills?: AdapterQueryOptions['skills']
12
- }
13
- ) {
14
- const bundle = await resolveWorkspaceAssetBundle({ cwd })
15
- const [data, resolvedOptions] = await resolvePromptAssetSelection({
16
- bundle,
17
- type,
18
- name,
19
- input
20
- })
21
- return [
22
- data,
23
- resolvedOptions as Partial<AdapterQueryOptions>
24
- ] as const
25
- }
@@ -1,2 +0,0 @@
1
- export { generateAdapterQueryOptions } from './generate-adapter-query-options'
2
- export { run } from './run'
@@ -1,74 +0,0 @@
1
- import process from 'node:process'
2
-
3
- import { loadConfig } from '@vibe-forge/core'
4
- import type { AdapterCtx, AdapterQueryOptions } from '@vibe-forge/core/adapter'
5
- import { getCache, setCache } from '@vibe-forge/core/utils/cache'
6
- import { createLogger } from '@vibe-forge/core/utils/create-logger'
7
- import { uuid } from '@vibe-forge/core/utils/uuid'
8
- import { resolveWorkspaceAssetBundle } from '@vibe-forge/core/utils/workspace-assets'
9
-
10
- import { resolveServerLogLevel } from '#~/env.js'
11
-
12
- import type { RunTaskOptions } from './type'
13
-
14
- export const prepare = async (
15
- options: RunTaskOptions,
16
- adapterOptions: AdapterQueryOptions
17
- ) => {
18
- const cwd = options.cwd ?? process.env.__VF_PROJECT_WORKSPACE_FOLDER__ ?? process.cwd()
19
-
20
- const {
21
- sessionId = uuid()
22
- } = adapterOptions
23
- const {
24
- ctxId = process.env.__VF_PROJECT_AI_CTX_ID__ ?? sessionId,
25
- env: envFromOptions
26
- } = options
27
- const {
28
- __IS_LOADER_CLI__: _0,
29
- ...prevEnv
30
- } = {
31
- ...process.env,
32
- ...envFromOptions
33
- }
34
- const env: Record<string, string | null | undefined> = {
35
- ...prevEnv,
36
- __VF_PROJECT_AI_CTX_ID__: ctxId,
37
- __VF_PROJECT_AI_SESSION_ID__: sessionId,
38
- __VF_PROJECT_AI_RUN_TYPE__: adapterOptions.runtime,
39
- // 移除 NODE_OPTIONS 环境变量,防止干扰子进程的运行环境
40
- NODE_OPTIONS: undefined
41
- }
42
- const logger = createLogger(
43
- cwd,
44
- ctxId,
45
- sessionId,
46
- env?.LOG_PREFIX ?? '',
47
- resolveServerLogLevel(env)
48
- )
49
-
50
- const jsonVariables: Record<string, string | null | undefined> = {
51
- ...env,
52
- WORKSPACE_FOLDER: cwd,
53
- __VF_PROJECT_WORKSPACE_FOLDER__: cwd
54
- }
55
- const [config, userConfig] = await loadConfig({ jsonVariables })
56
- const assets = await resolveWorkspaceAssetBundle({
57
- cwd,
58
- configs: [config, userConfig]
59
- })
60
- return [
61
- {
62
- ctxId,
63
- cwd,
64
- env,
65
- cache: {
66
- set: (key, value) => setCache(cwd, ctxId, sessionId, key, value),
67
- get: (key) => getCache(cwd, ctxId, sessionId, key)
68
- },
69
- logger,
70
- configs: [config, userConfig],
71
- assets
72
- } satisfies AdapterCtx
73
- ] as const
74
- }