taskdex 0.1.2 → 0.1.4
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/README.md +2 -0
- package/package.json +1 -1
- package/scripts/taskdex.mjs +79 -3
package/README.md
CHANGED
|
@@ -36,7 +36,9 @@ taskdex init
|
|
|
36
36
|
|
|
37
37
|
This will clone the repo and immediately run interactive terminal setup.
|
|
38
38
|
Setup now defaults to `dev-client` runtime, can build the native app (`expo run:ios` / `expo run:android`), and then starts Expo in `--dev-client` mode.
|
|
39
|
+
For iOS builds, setup asks whether to run on a real iPhone or Simulator, and supports choosing a specific iPhone device name.
|
|
39
40
|
When a `pnpm-lock.yaml` is present in `mobile`, the CLI installs mobile dependencies with pnpm automatically.
|
|
41
|
+
Setup writes runtime bridge env values into `mobile/.env.local`, sets `CODEX_CWD` for bridge agent workspace, and can pass `OPENAI_API_KEY` if provided.
|
|
40
42
|
|
|
41
43
|
For an already-cloned repo:
|
|
42
44
|
|
package/package.json
CHANGED
package/scripts/taskdex.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
5
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import process from 'node:process';
|
|
@@ -160,6 +160,13 @@ function parseBuildPlatform(input) {
|
|
|
160
160
|
return normalized;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
function parseIosTarget(input) {
|
|
164
|
+
const normalized = input.trim().toLowerCase() || 'iphone';
|
|
165
|
+
if (['iphone', 'device', 'physical'].includes(normalized)) return 'iphone';
|
|
166
|
+
if (['simulator', 'sim', 'ios-simulator'].includes(normalized)) return 'simulator';
|
|
167
|
+
throw new Error('iOS target must be "iphone" or "simulator".');
|
|
168
|
+
}
|
|
169
|
+
|
|
163
170
|
function parseInstallChoice(input) {
|
|
164
171
|
const normalized = input.trim().toLowerCase();
|
|
165
172
|
if (!normalized || normalized === 'y' || normalized === 'yes') return true;
|
|
@@ -167,6 +174,26 @@ function parseInstallChoice(input) {
|
|
|
167
174
|
throw new Error('Install choice must be Y or N.');
|
|
168
175
|
}
|
|
169
176
|
|
|
177
|
+
function upsertEnvVars(envFilePath, values) {
|
|
178
|
+
const keys = Object.keys(values);
|
|
179
|
+
const original = existsSync(envFilePath) ? readFileSync(envFilePath, 'utf8') : '';
|
|
180
|
+
const lines = original ? original.split(/\r?\n/) : [];
|
|
181
|
+
const seen = new Set();
|
|
182
|
+
const updated = lines.map((line) => {
|
|
183
|
+
const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/);
|
|
184
|
+
if (!match) return line;
|
|
185
|
+
const key = match[1];
|
|
186
|
+
if (!keys.includes(key)) return line;
|
|
187
|
+
seen.add(key);
|
|
188
|
+
return `${key}=${values[key]}`;
|
|
189
|
+
});
|
|
190
|
+
for (const key of keys) {
|
|
191
|
+
if (!seen.has(key)) updated.push(`${key}=${values[key]}`);
|
|
192
|
+
}
|
|
193
|
+
const nextContent = `${updated.filter((line) => line !== '').join('\n')}\n`;
|
|
194
|
+
writeFileSync(envFilePath, nextContent, 'utf8');
|
|
195
|
+
}
|
|
196
|
+
|
|
170
197
|
function wait(ms) {
|
|
171
198
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
172
199
|
}
|
|
@@ -183,12 +210,23 @@ async function readSetupConfig() {
|
|
|
183
210
|
const runtime = parseRuntime(runtimeInput);
|
|
184
211
|
let buildDevClient = false;
|
|
185
212
|
let buildPlatform = process.platform === 'darwin' ? 'ios' : 'android';
|
|
213
|
+
let iosTarget = 'iphone';
|
|
214
|
+
let iosDeviceName = '';
|
|
215
|
+
const openAiApiKey = (await rl.question('OPENAI_API_KEY (optional, Enter to use current shell/Codex login): ')).trim();
|
|
216
|
+
const workspaceInput = (await rl.question('Agent workspace path [repo root]: ')).trim();
|
|
186
217
|
if (runtime === 'dev-client') {
|
|
187
218
|
const buildInput = await rl.question('Build native development client now? [Y/n]: ');
|
|
188
219
|
buildDevClient = parseInstallChoice(buildInput);
|
|
189
220
|
if (buildDevClient) {
|
|
190
221
|
const platformInput = await rl.question(`Build platform (ios/android) [${buildPlatform}]: `);
|
|
191
222
|
buildPlatform = parseBuildPlatform(platformInput);
|
|
223
|
+
if (buildPlatform === 'ios') {
|
|
224
|
+
const targetInput = await rl.question('iOS target (iphone/simulator) [iphone]: ');
|
|
225
|
+
iosTarget = parseIosTarget(targetInput);
|
|
226
|
+
if (iosTarget === 'iphone') {
|
|
227
|
+
iosDeviceName = (await rl.question('iPhone device name (optional, Enter to choose automatically): ')).trim();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
192
230
|
}
|
|
193
231
|
}
|
|
194
232
|
|
|
@@ -199,6 +237,10 @@ async function readSetupConfig() {
|
|
|
199
237
|
runtime,
|
|
200
238
|
buildDevClient,
|
|
201
239
|
buildPlatform,
|
|
240
|
+
iosTarget,
|
|
241
|
+
iosDeviceName,
|
|
242
|
+
openAiApiKey,
|
|
243
|
+
workspaceInput,
|
|
202
244
|
installDeps: parseInstallChoice(installInput),
|
|
203
245
|
};
|
|
204
246
|
} finally {
|
|
@@ -213,11 +255,34 @@ async function runInteractiveSetup(rootDir) {
|
|
|
213
255
|
throw new Error(`Invalid Taskdex repository at ${rootDir}`);
|
|
214
256
|
}
|
|
215
257
|
|
|
216
|
-
const {
|
|
258
|
+
const {
|
|
259
|
+
port,
|
|
260
|
+
apiKey,
|
|
261
|
+
expoMode,
|
|
262
|
+
runtime,
|
|
263
|
+
buildDevClient,
|
|
264
|
+
buildPlatform,
|
|
265
|
+
iosTarget,
|
|
266
|
+
iosDeviceName,
|
|
267
|
+
openAiApiKey,
|
|
268
|
+
workspaceInput,
|
|
269
|
+
installDeps,
|
|
270
|
+
} = await readSetupConfig();
|
|
217
271
|
const bridgeUrl = `ws://${getLocalIPv4()}:${port}`;
|
|
272
|
+
const workspacePath = workspaceInput ? path.resolve(workspaceInput) : rootDir;
|
|
273
|
+
|
|
274
|
+
if (!existsSync(workspacePath)) {
|
|
275
|
+
throw new Error(`Workspace path does not exist: ${workspacePath}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
upsertEnvVars(path.join(mobileDir, '.env.local'), {
|
|
279
|
+
EXPO_PUBLIC_BRIDGE_URL: bridgeUrl,
|
|
280
|
+
EXPO_PUBLIC_BRIDGE_API_KEY: apiKey,
|
|
281
|
+
});
|
|
218
282
|
|
|
219
283
|
console.log(`\nBridge URL: ${bridgeUrl}`);
|
|
220
284
|
console.log(`Bridge API key: ${apiKey}`);
|
|
285
|
+
console.log(`Agent workspace: ${workspacePath}`);
|
|
221
286
|
console.log(`Expo mode: ${expoMode}\n`);
|
|
222
287
|
|
|
223
288
|
if (installDeps) {
|
|
@@ -237,6 +302,8 @@ async function runInteractiveSetup(rootDir) {
|
|
|
237
302
|
...process.env,
|
|
238
303
|
PORT: String(port),
|
|
239
304
|
API_KEY: apiKey,
|
|
305
|
+
CODEX_CWD: workspacePath,
|
|
306
|
+
...(openAiApiKey ? { OPENAI_API_KEY: openAiApiKey } : {}),
|
|
240
307
|
},
|
|
241
308
|
});
|
|
242
309
|
|
|
@@ -255,7 +322,16 @@ async function runInteractiveSetup(rootDir) {
|
|
|
255
322
|
|
|
256
323
|
if (runtime === 'dev-client' && buildDevClient) {
|
|
257
324
|
console.log(`\nBuilding native dev client (${buildPlatform})...\n`);
|
|
258
|
-
|
|
325
|
+
const buildArgs = ['expo', `run:${buildPlatform}`, '--no-bundler'];
|
|
326
|
+
if (buildPlatform === 'ios' && iosTarget === 'iphone') {
|
|
327
|
+
if (iosDeviceName) {
|
|
328
|
+
buildArgs.push('--device', iosDeviceName);
|
|
329
|
+
} else {
|
|
330
|
+
buildArgs.push('--device');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
await runCommand(npxCmd, buildArgs, {
|
|
259
335
|
cwd: mobileDir,
|
|
260
336
|
env: expoEnv,
|
|
261
337
|
});
|