@oodarun/cli 0.1.3 → 0.1.5

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 (2) hide show
  1. package/dist/cli.js +77 -58
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1791,27 +1791,25 @@ async function deployToolsViaRest(projectName, token, projectRoot, onProgress) {
1791
1791
  log("Deploying browser overlay tools...");
1792
1792
  const toolsDistDir = getToolsDistDir();
1793
1793
  const toolFiles = ["element-selector.js", "change-applicator.js", "bridge-client.js", "toolbar.js"];
1794
+ const uploads = [];
1794
1795
  for (const file of toolFiles) {
1795
1796
  const localPath = path4.join(toolsDistDir, file);
1796
1797
  try {
1797
1798
  const content = fs4.readFileSync(localPath, "utf-8");
1798
- await writeFile(`/home/user/.ooda/tools/${file}`, content);
1799
+ uploads.push(writeFile(`/home/user/.ooda/tools/${file}`, content));
1799
1800
  } catch {
1800
1801
  log(`Warning: ${file} not found in dist/tools \u2014 skipping`);
1801
1802
  }
1802
1803
  }
1803
- await writeFile(`${projectRoot}/ooda-vite-plugin.mjs`, OODA_VITE_PLUGIN);
1804
- try {
1805
- await exec(`cd "${projectRoot}" && node -e "require('ws')" 2>/dev/null || npm install --save-dev ws 2>/dev/null`);
1806
- } catch {
1807
- }
1808
- await writeFile("/tmp/ooda-patch-config.mjs", OODA_CONFIG_PATCH_SCRIPT);
1809
- try {
1810
- const result = await exec(`node /tmp/ooda-patch-config.mjs "${projectRoot}" 2>&1`);
1811
- log(`Config patch: ${result.trim()}`);
1812
- } catch (err) {
1813
- log(`Config patch failed: ${err instanceof Error ? err.message : String(err)}`);
1814
- }
1804
+ uploads.push(writeFile(`${projectRoot}/ooda-vite-plugin.mjs`, OODA_VITE_PLUGIN));
1805
+ uploads.push(writeFile("/tmp/ooda-patch-config.mjs", OODA_CONFIG_PATCH_SCRIPT));
1806
+ await Promise.all(uploads);
1807
+ const patchResult = await exec(`node /tmp/ooda-patch-config.mjs "${projectRoot}" 2>&1`).catch((err) => {
1808
+ const msg = err instanceof Error ? err.message : String(err);
1809
+ log(`Config patch failed: ${msg}`);
1810
+ return "";
1811
+ });
1812
+ if (patchResult) log(`Config patch: ${String(patchResult).trim()}`);
1815
1813
  }
1816
1814
 
1817
1815
  // src/tools/installer.ts
@@ -2274,8 +2272,21 @@ async function createOrReuseProject(projectName, token, onProgress) {
2274
2272
  });
2275
2273
  if (res.ok) {
2276
2274
  const data = await res.json();
2277
- onProgress({ step: "Waiting for sandbox to start...", detail: "~10s" });
2278
- await new Promise((r) => setTimeout(r, 1e4));
2275
+ onProgress({ step: "Waiting for sandbox to start..." });
2276
+ for (let i = 0; i < 15; i++) {
2277
+ try {
2278
+ const params = new URLSearchParams();
2279
+ params.append("cmd", "echo");
2280
+ params.append("cmd", "ready");
2281
+ const check = await fetch(`${API_BASE}/projects/${projectName}/exec?${params}`, {
2282
+ method: "POST",
2283
+ headers
2284
+ });
2285
+ if (check.ok) break;
2286
+ } catch {
2287
+ }
2288
+ await new Promise((r) => setTimeout(r, 2e3));
2289
+ }
2279
2290
  return data.url;
2280
2291
  }
2281
2292
  if (res.status === 409) {
@@ -2972,6 +2983,8 @@ async function patchServerConfig(projectName, token, projectRoot, onProgress) {
2972
2983
  console.log("[project-provisioner] Server config patch result:", result.trim());
2973
2984
  }
2974
2985
  async function provisionFromFolder(localPath, projectName, token, onProgress, branchName, gitInfo, claudeToken, claudeEnv, githubToken) {
2986
+ const t0 = Date.now();
2987
+ const progress = (p) => onProgress({ ...p, step: `[+${((Date.now() - t0) / 1e3).toFixed(1)}s] ${p.step}` });
2975
2988
  const projectRoot = `/home/user/${path5.basename(localPath)}`;
2976
2989
  const headers = { Authorization: `Bearer ${token}` };
2977
2990
  async function exec(cmd) {
@@ -2996,20 +3009,20 @@ async function provisionFromFolder(localPath, projectName, token, onProgress, br
2996
3009
  try {
2997
3010
  const project = detectProject(localPath);
2998
3011
  console.log(`[project-provisioner] Detected: framework=${project.framework}, pm=${project.packageManager}, config=${project.configFile}`);
2999
- const projectUrl = await createOrReuseProject(projectName, token, onProgress);
3000
- const fileCount = await uploadFolder(localPath, projectRoot, projectName, token, onProgress);
3001
- onProgress({ step: "Upload complete", detail: `${fileCount} files` });
3012
+ const projectUrl = await createOrReuseProject(projectName, token, progress);
3013
+ const fileCount = await uploadFolder(localPath, projectRoot, projectName, token, progress);
3014
+ progress({ step: "Upload complete", detail: `${fileCount} files` });
3002
3015
  const effectiveClaudeToken = claudeToken || process.env.CLAUDE_CODE_OAUTH_TOKEN;
3003
3016
  if (effectiveClaudeToken) {
3004
- await setupClaudeAuth(projectName, token, effectiveClaudeToken, onProgress, claudeEnv);
3017
+ await setupClaudeAuth(projectName, token, effectiveClaudeToken, progress, claudeEnv);
3005
3018
  }
3006
- await deployClaudeConfig(projectName, token, projectRoot, project.framework, onProgress, gitInfo || null, branchName || null);
3019
+ await deployClaudeConfig(projectName, token, projectRoot, project.framework, progress, gitInfo || null, branchName || null);
3007
3020
  await writeRemoteFile("/home/user/.ooda/publish.mjs", PUBLISH_SCRIPT2);
3008
- await patchServerConfig(projectName, token, projectRoot, onProgress);
3009
- await installSourcePlugin(projectName, token, projectRoot, project.framework, onProgress);
3021
+ await patchServerConfig(projectName, token, projectRoot, progress);
3022
+ await installSourcePlugin(projectName, token, projectRoot, project.framework, progress);
3010
3023
  try {
3011
3024
  await deployToolsViaRest(projectName, token, projectRoot, (msg) => {
3012
- onProgress({ step: msg });
3025
+ progress({ step: msg });
3013
3026
  });
3014
3027
  } catch (err) {
3015
3028
  console.warn("[project-provisioner] Tool deployment failed:", err instanceof Error ? err.message : String(err));
@@ -3025,21 +3038,21 @@ async function provisionFromFolder(localPath, projectName, token, onProgress, br
3025
3038
  (t) => t.mcp?.transport === "stdio"
3026
3039
  );
3027
3040
  if (hasInstallableTools) {
3028
- onProgress({ step: "Installing MCP tools..." });
3041
+ progress({ step: "Installing MCP tools..." });
3029
3042
  await installToolsViaRest(projectName, token, projectRoot, tools, pm, (msg) => {
3030
- onProgress({ step: msg });
3043
+ progress({ step: msg });
3031
3044
  });
3032
3045
  }
3033
3046
  if (hasMcpTools) {
3034
3047
  const mcpJson = serializeMcpJson(tools);
3035
3048
  await writeRemoteFile(`${projectRoot}/.mcp.json`, mcpJson);
3036
- onProgress({ step: "Wrote .mcp.json" });
3049
+ progress({ step: "Wrote .mcp.json" });
3037
3050
  }
3038
3051
  } catch (err) {
3039
3052
  console.warn("[project-provisioner] MCP tool setup failed:", err instanceof Error ? err.message : String(err));
3040
3053
  }
3041
3054
  if (branchName && gitInfo?.isGitRepo) {
3042
- onProgress({ step: "Initializing git on project..." });
3055
+ progress({ step: "Initializing git on project..." });
3043
3056
  const userName = gitInfo.userName || "Ooda User";
3044
3057
  const userEmail = gitInfo.userEmail || "user@ooda.dev";
3045
3058
  const gitInitScript = [
@@ -3061,7 +3074,7 @@ async function provisionFromFolder(localPath, projectName, token, onProgress, br
3061
3074
  console.log(`[project-provisioner] Added remote: ${gitInfo.originUrl}`);
3062
3075
  }
3063
3076
  if (githubToken) {
3064
- onProgress({ step: "Setting up git credentials..." });
3077
+ progress({ step: "Setting up git credentials..." });
3065
3078
  const homeDir = "/home/user";
3066
3079
  await exec(`git config --global credential.helper store`);
3067
3080
  await writeRemoteFile(
@@ -3075,7 +3088,7 @@ async function provisionFromFolder(localPath, projectName, token, onProgress, br
3075
3088
  console.error("[project-provisioner] Git init failed:", err);
3076
3089
  }
3077
3090
  }
3078
- onProgress({ step: "Ready for setup", detail: projectUrl });
3091
+ progress({ step: "Ready for setup", detail: projectUrl });
3079
3092
  return {
3080
3093
  success: true,
3081
3094
  projectName,
@@ -3147,6 +3160,8 @@ async function detectProjectRemote(projectName, token, projectRoot) {
3147
3160
  return { framework, packageManager, devCommand, configFile };
3148
3161
  }
3149
3162
  async function provisionFromGitHub(parsed, projectName, token, onProgress, githubToken, claudeToken) {
3163
+ const t0 = Date.now();
3164
+ const progress = (p) => onProgress({ ...p, step: `[+${((Date.now() - t0) / 1e3).toFixed(1)}s] ${p.step}` });
3150
3165
  const headers = { Authorization: `Bearer ${token}` };
3151
3166
  let homeDir = "/home/user";
3152
3167
  async function exec(cmd) {
@@ -3169,8 +3184,8 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3169
3184
  });
3170
3185
  }
3171
3186
  try {
3172
- const projectUrl = await createOrReuseProject(projectName, token, onProgress);
3173
- onProgress({ step: "Waiting for sandbox to be ready..." });
3187
+ const projectUrl = await createOrReuseProject(projectName, token, progress);
3188
+ progress({ step: "Waiting for sandbox to be ready..." });
3174
3189
  for (let i = 0; i < 15; i++) {
3175
3190
  const check = await exec("test -d /home/user && echo OK || echo NOT_READY");
3176
3191
  const trimmed = check.trim();
@@ -3184,7 +3199,7 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3184
3199
  console.log(`[project-provisioner] Sandbox home directory: ${homeDir}`);
3185
3200
  const projectRoot = `${homeDir}/${parsed.repo}`;
3186
3201
  const cloneUrl = buildAuthenticatedCloneUrl(parsed, githubToken);
3187
- onProgress({ step: "Cloning from GitHub...", detail: `${parsed.owner}/${parsed.repo}` });
3202
+ progress({ step: "Cloning from GitHub...", detail: `${parsed.owner}/${parsed.repo}` });
3188
3203
  const cloneOutput = await exec(
3189
3204
  `cd ${homeDir} && git clone ${cloneUrl} ${parsed.repo} 2>&1`
3190
3205
  );
@@ -3204,7 +3219,7 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3204
3219
  const workingBranch = `ooda/${baseBranch}`;
3205
3220
  await exec(`cd ${projectRoot} && git checkout -b ${workingBranch} 2>&1`);
3206
3221
  if (githubToken) {
3207
- onProgress({ step: "Setting up git credentials..." });
3222
+ progress({ step: "Setting up git credentials..." });
3208
3223
  await exec(`cd ${projectRoot} && git remote set-url origin https://github.com/${parsed.owner}/${parsed.repo}.git`);
3209
3224
  await exec(`git config --global credential.helper store`);
3210
3225
  await writeRemoteFile(
@@ -3214,12 +3229,12 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3214
3229
  );
3215
3230
  }
3216
3231
  await exec(`cd ${projectRoot} && git config user.name "ooda" && git config user.email "dev@ooda.run"`);
3217
- onProgress({ step: "Detecting project..." });
3232
+ progress({ step: "Detecting project..." });
3218
3233
  const project = await detectProjectRemote(projectName, token, projectRoot);
3219
3234
  console.log(`[project-provisioner] Detected: framework=${project.framework}, pm=${project.packageManager}`);
3220
3235
  const effectiveClaudeToken = claudeToken || process.env.CLAUDE_CODE_OAUTH_TOKEN;
3221
3236
  if (effectiveClaudeToken) {
3222
- await setupClaudeAuth(projectName, token, effectiveClaudeToken, onProgress);
3237
+ await setupClaudeAuth(projectName, token, effectiveClaudeToken, progress);
3223
3238
  }
3224
3239
  const gitInfo = {
3225
3240
  isGitRepo: true,
@@ -3229,7 +3244,7 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3229
3244
  userName: "ooda",
3230
3245
  userEmail: "dev@ooda.run"
3231
3246
  };
3232
- await deployClaudeConfig(projectName, token, projectRoot, project.framework, onProgress, gitInfo, workingBranch);
3247
+ await deployClaudeConfig(projectName, token, projectRoot, project.framework, progress, gitInfo, workingBranch);
3233
3248
  const gitWorkflowPatch = `
3234
3249
  ## Git Workflow
3235
3250
  - This project was cloned from GitHub (${parsed.owner}/${parsed.repo}).
@@ -3251,11 +3266,11 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3251
3266
  );
3252
3267
  await writeRemoteFile(`${projectRoot}/CLAUDE.md`, updatedClaudeMd);
3253
3268
  await writeRemoteFile(`${homeDir}/.ooda/publish.mjs`, PUBLISH_SCRIPT2);
3254
- await patchServerConfig(projectName, token, projectRoot, onProgress);
3255
- await installSourcePlugin(projectName, token, projectRoot, project.framework, onProgress);
3269
+ await patchServerConfig(projectName, token, projectRoot, progress);
3270
+ await installSourcePlugin(projectName, token, projectRoot, project.framework, progress);
3256
3271
  try {
3257
3272
  await deployToolsViaRest(projectName, token, projectRoot, (msg) => {
3258
- onProgress({ step: msg });
3273
+ progress({ step: msg });
3259
3274
  });
3260
3275
  } catch (err) {
3261
3276
  console.warn("[project-provisioner] Tool deployment failed:", err instanceof Error ? err.message : String(err));
@@ -3277,21 +3292,21 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3277
3292
  (t) => t.mcp?.transport === "stdio"
3278
3293
  );
3279
3294
  if (hasInstallableTools) {
3280
- onProgress({ step: "Installing MCP tools..." });
3295
+ progress({ step: "Installing MCP tools..." });
3281
3296
  await installToolsViaRest(projectName, token, projectRoot, tools, pm, (msg) => {
3282
- onProgress({ step: msg });
3297
+ progress({ step: msg });
3283
3298
  });
3284
3299
  }
3285
3300
  if (hasMcpTools) {
3286
3301
  const mcpJson = serializeMcpJson(tools);
3287
3302
  await writeRemoteFile(`${projectRoot}/.mcp.json`, mcpJson);
3288
- onProgress({ step: "Wrote .mcp.json" });
3303
+ progress({ step: "Wrote .mcp.json" });
3289
3304
  }
3290
3305
  }
3291
3306
  } catch (err) {
3292
3307
  console.warn("[project-provisioner] MCP tool setup failed:", err instanceof Error ? err.message : String(err));
3293
3308
  }
3294
- onProgress({ step: "Ready for setup", detail: projectUrl });
3309
+ progress({ step: "Ready for setup", detail: projectUrl });
3295
3310
  return {
3296
3311
  success: true,
3297
3312
  projectName,
@@ -3306,6 +3321,8 @@ async function provisionFromGitHub(parsed, projectName, token, onProgress, githu
3306
3321
  }
3307
3322
  }
3308
3323
  async function provisionFromTemplate(projectName, templateId, token, onProgress, claudeToken, claudeEnv) {
3324
+ const t0 = Date.now();
3325
+ const progress = (p) => onProgress({ ...p, step: `[+${((Date.now() - t0) / 1e3).toFixed(1)}s] ${p.step}` });
3309
3326
  const { getProjectTemplateById: getProjectTemplateById2 } = await Promise.resolve().then(() => (init_project_templates(), project_templates_exports));
3310
3327
  const template = getProjectTemplateById2(templateId);
3311
3328
  if (!template) {
@@ -3341,20 +3358,20 @@ async function provisionFromTemplate(projectName, templateId, token, onProgress,
3341
3358
  }
3342
3359
  }
3343
3360
  try {
3344
- const projectUrl = await createOrReuseProject(projectName, token, onProgress);
3345
- onProgress({ step: "Uploading template files...", detail: `${template.files.length} files` });
3361
+ const projectUrl = await createOrReuseProject(projectName, token, progress);
3362
+ progress({ step: "Uploading template files...", detail: `${template.files.length} files` });
3346
3363
  for (const file of template.files) {
3347
3364
  await writeFile(`${projectRoot}/${file.path}`, file.content);
3348
3365
  }
3349
3366
  const effectiveClaudeToken = claudeToken || process.env.CLAUDE_CODE_OAUTH_TOKEN;
3350
3367
  if (effectiveClaudeToken) {
3351
- await setupClaudeAuth(projectName, token, effectiveClaudeToken, onProgress, claudeEnv);
3368
+ await setupClaudeAuth(projectName, token, effectiveClaudeToken, progress, claudeEnv);
3352
3369
  }
3353
- await deployClaudeConfig(projectName, token, projectRoot, "vite", onProgress);
3370
+ await deployClaudeConfig(projectName, token, projectRoot, "vite", progress);
3354
3371
  await writeFile("/home/user/.ooda/publish.mjs", PUBLISH_SCRIPT2);
3355
3372
  try {
3356
3373
  await deployToolsViaRest(projectName, token, projectRoot, (msg) => {
3357
- onProgress({ step: msg });
3374
+ progress({ step: msg });
3358
3375
  });
3359
3376
  } catch {
3360
3377
  }
@@ -3368,26 +3385,26 @@ async function provisionFromTemplate(projectName, templateId, token, onProgress,
3368
3385
  (t) => t.mcp?.transport === "stdio"
3369
3386
  );
3370
3387
  if (hasInstallableTools) {
3371
- onProgress({ step: "Installing MCP tools..." });
3388
+ progress({ step: "Installing MCP tools..." });
3372
3389
  await installToolsViaRest(projectName, token, projectRoot, tools, "npm", (msg) => {
3373
- onProgress({ step: msg });
3390
+ progress({ step: msg });
3374
3391
  });
3375
3392
  }
3376
3393
  if (hasMcpTools) {
3377
3394
  const mcpJson = serializeMcpJson(tools);
3378
3395
  await writeFile(`${projectRoot}/.mcp.json`, mcpJson);
3379
- onProgress({ step: "Wrote .mcp.json" });
3396
+ progress({ step: "Wrote .mcp.json" });
3380
3397
  }
3381
3398
  } catch (err) {
3382
3399
  console.warn("[project-provisioner] MCP tool setup failed:", err instanceof Error ? err.message : String(err));
3383
3400
  }
3384
- onProgress({ step: "Starting dev server..." });
3401
+ progress({ step: "Starting dev server..." });
3385
3402
  await exec("pkill -f vite || true");
3386
3403
  await new Promise((r) => setTimeout(r, 2e3));
3387
3404
  exec(`cd ${projectRoot} && ${template.startCmd}`).catch(() => {
3388
3405
  });
3389
3406
  await new Promise((r) => setTimeout(r, 6e3));
3390
- onProgress({ step: "Done!", detail: projectUrl });
3407
+ progress({ step: "Done!", detail: projectUrl });
3391
3408
  return {
3392
3409
  success: true,
3393
3410
  projectName,
@@ -4601,6 +4618,8 @@ function getActiveProject() {
4601
4618
  return _activeProject;
4602
4619
  }
4603
4620
  async function connectAndRunClaude(projectName, apiToken, claudeToken, projectUrl, initialPrompt, previewUrl) {
4621
+ const t0 = Date.now();
4622
+ const elapsed = () => `[+${((Date.now() - t0) / 1e3).toFixed(1)}s]`;
4604
4623
  console.log(`
4605
4624
  ${c.cyan}Connecting to ${c.bold}${projectName}${c.reset}${c.cyan}...${c.reset}`);
4606
4625
  const orgClaude = isOrgMode() ? getOrgClaudeConfig() : null;
@@ -4662,7 +4681,7 @@ async function connectAndRunClaude(projectName, apiToken, claudeToken, projectUr
4662
4681
  return;
4663
4682
  }
4664
4683
  const projectRoot = await findProjectRoot(projectName, apiToken);
4665
- console.log(` ${c.green}${c.bold}\u2713${c.reset} Connected`);
4684
+ console.log(` ${c.green}${c.bold}\u2713${c.reset} Connected ${c.gray}${elapsed()}${c.reset}`);
4666
4685
  pushPhase("setup", "Writing config files");
4667
4686
  const devServerStatus = await readDevServerStatus(client);
4668
4687
  if (devServerStatus) {
@@ -4793,7 +4812,7 @@ with open(p, 'w') as f: json.dump(d, f)
4793
4812
  }
4794
4813
  }
4795
4814
  pushPhase("starting", "Launching Claude Code");
4796
- console.log(` ${c.gray}Launching claude in ${projectRoot}...${c.reset}
4815
+ console.log(` ${c.gray}${elapsed()} Launching claude in ${projectRoot}...${c.reset}
4797
4816
  `);
4798
4817
  if (process.stdin.readable) {
4799
4818
  process.stdin.resume();
@@ -5058,8 +5077,8 @@ ${text}`;
5058
5077
  `);
5059
5078
  break;
5060
5079
  }
5061
- const elapsed = Date.now() - spawnedAt;
5062
- if (cmdAlive || elapsed >= EARLY_DROP_THRESHOLD_MS) {
5080
+ const elapsed2 = Date.now() - spawnedAt;
5081
+ if (cmdAlive || elapsed2 >= EARLY_DROP_THRESHOLD_MS) {
5063
5082
  break;
5064
5083
  }
5065
5084
  if (attempt < MAX_SPAWN_RETRIES) {
@@ -5307,7 +5326,7 @@ async function deployFromGitHubFlow(target, apiToken, claudeToken) {
5307
5326
  }
5308
5327
 
5309
5328
  // src/cli/index.ts
5310
- var CLI_VERSION = "0.1.3";
5329
+ var CLI_VERSION = "0.1.5";
5311
5330
  function formatMutationError(result) {
5312
5331
  const parts = [];
5313
5332
  if (result.status !== void 0) parts.push(String(result.status));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oodarun/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Launch Claude Code on cloud dev environments",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",