@agenteract/core 0.0.3 → 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.
Files changed (41) hide show
  1. package/dist/cjs/package.json +3 -1
  2. package/dist/cjs/src/config-types.d.ts +29 -0
  3. package/dist/cjs/src/config-types.js +2 -0
  4. package/dist/cjs/src/index.d.ts +1 -0
  5. package/dist/cjs/src/index.js +15 -0
  6. package/dist/cjs/src/node/config.d.ts +52 -0
  7. package/dist/cjs/src/node/config.js +324 -0
  8. package/dist/cjs/src/node/index.d.ts +2 -0
  9. package/dist/cjs/src/node/index.js +18 -0
  10. package/dist/cjs/src/node/pnpm.d.ts +5 -0
  11. package/dist/cjs/src/node/pnpm.js +12 -0
  12. package/dist/esm/package.json +3 -1
  13. package/dist/esm/src/config-types.d.ts +29 -0
  14. package/dist/esm/src/config-types.js +1 -0
  15. package/dist/esm/src/index.d.ts +1 -0
  16. package/dist/esm/src/index.js +1 -0
  17. package/dist/esm/src/node/config.d.ts +52 -0
  18. package/dist/esm/src/node/config.js +303 -0
  19. package/dist/esm/src/node/index.d.ts +2 -0
  20. package/dist/esm/src/node/index.js +2 -0
  21. package/dist/esm/src/node/pnpm.d.ts +5 -0
  22. package/dist/esm/src/node/pnpm.js +9 -0
  23. package/dist/src/config-types.d.ts +29 -0
  24. package/dist/src/config-types.js +1 -0
  25. package/dist/src/index.d.ts +1 -0
  26. package/dist/src/index.js +1 -0
  27. package/dist/src/node/config.d.ts +52 -0
  28. package/dist/src/node/config.js +303 -0
  29. package/dist/src/node/index.d.ts +2 -0
  30. package/dist/src/node/index.js +2 -0
  31. package/dist/src/node/pnpm.d.ts +5 -0
  32. package/dist/src/node/pnpm.js +9 -0
  33. package/package.json +15 -3
  34. package/NOTICE +0 -4
  35. package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
  36. package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
  37. package/dist/tsconfig.tsbuildinfo +0 -1
  38. package/src/index.ts +0 -43
  39. package/tsconfig.cjs.json +0 -8
  40. package/tsconfig.esm.json +0 -8
  41. package/tsconfig.json +0 -11
@@ -0,0 +1,303 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { randomUUID } from 'crypto';
4
+ export function getRuntimeConfigPath(cwd = process.cwd()) {
5
+ return path.join(cwd, '.agenteract-runtime.json');
6
+ }
7
+ export async function saveRuntimeConfig(config, cwd = process.cwd()) {
8
+ await fs.writeFile(getRuntimeConfigPath(cwd), JSON.stringify(config, null, 2));
9
+ }
10
+ export async function loadRuntimeConfig(cwd = process.cwd()) {
11
+ try {
12
+ const content = await fs.readFile(getRuntimeConfigPath(cwd), 'utf-8');
13
+ return JSON.parse(content);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export async function deleteRuntimeConfig(cwd = process.cwd()) {
20
+ try {
21
+ await fs.unlink(getRuntimeConfigPath(cwd));
22
+ }
23
+ catch {
24
+ // Ignore if file doesn't exist
25
+ }
26
+ }
27
+ export function generateAuthToken() {
28
+ return randomUUID();
29
+ }
30
+ /**
31
+ * Set the default device for a project
32
+ */
33
+ export async function setDefaultDevice(projectName, deviceId, cwd = process.cwd()) {
34
+ const config = await loadRuntimeConfig(cwd);
35
+ if (!config) {
36
+ throw new Error('Runtime config not found. Is the server running?');
37
+ }
38
+ if (!config.defaultDevices) {
39
+ config.defaultDevices = {};
40
+ }
41
+ config.defaultDevices[projectName] = deviceId;
42
+ await saveRuntimeConfig(config, cwd);
43
+ }
44
+ /**
45
+ * Get the default device for a project
46
+ */
47
+ export async function getDefaultDevice(projectName, cwd = process.cwd()) {
48
+ const config = await loadRuntimeConfig(cwd);
49
+ return config?.defaultDevices?.[projectName];
50
+ }
51
+ export class MissingConfigError extends Error {
52
+ constructor(message) {
53
+ super(message);
54
+ this.name = 'MissingConfigError';
55
+ }
56
+ }
57
+ export async function loadConfig(rootDir) {
58
+ const configPath = path.join(rootDir, 'agenteract.config.js');
59
+ try {
60
+ await fs.access(configPath);
61
+ }
62
+ catch (error) {
63
+ throw new MissingConfigError('Agenteract config file not found');
64
+ }
65
+ // In a Jest environment, dynamic import() of file URLs can be tricky.
66
+ // A simple and effective workaround is to read the file and evaluate it.
67
+ // This avoids the module resolution issues within the test runner.
68
+ const configContent = await fs.readFile(configPath, 'utf-8');
69
+ // A simple regex to extract the default export object.
70
+ // This is not a full parser, but it's robust enough for our config file format.
71
+ const match = configContent.match(/export default (\{[\s\S]*\});/);
72
+ if (!match) {
73
+ console.error(`configContent: ${configContent}`);
74
+ throw new Error('Could not parse agenteract.config.js. Make sure it has a default export.');
75
+ }
76
+ // We can use Function to evaluate the object literal.
77
+ // It's safer than eval() because it doesn't have access to the outer scope.
78
+ return new Function(`return ${match[1]}`)();
79
+ }
80
+ /**
81
+ * Find the root directory containing agenteract.config.js
82
+ * Searches upward from the current working directory
83
+ */
84
+ export async function findConfigRoot(startDir = process.cwd()) {
85
+ let currentDir = startDir;
86
+ const root = path.parse(currentDir).root;
87
+ while (currentDir !== root) {
88
+ const configPath = path.join(currentDir, 'agenteract.config.js');
89
+ try {
90
+ await fs.access(configPath);
91
+ return currentDir;
92
+ }
93
+ catch {
94
+ currentDir = path.dirname(currentDir);
95
+ }
96
+ }
97
+ return null;
98
+ }
99
+ /**
100
+ * Get the URL for the agent server
101
+ */
102
+ export function getAgentServerUrl(config) {
103
+ return `http://localhost:${config.port || 8766}`;
104
+ }
105
+ /**
106
+ * Get the URL for a project's dev server
107
+ */
108
+ export function getProjectServerUrl(config, projectName) {
109
+ const project = config.projects.find(p => p.name === projectName);
110
+ if (!project)
111
+ return null;
112
+ // Check new devServer format first, then legacy ptyPort
113
+ const port = project.devServer?.port || project.ptyPort;
114
+ if (!port) {
115
+ return null;
116
+ }
117
+ return `http://localhost:${port}`;
118
+ }
119
+ /**
120
+ * Get the URL for a dev server by type
121
+ */
122
+ export function getDevServerUrlByType(config, type) {
123
+ const project = config.projects.find(p => p.type === type && (p.ptyPort || p.devServer?.port));
124
+ if (!project) {
125
+ return null;
126
+ }
127
+ const port = project.devServer?.port || project.ptyPort;
128
+ return `http://localhost:${port}`;
129
+ }
130
+ /**
131
+ * Type presets for backward compatibility
132
+ * Maps old 'type' field to new devServer configuration
133
+ */
134
+ export const TYPE_PRESETS = {
135
+ expo: {
136
+ command: 'npx expo start',
137
+ keyCommands: { reload: 'r', ios: 'i', android: 'a' }
138
+ },
139
+ vite: {
140
+ command: 'npx vite',
141
+ keyCommands: { reload: 'r', quit: 'q' }
142
+ },
143
+ flutter: {
144
+ command: 'flutter run',
145
+ validation: {
146
+ fileExists: ['pubspec.yaml'],
147
+ commandInPath: 'flutter',
148
+ errorHints: {
149
+ 'command not found': 'Install Flutter: https://flutter.dev/docs/get-started/install',
150
+ 'No pubspec.yaml': 'Flutter projects require a pubspec.yaml file in the project directory'
151
+ }
152
+ },
153
+ keyCommands: { reload: 'r', restart: 'R', quit: 'q', help: 'h' }
154
+ }
155
+ };
156
+ /**
157
+ * Get default PTY port for a given type
158
+ */
159
+ function getDefaultPortForType(type) {
160
+ const defaults = {
161
+ expo: 8790,
162
+ vite: 8791,
163
+ flutter: 8792
164
+ };
165
+ return defaults[type] || 8790;
166
+ }
167
+ export async function addConfig(rootDir, projectPath, name, typeOrCommand, port, scheme) {
168
+ const configPath = path.join(rootDir, 'agenteract.config.js');
169
+ let config;
170
+ try {
171
+ config = await loadConfig(rootDir);
172
+ }
173
+ catch (error) {
174
+ if (error instanceof MissingConfigError) {
175
+ config = { port: 8766, projects: [] };
176
+ }
177
+ else {
178
+ // For other errors (like parsing), we should not proceed.
179
+ throw error;
180
+ }
181
+ }
182
+ config.projects = config.projects || [];
183
+ let nameExists = config.projects.find((p) => p.name === name);
184
+ let pathExists = config.projects.find((p) => p.path === projectPath);
185
+ if ((nameExists || pathExists) && nameExists !== pathExists) {
186
+ console.error('project name and path exist across multiple projects. Please use a different name or path.');
187
+ console.error(`name: ${name}, path: ${projectPath}`);
188
+ return;
189
+ }
190
+ let update = nameExists || pathExists;
191
+ // Determine if this is legacy format (type) or new format (command)
192
+ const LEGACY_TYPES = ['expo', 'vite', 'flutter', 'native'];
193
+ const isLegacyFormat = LEGACY_TYPES.includes(typeOrCommand);
194
+ // Allocate a port if not provided
195
+ let ptyPort;
196
+ if (port) {
197
+ // Explicit port provided
198
+ ptyPort = port;
199
+ }
200
+ else if (update) {
201
+ // Reuse existing port when updating
202
+ ptyPort = update.ptyPort || update.devServer?.port || 8790;
203
+ }
204
+ else {
205
+ // Find next available port for new project
206
+ ptyPort = 8790;
207
+ while (config.projects.some((p) => (p.ptyPort === ptyPort) || (p.devServer?.port === ptyPort))) {
208
+ ptyPort++;
209
+ }
210
+ }
211
+ let newProjectConfig;
212
+ if (isLegacyFormat) {
213
+ // Legacy format: use old 'type' field for backwards compatibility
214
+ // Native apps don't have dev servers
215
+ if (typeOrCommand === 'native') {
216
+ newProjectConfig = {
217
+ name,
218
+ path: projectPath,
219
+ type: 'native',
220
+ ...(scheme && { scheme })
221
+ };
222
+ }
223
+ else {
224
+ // For non-native legacy types, create with new devServer format
225
+ const preset = TYPE_PRESETS[typeOrCommand];
226
+ if (!preset) {
227
+ console.error(`Unknown type '${typeOrCommand}'`);
228
+ return;
229
+ }
230
+ newProjectConfig = {
231
+ name,
232
+ path: projectPath,
233
+ devServer: {
234
+ ...preset,
235
+ port: ptyPort
236
+ },
237
+ ...(scheme && { scheme })
238
+ };
239
+ console.log(`ℹ️ Creating config with new devServer format (migrated from legacy type '${typeOrCommand}')`);
240
+ }
241
+ }
242
+ else {
243
+ // New format: generic dev server command
244
+ newProjectConfig = {
245
+ name,
246
+ path: projectPath,
247
+ devServer: {
248
+ command: typeOrCommand,
249
+ port: ptyPort
250
+ },
251
+ ...(scheme && { scheme })
252
+ };
253
+ }
254
+ // If the project already exists, replace it completely
255
+ if (update) {
256
+ // Find the index and replace the entire object to avoid keeping old fields
257
+ const index = config.projects.indexOf(update);
258
+ config.projects[index] = newProjectConfig;
259
+ }
260
+ else {
261
+ config.projects.push(newProjectConfig);
262
+ }
263
+ await fs.writeFile(configPath, `export default ${JSON.stringify(config, null, 2)};`);
264
+ console.log(`✅ Config updated: ${name} at ${projectPath}`);
265
+ if (newProjectConfig.devServer) {
266
+ console.log(` Dev server: ${newProjectConfig.devServer.command} (port: ${newProjectConfig.devServer.port})`);
267
+ }
268
+ if (scheme) {
269
+ console.log(` URL scheme: ${scheme}`);
270
+ }
271
+ }
272
+ /**
273
+ * Normalize project config: migrate old format to new format
274
+ * Logs deprecation warnings when using old 'type' field
275
+ */
276
+ export function normalizeProjectConfig(project, rootDir) {
277
+ // Already using new format
278
+ if (project.devServer) {
279
+ return project;
280
+ }
281
+ // Native type has no dev server
282
+ if (project.type === 'native') {
283
+ return project;
284
+ }
285
+ // Auto-migrate from old type-based format
286
+ if (project.type && project.type !== 'auto') {
287
+ console.warn(`⚠️ [${project.name}] Using deprecated 'type' field. ` +
288
+ `Migrate to 'devServer' config. See docs/MIGRATION_V2.md`);
289
+ const preset = TYPE_PRESETS[project.type];
290
+ if (!preset) {
291
+ console.error(`Unknown type '${project.type}' for project '${project.name}'`);
292
+ return project;
293
+ }
294
+ return {
295
+ ...project,
296
+ devServer: {
297
+ ...preset,
298
+ port: project.ptyPort || getDefaultPortForType(project.type)
299
+ }
300
+ };
301
+ }
302
+ return project;
303
+ }
@@ -0,0 +1,2 @@
1
+ export * from './config.js';
2
+ export * from './pnpm.js';
@@ -0,0 +1,2 @@
1
+ export * from './config.js';
2
+ export * from './pnpm.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * if we're running in the agenteract monorepo, set node's CWD back to the original working directory
3
+ * otherwise pnpm commands have CWD set to the monorepo root
4
+ */
5
+ export declare function resetPNPMWorkspaceCWD(): void;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * if we're running in the agenteract monorepo, set node's CWD back to the original working directory
3
+ * otherwise pnpm commands have CWD set to the monorepo root
4
+ */
5
+ export function resetPNPMWorkspaceCWD() {
6
+ if (process.env.PNPM_PACKAGE_NAME == 'agenteract' && process.env.PWD && process.env.PWD !== process.cwd()) {
7
+ process.chdir(process.env.PWD);
8
+ }
9
+ }
@@ -0,0 +1,29 @@
1
+ export interface DevServerConfig {
2
+ command: string;
3
+ port: number;
4
+ cwd?: string;
5
+ env?: Record<string, string>;
6
+ validation?: {
7
+ fileExists?: string[];
8
+ commandInPath?: string;
9
+ errorHints?: Record<string, string>;
10
+ };
11
+ keyCommands?: Record<string, string>;
12
+ }
13
+ export interface ProjectConfig {
14
+ name: string;
15
+ path: string;
16
+ type?: 'expo' | 'vite' | 'flutter' | 'native' | 'auto';
17
+ ptyPort?: number;
18
+ devServer?: DevServerConfig;
19
+ scheme?: string;
20
+ }
21
+ export interface AgenteractConfig {
22
+ server?: {
23
+ port?: number;
24
+ wsPort?: number;
25
+ logPort?: number;
26
+ };
27
+ port?: number;
28
+ projects: ProjectConfig[];
29
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -16,3 +16,4 @@ export declare function detectInvoker(): {
16
16
  isNpx: boolean;
17
17
  isPnpmDlx: boolean;
18
18
  };
19
+ export * from './config-types.js';
package/dist/src/index.js CHANGED
@@ -22,3 +22,4 @@ export function detectInvoker() {
22
22
  const isPnpmDlx = pkgManager === 'pnpm' && process.argv[1]?.includes('dlx');
23
23
  return { pkgManager, isNpx, isPnpmDlx };
24
24
  }
25
+ export * from './config-types.js';
@@ -0,0 +1,52 @@
1
+ import { AgenteractConfig, DevServerConfig, ProjectConfig } from '../config-types.js';
2
+ export interface RuntimeConfig {
3
+ host: string;
4
+ port: number;
5
+ token: string;
6
+ defaultDevices?: Record<string, string>;
7
+ }
8
+ export declare function getRuntimeConfigPath(cwd?: string): string;
9
+ export declare function saveRuntimeConfig(config: RuntimeConfig, cwd?: string): Promise<void>;
10
+ export declare function loadRuntimeConfig(cwd?: string): Promise<RuntimeConfig | null>;
11
+ export declare function deleteRuntimeConfig(cwd?: string): Promise<void>;
12
+ export declare function generateAuthToken(): string;
13
+ /**
14
+ * Set the default device for a project
15
+ */
16
+ export declare function setDefaultDevice(projectName: string, deviceId: string, cwd?: string): Promise<void>;
17
+ /**
18
+ * Get the default device for a project
19
+ */
20
+ export declare function getDefaultDevice(projectName: string, cwd?: string): Promise<string | undefined>;
21
+ export declare class MissingConfigError extends Error {
22
+ constructor(message: string);
23
+ }
24
+ export declare function loadConfig(rootDir: string): Promise<AgenteractConfig>;
25
+ /**
26
+ * Find the root directory containing agenteract.config.js
27
+ * Searches upward from the current working directory
28
+ */
29
+ export declare function findConfigRoot(startDir?: string): Promise<string | null>;
30
+ /**
31
+ * Get the URL for the agent server
32
+ */
33
+ export declare function getAgentServerUrl(config: AgenteractConfig): string;
34
+ /**
35
+ * Get the URL for a project's dev server
36
+ */
37
+ export declare function getProjectServerUrl(config: AgenteractConfig, projectName: string): string | null;
38
+ /**
39
+ * Get the URL for a dev server by type
40
+ */
41
+ export declare function getDevServerUrlByType(config: AgenteractConfig, type: 'expo' | 'vite' | 'flutter'): string | null;
42
+ /**
43
+ * Type presets for backward compatibility
44
+ * Maps old 'type' field to new devServer configuration
45
+ */
46
+ export declare const TYPE_PRESETS: Record<string, Omit<DevServerConfig, 'port'>>;
47
+ export declare function addConfig(rootDir: string, projectPath: string, name: string, typeOrCommand: string, port?: number, scheme?: string): Promise<void>;
48
+ /**
49
+ * Normalize project config: migrate old format to new format
50
+ * Logs deprecation warnings when using old 'type' field
51
+ */
52
+ export declare function normalizeProjectConfig(project: ProjectConfig, rootDir: string): ProjectConfig;