taskdex 0.1.0 → 0.1.2

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 CHANGED
@@ -35,6 +35,8 @@ taskdex init
35
35
  ```
36
36
 
37
37
  This will clone the repo and immediately run interactive terminal setup.
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
+ When a `pnpm-lock.yaml` is present in `mobile`, the CLI installs mobile dependencies with pnpm automatically.
38
40
 
39
41
  For an already-cloned repo:
40
42
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskdex",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Terminal-first installer and launcher for Taskdex mobile + bridge",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,6 +43,16 @@ function runCommand(command, args, options = {}) {
43
43
  });
44
44
  }
45
45
 
46
+ async function installMobileDependencies(mobileDir) {
47
+ const hasPnpmLock = existsSync(path.join(mobileDir, 'pnpm-lock.yaml'));
48
+ if (hasPnpmLock) {
49
+ // Expo mobile app in this repo is pnpm-based; npm can fail on some npm/arborist versions.
50
+ await runCommand(npxCmd, ['-y', 'pnpm@9', 'install'], { cwd: mobileDir });
51
+ return;
52
+ }
53
+ await runCommand(npmCmd, ['install'], { cwd: mobileDir });
54
+ }
55
+
46
56
  function isTaskdexRepo(dir) {
47
57
  return (
48
58
  existsSync(path.join(dir, 'bridge-server')) &&
@@ -134,6 +144,22 @@ function parseExpoMode(input) {
134
144
  return normalized;
135
145
  }
136
146
 
147
+ function parseRuntime(input) {
148
+ const normalized = input.trim().toLowerCase() || 'dev-client';
149
+ if (['dev-client', 'dev', 'devbuild', 'dev-build'].includes(normalized)) return 'dev-client';
150
+ if (['expo-go', 'go', 'expo'].includes(normalized)) return 'expo-go';
151
+ throw new Error('Runtime must be "dev-client" or "expo-go".');
152
+ }
153
+
154
+ function parseBuildPlatform(input) {
155
+ const fallback = process.platform === 'darwin' ? 'ios' : 'android';
156
+ const normalized = input.trim().toLowerCase() || fallback;
157
+ if (!['ios', 'android'].includes(normalized)) {
158
+ throw new Error('Build platform must be "ios" or "android".');
159
+ }
160
+ return normalized;
161
+ }
162
+
137
163
  function parseInstallChoice(input) {
138
164
  const normalized = input.trim().toLowerCase();
139
165
  if (!normalized || normalized === 'y' || normalized === 'yes') return true;
@@ -152,12 +178,27 @@ async function readSetupConfig() {
152
178
  const portInput = await rl.question('Bridge port [3001]: ');
153
179
  const apiKeyInput = await rl.question('Bridge API key [auto-generate]: ');
154
180
  const expoModeInput = await rl.question('Expo mode (lan/tunnel) [lan]: ');
181
+ const runtimeInput = await rl.question('App runtime (dev-client/expo-go) [dev-client]: ');
155
182
  const installInput = await rl.question('Install npm dependencies first? [Y/n]: ');
183
+ const runtime = parseRuntime(runtimeInput);
184
+ let buildDevClient = false;
185
+ let buildPlatform = process.platform === 'darwin' ? 'ios' : 'android';
186
+ if (runtime === 'dev-client') {
187
+ const buildInput = await rl.question('Build native development client now? [Y/n]: ');
188
+ buildDevClient = parseInstallChoice(buildInput);
189
+ if (buildDevClient) {
190
+ const platformInput = await rl.question(`Build platform (ios/android) [${buildPlatform}]: `);
191
+ buildPlatform = parseBuildPlatform(platformInput);
192
+ }
193
+ }
156
194
 
157
195
  return {
158
196
  port: parsePort((portInput || '3001').trim()),
159
197
  apiKey: apiKeyInput.trim() || crypto.randomBytes(24).toString('hex'),
160
198
  expoMode: parseExpoMode(expoModeInput),
199
+ runtime,
200
+ buildDevClient,
201
+ buildPlatform,
161
202
  installDeps: parseInstallChoice(installInput),
162
203
  };
163
204
  } finally {
@@ -172,7 +213,7 @@ async function runInteractiveSetup(rootDir) {
172
213
  throw new Error(`Invalid Taskdex repository at ${rootDir}`);
173
214
  }
174
215
 
175
- const { port, apiKey, expoMode, installDeps } = await readSetupConfig();
216
+ const { port, apiKey, expoMode, runtime, buildDevClient, buildPlatform, installDeps } = await readSetupConfig();
176
217
  const bridgeUrl = `ws://${getLocalIPv4()}:${port}`;
177
218
 
178
219
  console.log(`\nBridge URL: ${bridgeUrl}`);
@@ -182,8 +223,10 @@ async function runInteractiveSetup(rootDir) {
182
223
  if (installDeps) {
183
224
  console.log('Installing bridge dependencies...\n');
184
225
  await runCommand(npmCmd, ['install'], { cwd: bridgeDir });
226
+ // Keep older clones working where this dependency might be missing.
227
+ await runCommand(npmCmd, ['install', 'qrcode-terminal@^0.12.0'], { cwd: bridgeDir });
185
228
  console.log('\nInstalling mobile dependencies...\n');
186
- await runCommand(npmCmd, ['install'], { cwd: mobileDir });
229
+ await installMobileDependencies(mobileDir);
187
230
  }
188
231
 
189
232
  console.log('\nStarting bridge server...\n');
@@ -204,18 +247,32 @@ async function runInteractiveSetup(rootDir) {
204
247
 
205
248
  await wait(1200);
206
249
 
250
+ const expoEnv = {
251
+ ...process.env,
252
+ EXPO_PUBLIC_BRIDGE_URL: bridgeUrl,
253
+ EXPO_PUBLIC_BRIDGE_API_KEY: apiKey,
254
+ };
255
+
256
+ if (runtime === 'dev-client' && buildDevClient) {
257
+ console.log(`\nBuilding native dev client (${buildPlatform})...\n`);
258
+ await runCommand(npxCmd, ['expo', `run:${buildPlatform}`, '--no-bundler'], {
259
+ cwd: mobileDir,
260
+ env: expoEnv,
261
+ });
262
+ }
263
+
207
264
  console.log('\nStarting Expo. Scan the QR code to open the app.');
208
265
  console.log('Bridge URL and API key are prefilled from this terminal setup.\n');
266
+ if (runtime === 'dev-client' && !buildDevClient) {
267
+ console.log('If dev client is not installed yet, rerun setup and enable native build.\n');
268
+ }
269
+
270
+ const expoArgs = runtime === 'dev-client'
271
+ ? ['expo', 'start', '--dev-client', `--${expoMode}`]
272
+ : ['expo', 'start', `--${expoMode}`];
209
273
 
210
274
  try {
211
- await runCommand(npxCmd, ['expo', 'start', `--${expoMode}`], {
212
- cwd: mobileDir,
213
- env: {
214
- ...process.env,
215
- EXPO_PUBLIC_BRIDGE_URL: bridgeUrl,
216
- EXPO_PUBLIC_BRIDGE_API_KEY: apiKey,
217
- },
218
- });
275
+ await runCommand(npxCmd, expoArgs, { cwd: mobileDir, env: expoEnv });
219
276
  } finally {
220
277
  if (!bridgeProcess.killed) {
221
278
  bridgeProcess.kill('SIGTERM');