@vibe-forge/tsconfigs 0.8.0 → 0.9.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.
Files changed (71) hide show
  1. package/dist/apps/cli/__tests__/clear.spec.d.ts +1 -0
  2. package/dist/apps/cli/__tests__/clear.spec.js +72 -0
  3. package/dist/apps/cli/src/commands/clear.d.ts +4 -0
  4. package/dist/apps/cli/src/commands/clear.js +62 -39
  5. package/dist/apps/client/src/main.d.ts +1 -0
  6. package/dist/apps/client/src/main.js +1 -0
  7. package/dist/apps/server/__tests__/db/connection.spec.d.ts +1 -0
  8. package/dist/apps/server/__tests__/db/connection.spec.js +58 -0
  9. package/dist/apps/server/__tests__/db/index.spec.js +129 -5
  10. package/dist/apps/server/__tests__/db/schema.spec.js +3 -6
  11. package/dist/apps/server/__tests__/db/sqlite.spec.d.ts +1 -0
  12. package/dist/apps/server/__tests__/db/sqlite.spec.js +51 -0
  13. package/dist/apps/server/__tests__/services/session-start.spec.js +3 -3
  14. package/dist/apps/server/src/db/automation/repo.d.ts +2 -2
  15. package/dist/apps/server/src/db/channelSessions/repo.d.ts +2 -2
  16. package/dist/apps/server/src/db/connection.d.ts +2 -2
  17. package/dist/apps/server/src/db/connection.js +2 -2
  18. package/dist/apps/server/src/db/index.d.ts +2 -2
  19. package/dist/apps/server/src/db/schema.d.ts +3 -3
  20. package/dist/apps/server/src/db/sessions/messages.repo.d.ts +2 -2
  21. package/dist/apps/server/src/db/sessions/repo.d.ts +2 -2
  22. package/dist/apps/server/src/db/sessions/repo.js +1 -1
  23. package/dist/apps/server/src/db/sessions/tags.repo.d.ts +2 -2
  24. package/dist/apps/server/src/db/sqlite.d.ts +44 -0
  25. package/dist/apps/server/src/db/sqlite.js +83 -0
  26. package/dist/apps/server/src/index.js +1 -1
  27. package/dist/apps/server/src/routes/config.js +1 -1
  28. package/dist/apps/server/src/services/config/index.d.ts +2 -8
  29. package/dist/apps/server/src/services/config/index.js +3 -39
  30. package/dist/apps/server/src/services/session/index.js +1 -1
  31. package/dist/apps/server/src/services/session/notification.js +1 -1
  32. package/dist/packages/adapters/claude-code/__tests__/default-config.spec.js +15 -0
  33. package/dist/packages/adapters/claude-code/__tests__/prepare.spec.js +61 -1
  34. package/dist/packages/adapters/claude-code/__tests__/router-daemon.spec.d.ts +1 -0
  35. package/dist/packages/adapters/claude-code/__tests__/router-daemon.spec.js +183 -0
  36. package/dist/packages/adapters/claude-code/src/adapter-config.d.ts +1 -0
  37. package/dist/packages/adapters/claude-code/src/runtime/init.js +0 -45
  38. package/dist/packages/adapters/claude-code/src/runtime/prepare.d.ts +6 -9
  39. package/dist/packages/adapters/claude-code/src/runtime/prepare.js +25 -27
  40. package/dist/packages/adapters/claude-code/src/runtime/router-daemon.d.ts +19 -0
  41. package/dist/packages/adapters/claude-code/src/runtime/router-daemon.js +189 -0
  42. package/dist/packages/channels/lark/src/index.d.ts +4 -4
  43. package/dist/packages/config/__tests__/load.spec.js +219 -2
  44. package/dist/packages/config/__tests__/merge.spec.d.ts +1 -0
  45. package/dist/packages/config/__tests__/merge.spec.js +92 -0
  46. package/dist/packages/config/src/index.d.ts +1 -0
  47. package/dist/packages/config/src/index.js +1 -0
  48. package/dist/packages/config/src/load.d.ts +1 -1
  49. package/dist/packages/config/src/load.js +167 -53
  50. package/dist/packages/config/src/merge.d.ts +7 -0
  51. package/dist/packages/config/src/merge.js +92 -0
  52. package/dist/packages/tsconfigs/tsconfig.bundler.test.tsbuildinfo +1 -1
  53. package/dist/packages/tsconfigs/tsconfig.bundler.tsbuildinfo +1 -1
  54. package/dist/packages/tsconfigs/tsconfig.bundler.web.test.tsbuildinfo +1 -1
  55. package/dist/packages/tsconfigs/tsconfig.bundler.web.tsbuildinfo +1 -1
  56. package/dist/packages/tsconfigs/tsconfig.node.test.tsbuildinfo +1 -1
  57. package/dist/packages/tsconfigs/tsconfig.node.tsbuildinfo +1 -1
  58. package/dist/packages/types/src/config.d.ts +1 -0
  59. package/dist/packages/workspace-assets/__tests__/adapter-asset-plan.spec.d.ts +1 -0
  60. package/dist/packages/workspace-assets/__tests__/adapter-asset-plan.spec.js +121 -0
  61. package/dist/packages/workspace-assets/__tests__/bundle.spec.d.ts +1 -0
  62. package/dist/packages/workspace-assets/__tests__/bundle.spec.js +61 -0
  63. package/dist/packages/workspace-assets/__tests__/prompt-selection.spec.d.ts +1 -0
  64. package/dist/packages/workspace-assets/__tests__/prompt-selection.spec.js +29 -0
  65. package/dist/packages/workspace-assets/__tests__/snapshot.d.ts +15 -0
  66. package/dist/packages/workspace-assets/__tests__/snapshot.js +203 -0
  67. package/dist/packages/workspace-assets/__tests__/test-helpers.d.ts +2 -0
  68. package/dist/packages/workspace-assets/__tests__/test-helpers.js +17 -0
  69. package/dist/packages/workspace-assets/__tests__/workspace-assets.snapshot.spec.d.ts +1 -0
  70. package/dist/packages/workspace-assets/__tests__/workspace-assets.snapshot.spec.js +172 -0
  71. package/package.json +1 -1
@@ -1,8 +1,20 @@
1
- import { existsSync } from 'node:fs';
1
+ import { existsSync, statSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
- import { resolve } from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, extname, resolve } from 'node:path';
4
5
  import process from 'node:process';
5
6
  import { load } from 'js-yaml';
7
+ import { mergeConfigs } from './merge';
8
+ const CONFIG_FILE_EXTENSIONS = new Set([
9
+ '.json',
10
+ '.yaml',
11
+ '.yml'
12
+ ]);
13
+ const PACKAGE_DEFAULT_CONFIG_FILES = [
14
+ '.ai.config.json',
15
+ '.ai.config.yaml',
16
+ '.ai.config.yml'
17
+ ];
6
18
  const serializeJsonVariables = (value) => (JSON.stringify(Object.entries(value)
7
19
  .sort(([left], [right]) => left.localeCompare(right))));
8
20
  const resolveConfigCacheKey = (options) => {
@@ -12,54 +24,160 @@ const resolveConfigCacheKey = (options) => {
12
24
  return `${cwd}\n${disableDevConfig}\n${jsonVariables}`;
13
25
  };
14
26
  const resolveConfigPath = (cwd, filePath) => resolve(cwd, filePath);
27
+ const isExistingFilePath = (filePath) => {
28
+ if (!existsSync(filePath))
29
+ return false;
30
+ try {
31
+ return statSync(filePath).isFile();
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ };
37
+ const replaceJsonVariables = (content, jsonVariables) => (content.replace(/\$\{(\w+)\}/g, (_, key) => jsonVariables[key] ?? `$\{${key}}`));
15
38
  export const buildConfigJsonVariables = (cwd, env = process.env) => ({
16
39
  ...env,
17
40
  WORKSPACE_FOLDER: cwd,
18
41
  __VF_PROJECT_WORKSPACE_FOLDER__: cwd
19
42
  });
20
- const loadJSConfig = async (cwd, paths) => {
21
- for (const path of paths) {
22
- try {
23
- const configPath = resolveConfigPath(cwd, path);
24
- if (!existsSync(configPath)) {
25
- continue;
26
- }
27
- // eslint-disable-next-line ts/no-require-imports
28
- return (require(configPath)?.default ?? {});
43
+ const isRecord = (value) => (value != null &&
44
+ typeof value === 'object' &&
45
+ !Array.isArray(value));
46
+ const toExtendPaths = (value) => {
47
+ if (typeof value === 'string' && value.trim() !== '') {
48
+ return [value.trim()];
49
+ }
50
+ if (!Array.isArray(value))
51
+ return [];
52
+ return value
53
+ .filter((item) => typeof item === 'string' && item.trim() !== '')
54
+ .map(item => item.trim());
55
+ };
56
+ const omitExtendField = (value) => {
57
+ const { extend: _extend, ...rest } = value;
58
+ return rest;
59
+ };
60
+ const resolveExtendCandidates = (configPath, extendPath) => {
61
+ const resolvedPath = resolve(dirname(configPath), extendPath);
62
+ if (extname(resolvedPath) !== '')
63
+ return [resolvedPath];
64
+ return [
65
+ resolvedPath,
66
+ `${resolvedPath}.json`,
67
+ `${resolvedPath}.yaml`,
68
+ `${resolvedPath}.yml`
69
+ ];
70
+ };
71
+ const parsePackageSpecifier = (specifier) => {
72
+ if (specifier.trim() === '' ||
73
+ specifier.startsWith('.') ||
74
+ specifier.startsWith('/') ||
75
+ /^[A-Za-z]:[\\/]/.test(specifier)) {
76
+ return undefined;
77
+ }
78
+ const segments = specifier.split('/');
79
+ if (segments.length === 0)
80
+ return undefined;
81
+ if (specifier.startsWith('@')) {
82
+ const [scope, name, ...rest] = segments;
83
+ if (!scope || !name)
84
+ return undefined;
85
+ return {
86
+ packageName: `${scope}/${name}`,
87
+ subpath: rest.length > 0 ? rest.join('/') : undefined
88
+ };
89
+ }
90
+ const [name, ...rest] = segments;
91
+ if (!name)
92
+ return undefined;
93
+ return {
94
+ packageName: name,
95
+ subpath: rest.length > 0 ? rest.join('/') : undefined
96
+ };
97
+ };
98
+ const resolveConfigCandidatesFromBasePath = (basePath) => (extname(basePath) !== ''
99
+ ? [basePath]
100
+ : [
101
+ basePath,
102
+ `${basePath}.json`,
103
+ `${basePath}.yaml`,
104
+ `${basePath}.yml`
105
+ ]);
106
+ const resolveDependencyExtendPath = (configPath, extendPath) => {
107
+ const resolver = createRequire(configPath);
108
+ try {
109
+ const directResolvedPath = resolver.resolve(extendPath);
110
+ if (isExistingFilePath(directResolvedPath) &&
111
+ CONFIG_FILE_EXTENSIONS.has(extname(directResolvedPath).toLowerCase())) {
112
+ return directResolvedPath;
29
113
  }
30
- catch (e) {
31
- console.error(`Failed to load config file ${path}: ${e}`);
114
+ }
115
+ catch { }
116
+ const parsed = parsePackageSpecifier(extendPath);
117
+ if (parsed == null)
118
+ return undefined;
119
+ try {
120
+ const packageJsonPath = resolver.resolve(`${parsed.packageName}/package.json`);
121
+ const packageRoot = dirname(packageJsonPath);
122
+ if (parsed.subpath == null) {
123
+ return PACKAGE_DEFAULT_CONFIG_FILES
124
+ .map(fileName => resolve(packageRoot, fileName))
125
+ .find(candidate => isExistingFilePath(candidate));
32
126
  }
127
+ return resolveConfigCandidatesFromBasePath(resolve(packageRoot, parsed.subpath))
128
+ .find(candidate => isExistingFilePath(candidate));
129
+ }
130
+ catch {
131
+ return undefined;
33
132
  }
34
133
  };
35
- const loadJSONConfig = async (cwd, paths, jsonVariables) => {
36
- for (const path of paths) {
37
- try {
38
- const configPath = resolveConfigPath(cwd, path);
39
- if (!existsSync(configPath)) {
40
- continue;
41
- }
42
- const configContent = await readFile(configPath, 'utf-8');
43
- const configResolvedContent = configContent
44
- .replace(/\$\{(\w+)\}/g, (_, key) => jsonVariables[key] ?? `$\{${key}}`);
45
- return JSON.parse(configResolvedContent);
46
- }
47
- catch (e) {
48
- console.error(`Failed to load config file ${path}: ${e}`);
134
+ const resolveExistingExtendPath = (configPath, extendPath) => (resolveExtendCandidates(configPath, extendPath)
135
+ .find(candidate => isExistingFilePath(candidate)) ??
136
+ resolveDependencyExtendPath(configPath, extendPath));
137
+ const readConfigFile = async (configPath, jsonVariables) => {
138
+ const configContent = await readFile(configPath, 'utf-8');
139
+ const configResolvedContent = replaceJsonVariables(configContent, jsonVariables);
140
+ const extension = extname(configPath).toLowerCase();
141
+ if (extension === '.json') {
142
+ return JSON.parse(configResolvedContent);
143
+ }
144
+ if (extension === '.yaml' || extension === '.yml') {
145
+ return load(configResolvedContent);
146
+ }
147
+ throw new Error(`Unsupported config file extension "${extension || '<none>'}"`);
148
+ };
149
+ const loadResolvedConfigFile = async (configPath, jsonVariables, loadingStack) => {
150
+ if (loadingStack.has(configPath)) {
151
+ throw new Error(`Circular config extend detected: ${[
152
+ ...loadingStack,
153
+ configPath
154
+ ].join(' -> ')}`);
155
+ }
156
+ const rawConfig = await readConfigFile(configPath, jsonVariables);
157
+ if (!isRecord(rawConfig)) {
158
+ throw new Error(`Config file "${configPath}" must resolve to an object`);
159
+ }
160
+ const nextLoadingStack = new Set(loadingStack);
161
+ nextLoadingStack.add(configPath);
162
+ let mergedExtendedConfig;
163
+ for (const extendPath of toExtendPaths(rawConfig.extend)) {
164
+ const extendedConfigPath = resolveExistingExtendPath(configPath, extendPath);
165
+ if (extendedConfigPath == null) {
166
+ throw new Error(`Extended config "${extendPath}" not found from "${configPath}"`);
49
167
  }
168
+ const extendedConfig = await loadResolvedConfigFile(extendedConfigPath, jsonVariables, nextLoadingStack);
169
+ mergedExtendedConfig = mergeConfigs(mergedExtendedConfig, extendedConfig);
50
170
  }
171
+ return mergeConfigs(mergedExtendedConfig, omitExtendField(rawConfig));
51
172
  };
52
- const loadYAMLConfig = async (cwd, paths, jsonVariables) => {
173
+ const loadConfigFromPaths = async (cwd, paths, jsonVariables) => {
53
174
  for (const path of paths) {
54
175
  try {
55
176
  const configPath = resolveConfigPath(cwd, path);
56
- if (!existsSync(configPath)) {
177
+ if (!isExistingFilePath(configPath)) {
57
178
  continue;
58
179
  }
59
- const configContent = await readFile(configPath, 'utf-8');
60
- const configResolvedContent = configContent
61
- .replace(/\$\{(\w+)\}/g, (_, key) => jsonVariables[key] ?? `$\{${key}}`);
62
- return load(configResolvedContent);
180
+ return await loadResolvedConfigFile(configPath, jsonVariables, new Set());
63
181
  }
64
182
  catch (e) {
65
183
  console.error(`Failed to load config file ${path}: ${e}`);
@@ -86,30 +204,26 @@ export const loadConfig = (options = {}) => {
86
204
  return cachedConfig;
87
205
  }
88
206
  const cwd = options.cwd ?? process.cwd();
89
- const shouldLoadDevConfig = (options.disableDevConfig !== true &&
90
- process.env[DISABLE_DEV_CONFIG_ENV] !== '1');
207
+ const shouldLoadDevConfig = options.disableDevConfig !== true &&
208
+ process.env[DISABLE_DEV_CONFIG_ENV] !== '1';
91
209
  const nextConfig = (async () => [
92
- await loadJSONConfig(cwd, [
210
+ await loadConfigFromPaths(cwd, [
93
211
  './.ai.config.json',
94
- './infra/.ai.config.json'
95
- ], options.jsonVariables ?? {}) ??
96
- await loadYAMLConfig(cwd, [
97
- './.ai.config.yaml',
98
- './.ai.config.yml',
99
- './infra/.ai.config.yaml',
100
- './infra/.ai.config.yml'
101
- ], options.jsonVariables ?? {}),
212
+ './infra/.ai.config.json',
213
+ './.ai.config.yaml',
214
+ './.ai.config.yml',
215
+ './infra/.ai.config.yaml',
216
+ './infra/.ai.config.yml'
217
+ ], options.jsonVariables ?? {}),
102
218
  shouldLoadDevConfig
103
- ? await loadJSONConfig(cwd, [
219
+ ? await loadConfigFromPaths(cwd, [
104
220
  './.ai.dev.config.json',
105
- './infra/.ai.dev.config.json'
106
- ], options.jsonVariables ?? {}) ??
107
- await loadYAMLConfig(cwd, [
108
- './.ai.dev.config.yaml',
109
- './.ai.dev.config.yml',
110
- './infra/.ai.dev.config.yaml',
111
- './infra/.ai.dev.config.yml'
112
- ], options.jsonVariables ?? {})
221
+ './infra/.ai.dev.config.json',
222
+ './.ai.dev.config.yaml',
223
+ './.ai.dev.config.yml',
224
+ './infra/.ai.dev.config.yaml',
225
+ './infra/.ai.dev.config.yml'
226
+ ], options.jsonVariables ?? {})
113
227
  : undefined
114
228
  ])();
115
229
  configCache.set(cacheKey, nextConfig);
@@ -0,0 +1,7 @@
1
+ import type { Config } from '@vibe-forge/types';
2
+ export declare const mergeRecord: <T>(left?: Record<string, T>, right?: Record<string, T>) => {
3
+ [x: string]: T;
4
+ } | undefined;
5
+ export declare const mergeList: <T>(left?: T[], right?: T[]) => T[] | undefined;
6
+ export declare const mergeUniqueList: <T>(left?: T[], right?: T[]) => T[] | undefined;
7
+ export declare const mergeConfigs: <T extends Partial<Config> | undefined>(left: T, right: T) => T;
@@ -0,0 +1,92 @@
1
+ import { mergeAdapterConfigs } from '@vibe-forge/utils';
2
+ const hasOwnKeys = (value) => Object.keys(value).length > 0;
3
+ const isRecord = (value) => (value != null &&
4
+ typeof value === 'object' &&
5
+ !Array.isArray(value));
6
+ export const mergeRecord = (left, right) => {
7
+ if (left == null && right == null)
8
+ return undefined;
9
+ return {
10
+ ...(left ?? {}),
11
+ ...(right ?? {})
12
+ };
13
+ };
14
+ export const mergeList = (left, right) => {
15
+ if (left == null && right == null)
16
+ return undefined;
17
+ return [
18
+ ...(left ?? []),
19
+ ...(right ?? [])
20
+ ];
21
+ };
22
+ export const mergeUniqueList = (left, right) => {
23
+ const merged = mergeList(left, right);
24
+ return merged == null ? undefined : Array.from(new Set(merged));
25
+ };
26
+ const mergeNotificationEventConfigs = (left, right) => {
27
+ const keys = new Set([
28
+ ...Object.keys(left ?? {}),
29
+ ...Object.keys(right ?? {})
30
+ ]);
31
+ if (keys.size === 0)
32
+ return undefined;
33
+ const merged = Object.fromEntries(Array.from(keys).map((key) => [
34
+ key,
35
+ {
36
+ ...(left?.[key] ?? {}),
37
+ ...(right?.[key] ?? {})
38
+ }
39
+ ]));
40
+ return hasOwnKeys(merged) ? merged : undefined;
41
+ };
42
+ const mergeNotifications = (left, right) => {
43
+ if (left == null && right == null)
44
+ return undefined;
45
+ const merged = {
46
+ ...(left ?? {}),
47
+ ...(right ?? {}),
48
+ events: mergeNotificationEventConfigs(left?.events, right?.events)
49
+ };
50
+ return hasOwnKeys(merged) ? merged : undefined;
51
+ };
52
+ const mergePermissions = (left, right) => {
53
+ if (left == null && right == null)
54
+ return undefined;
55
+ const merged = {
56
+ ...(left ?? {}),
57
+ ...(right ?? {}),
58
+ allow: mergeList(left?.allow, right?.allow),
59
+ deny: mergeList(left?.deny, right?.deny),
60
+ ask: mergeList(left?.ask, right?.ask)
61
+ };
62
+ return hasOwnKeys(merged) ? merged : undefined;
63
+ };
64
+ const mergePluginConfigs = (left, right) => {
65
+ if (!isRecord(left) || !isRecord(right)) {
66
+ return right ?? left;
67
+ }
68
+ return mergeRecord(left, right);
69
+ };
70
+ export const mergeConfigs = (left, right) => {
71
+ const merged = {
72
+ ...(left ?? {}),
73
+ ...(right ?? {}),
74
+ adapters: mergeAdapterConfigs(left?.adapters, right?.adapters),
75
+ models: mergeRecord(left?.models, right?.models),
76
+ modelServices: mergeRecord(left?.modelServices, right?.modelServices),
77
+ channels: mergeRecord(left?.channels, right?.channels),
78
+ mcpServers: mergeRecord(left?.mcpServers, right?.mcpServers),
79
+ defaultIncludeMcpServers: mergeUniqueList(left?.defaultIncludeMcpServers, right?.defaultIncludeMcpServers),
80
+ defaultExcludeMcpServers: mergeUniqueList(left?.defaultExcludeMcpServers, right?.defaultExcludeMcpServers),
81
+ permissions: mergePermissions(left?.permissions, right?.permissions),
82
+ env: mergeRecord(left?.env, right?.env),
83
+ announcements: mergeList(left?.announcements, right?.announcements),
84
+ shortcuts: mergeRecord(left?.shortcuts, right?.shortcuts),
85
+ conversation: mergeRecord(left?.conversation, right?.conversation),
86
+ notifications: mergeNotifications(left?.notifications, right?.notifications),
87
+ plugins: mergePluginConfigs(left?.plugins, right?.plugins),
88
+ enabledPlugins: mergeRecord(left?.enabledPlugins, right?.enabledPlugins),
89
+ extraKnownMarketplaces: mergeRecord(left?.extraKnownMarketplaces, right?.extraKnownMarketplaces)
90
+ };
91
+ return merged;
92
+ };