@1mancompany/onemancompany 0.7.83 → 0.7.85

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/bin/cli.js CHANGED
@@ -142,6 +142,19 @@ function runShell(cmd, opts = {}) {
142
142
  return execSync(cmd, { stdio: "inherit", shell: true, ...opts });
143
143
  }
144
144
 
145
+ // Read the installed app's version from installDir/pyproject.toml.
146
+ // Returns null if the file is missing, unreadable, or has no version line.
147
+ // Exported via global for unit tests; the CLI itself uses it directly.
148
+ function readAppVersion(installDir) {
149
+ try {
150
+ const pyproject = fs.readFileSync(path.join(installDir, "pyproject.toml"), "utf-8");
151
+ const verMatch = pyproject.match(/^version\s*=\s*"([^"]+)"/m);
152
+ return verMatch ? verMatch[1] : null;
153
+ } catch {
154
+ return null;
155
+ }
156
+ }
157
+
145
158
  // ── UV installer ────────────────────────────────────────────────────────────
146
159
  function ensureUV() {
147
160
  if (commandExists("uv")) {
@@ -219,8 +232,8 @@ async function main() {
219
232
  ${cyan("OneManCompany")} — The Agent Operating System for One Man Companies
220
233
 
221
234
  ${green("Usage:")}
222
- npx @1mancompany/onemancompany Start (runs in background)
223
- npx @1mancompany/onemancompany --update Pull latest version then start
235
+ npx @1mancompany/onemancompany Start (runs in background; refreshes source by default)
236
+ npx @1mancompany/onemancompany --no-update Start without refreshing source (keep local edits)
224
237
  npx @1mancompany/onemancompany --debug Start with logs (Ctrl+C to stop)
225
238
  npx @1mancompany/onemancompany stop Stop background service
226
239
  npx @1mancompany/onemancompany init Re-run setup process (interactive)
@@ -233,7 +246,7 @@ ${green("Usage:")}
233
246
  ${green("Options:")}
234
247
  --dir <path> Install directory (default: ./OneManCompany)
235
248
  --port <port> Server port (default: 8000)
236
- --update Pull latest version before starting (default: use local)
249
+ --no-update Skip refreshing bundled code (default: refresh src/, frontend/, pyproject.toml, uv.lock on every run; company/ and config.yaml are always preserved)
237
250
  --debug Run in foreground with logs (default: background)
238
251
  --help, -h Show this help
239
252
 
@@ -325,35 +338,58 @@ ${green("What gets installed automatically:")}
325
338
  }
326
339
 
327
340
  // ── Install or update ──────────────────────────────────────────────────
328
- // The npm package bundles the full source. Copy it to installDir.
329
- // Only fall back to git clone if source is missing (shouldn't happen).
330
- const SOURCE_ITEMS = ["src", "frontend", "company", "pyproject.toml", "config.yaml", "uv.lock"];
331
- const wantUpdate = passthrough.includes("--update");
332
-
333
- function copyItems(items, destRoot) {
341
+ // Two classes of bundled items, treated differently on subsequent runs:
342
+ // * CODE owned by the project, refreshed on every run so `@dev` actually
343
+ // means "give me the latest code." Safe to overwrite because users
344
+ // shouldn't be editing these directly.
345
+ // * USER-OWNED — bootstrapped on first install, NEVER overwritten on
346
+ // update. `company/` holds workflows / SOPs / founding-employee profiles
347
+ // that users routinely edit; `config.yaml` holds local config. Blowing
348
+ // these away on every `npx` run would silently destroy work.
349
+ // Pass --no-update to skip refreshing CODE too (e.g. when debugging local
350
+ // patches in installDir/src). --update is a silent no-op (was the old
351
+ // opt-in flag; current behavior matches what it used to enable).
352
+ const CODE_ITEMS = ["src", "frontend", "pyproject.toml", "uv.lock"];
353
+ const USER_OWNED_ITEMS = ["company", "config.yaml"];
354
+ const wantNoUpdate = passthrough.includes("--no-update");
355
+
356
+ function copyItems(items, destRoot, { overwrite }) {
334
357
  for (const item of items) {
335
358
  const src = path.join(npmPkgRoot, item);
336
359
  const dest = path.join(destRoot, item);
337
- if (fs.existsSync(src)) {
338
- if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
360
+ if (!fs.existsSync(src)) continue;
361
+ if (fs.existsSync(dest)) {
362
+ if (!overwrite) continue;
363
+ // Copy to sibling temp then rename, so an interrupted run can't leave
364
+ // installDir/<item> in a half-deleted state.
365
+ const tmp = `${dest}.tmp-${process.pid}`;
366
+ if (fs.existsSync(tmp)) fs.rmSync(tmp, { recursive: true, force: true });
367
+ fs.cpSync(src, tmp, { recursive: true });
368
+ fs.rmSync(dest, { recursive: true, force: true });
369
+ fs.renameSync(tmp, dest);
370
+ } else {
339
371
  fs.cpSync(src, dest, { recursive: true });
340
372
  }
341
373
  }
342
374
  }
343
375
 
344
376
  if (fs.existsSync(installDir)) {
345
- if (wantUpdate && sourceIsBundled) {
346
- info(`Updating installation to v${cliVersion}...`);
347
- copyItems(SOURCE_ITEMS, installDir);
348
- } else if (wantUpdate && !sourceIsBundled) {
349
- warn("Update requested but bundled source not found — cannot update");
377
+ if (wantNoUpdate) {
378
+ info(`Using existing installation at ${installDir} (--no-update)`);
379
+ } else if (sourceIsBundled) {
380
+ info(`Updating code to v${cliVersion}... (user-owned files in company/ and config.yaml are preserved)`);
381
+ copyItems(CODE_ITEMS, installDir, { overwrite: true });
382
+ // Bootstrap user-owned items only if they were never created (e.g.
383
+ // partial-install recovery). Existing files are left untouched.
384
+ copyItems(USER_OWNED_ITEMS, installDir, { overwrite: false });
350
385
  } else {
351
- info(`Using existing installation at ${installDir}`);
386
+ warn(`Bundled source not found in npm package — keeping existing files at ${installDir}. Try reinstalling: npm cache clean --force && npx --yes @1mancompany/onemancompany@<version>`);
352
387
  }
353
388
  } else if (sourceIsBundled) {
354
389
  info(`Installing OneManCompany v${cliVersion} into ${installDir}...`);
355
390
  fs.mkdirSync(installDir, { recursive: true });
356
- copyItems(SOURCE_ITEMS, installDir);
391
+ copyItems(CODE_ITEMS, installDir, { overwrite: true });
392
+ copyItems(USER_OWNED_ITEMS, installDir, { overwrite: true });
357
393
  } else {
358
394
  // Fallback: no bundled source (broken package?) — clone from git
359
395
  info(`Cloning OneManCompany into ${installDir}...`);
@@ -361,9 +397,19 @@ ${green("What gets installed automatically:")}
361
397
  run(`git clone --depth 1 ${REPO_URL} "${installDir}"`, { env: cloneEnv });
362
398
  }
363
399
 
400
+ // ── Read the *installed* app version (the only honest source for the banner) —
401
+ // Do NOT use cliVersion as a fallback: when --no-update is set, the npm CLI
402
+ // and the installed app can be on different versions. Showing the wrong
403
+ // number is worse than showing "unknown".
404
+ const installedAppVersion = readAppVersion(installDir);
405
+ if (!installedAppVersion) {
406
+ warn(`Could not read app version from ${path.join(installDir, "pyproject.toml")} — banner shows "unknown"`);
407
+ }
408
+ const appVersion = installedAppVersion || "unknown";
409
+
364
410
  // ── Banner (after real version is known) ───────────────────────────
365
411
  console.log();
366
- const verTag = `v${cliVersion}`;
412
+ const verTag = `v${appVersion}`;
367
413
  const title = `OneManCompany — AI Company OS ${verTag}`;
368
414
  const pad = Math.max(0, 44 - title.length);
369
415
  console.log(cyan("╔═══════════════════════════════════════════════╗"));
@@ -534,7 +580,10 @@ ${green("What gets installed automatically:")}
534
580
 
535
581
  // Start server
536
582
  const debugMode = passthrough.includes("--debug");
537
- const launchArgs = passthrough.filter((a) => a !== "--debug" && a !== "--update");
583
+ // CLI-only flags must be stripped before forwarding to onemancompany.main,
584
+ // otherwise argparse there will reject the unknown argument.
585
+ const CLI_ONLY_FLAGS = new Set(["--debug", "--update", "--no-update"]);
586
+ const launchArgs = passthrough.filter((a) => !CLI_ONLY_FLAGS.has(a));
538
587
 
539
588
  // Build env: pass OMC_DEBUG=1 in debug mode
540
589
  const childEnv = { ...process.env };
@@ -542,7 +591,7 @@ ${green("What gets installed automatically:")}
542
591
 
543
592
  if (debugMode) {
544
593
  // ── Foreground mode: show logs, Ctrl+C to kill ──────────────────
545
- info(`Starting OneManCompany v${cliVersion} in debug mode (Ctrl+C to stop)...\n`);
594
+ info(`Starting OneManCompany v${appVersion} in debug mode (Ctrl+C to stop)...\n`);
546
595
  const child = spawn(pythonBin, ["-m", "onemancompany.main", ...launchArgs], {
547
596
  cwd: installDir,
548
597
  stdio: "inherit",
@@ -558,7 +607,7 @@ ${green("What gets installed automatically:")}
558
607
  process.on("SIGTERM", () => { child.kill("SIGTERM"); });
559
608
  } else {
560
609
  // ── Background mode: detach and exit CLI ────────────────────────
561
- info(`Starting OneManCompany v${cliVersion} in background...`);
610
+ info(`Starting OneManCompany v${appVersion} in background...`);
562
611
  const logFile = path.join(installDir, ".onemancompany", "server.log");
563
612
  // Ensure log directory exists
564
613
  const logDir = path.dirname(logFile);
@@ -581,7 +630,7 @@ ${green("What gets installed automatically:")}
581
630
  await new Promise((r) => setTimeout(r, 5000));
582
631
  if (isProcessRunning(child.pid)) {
583
632
  console.log();
584
- console.log(green(` ✓ OneManCompany v${cliVersion} is running!`));
633
+ console.log(green(` ✓ OneManCompany v${appVersion} is running!`));
585
634
  console.log();
586
635
  console.log(` ${cyan("→")} Open ${cyan("http://localhost:8000")} in your browser`);
587
636
  console.log(` ${dim(" Logs:")} ${logFile}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1mancompany/onemancompany",
3
- "version": "0.7.83",
3
+ "version": "0.7.85",
4
4
  "description": "The AI Operating System for One-Person Companies",
5
5
  "bin": {
6
6
  "onemancompany": "bin/cli.js"
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "onemancompany"
3
- version = "0.7.83"
3
+ version = "0.7.85"
4
4
  description = "A one-man company simulation with pixel art visualization and LangChain AI agents"
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [