overmux 0.0.2 → 0.0.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # overmux
2
2
 
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#118](https://github.com/richardgill/overmux/pull/118) [`78a3b8f`](https://github.com/richardgill/overmux/commit/78a3b8f9b143b8408989309bfaa399208daaa133) Thanks [@richardgill](https://github.com/richardgill)! - Catch up the initial patch release with init scaffolding, resilient terminal connections and tmux navigation, plus improved xterm links, clipboard support, and Unicode rendering. The Zellij integration remains an independent minor release and now requires the current Overmux peer version.
8
+
9
+ - [#117](https://github.com/richardgill/overmux/pull/117) [`d000b17`](https://github.com/richardgill/overmux/commit/d000b170d085f087e364672b3139b01e8fea1902) Thanks [@richardgill](https://github.com/richardgill)! - Scaffold AI agent guidance files when initializing an Overmux project.
10
+
11
+ - Updated dependencies [[`78a3b8f`](https://github.com/richardgill/overmux/commit/78a3b8f9b143b8408989309bfaa399208daaa133)]:
12
+ - @overmux/keybindings@0.0.3
13
+
3
14
  ## 0.0.2
4
15
 
5
16
  ### Patch Changes
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
2
+ import { createRequire, findPackageJSON } from "node:module";
3
3
  import { access, chmod, readFile, readdir, realpath } from "node:fs/promises";
4
4
  import { createConnection, isIP } from "node:net";
5
5
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
@@ -11,7 +11,7 @@ import ts from "typescript";
11
11
  import { isDeepStrictEqual } from "node:util";
12
12
  import { fork, spawnSync } from "node:child_process";
13
13
  import { fileURLToPath, pathToFileURL } from "node:url";
14
- import { createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readlinkSync, renameSync, rmSync, watch } from "node:fs";
14
+ import { cpSync, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
15
15
  import { createInterface } from "node:readline/promises";
16
16
  import { Readable, Transform } from "node:stream";
17
17
  import { pipeline } from "node:stream/promises";
@@ -877,7 +877,7 @@ function listAllRouteNamesAndAliasesForScan(routeMap, scannerCaseStyle, config)
877
877
  return [routeName];
878
878
  });
879
879
  }
880
- async function runCommand$1({ loader, parameters }, { context, inputs, scannerConfig, errorFormatting, determineExitCode, ansiColorByStream }) {
880
+ async function runCommand$2({ loader, parameters }, { context, inputs, scannerConfig, errorFormatting, determineExitCode, ansiColorByStream }) {
881
881
  let parsedArguments;
882
882
  try {
883
883
  const scanner = buildArgumentScanner(parameters, scannerConfig);
@@ -1348,7 +1348,7 @@ async function scanInputsAndRunTarget(app, rawInputs, context, text, ansiColorBy
1348
1348
  result
1349
1349
  });
1350
1350
  if (typeof hookStartExitCode === "number") return hookStartExitCode;
1351
- const exitCode = await runCommand$1(result.target, {
1351
+ const exitCode = await runCommand$2(result.target, {
1352
1352
  context: commandContext,
1353
1353
  inputs: result.unprocessedInputs,
1354
1354
  scannerConfig: app.config.scanner,
@@ -3529,7 +3529,7 @@ var downloadVerifiedAsset = async ({ asset, destination, expectedSha256, fetch =
3529
3529
  //#region src/internal/cli/commands/desktop-installer.ts
3530
3530
  var bundleIdentifier = "com.overmux.desktop";
3531
3531
  var bundleName = "Overmux.app";
3532
- var runCommand = (command, args) => {
3532
+ var runCommand$1 = (command, args) => {
3533
3533
  const result = spawnSync(command, args, { encoding: "utf8" });
3534
3534
  if (result.error) throw new Error(`Could not run ${command}`, { cause: result.error });
3535
3535
  return {
@@ -3565,7 +3565,7 @@ var createDependencies = (process) => ({
3565
3565
  findRelease: findDesktopRelease,
3566
3566
  homeDirectory: homedir(),
3567
3567
  platform: process.platform,
3568
- runCommand,
3568
+ runCommand: runCommand$1,
3569
3569
  sleep,
3570
3570
  stderr: process.stderr,
3571
3571
  stdout: process.stdout
@@ -3814,6 +3814,336 @@ var createDocsCommand = ({ docsRoot, output }) => buildCommand({
3814
3814
  docs: { brief: "Print the installed documentation directory" }
3815
3815
  });
3816
3816
  //#endregion
3817
+ //#region src/internal/cli/commands/init-template.ts
3818
+ var initScaffoldPaths = [
3819
+ ".gitignore",
3820
+ "AGENTS.md",
3821
+ "CLAUDE.md",
3822
+ "mise.toml",
3823
+ "package.json",
3824
+ "pnpm-lock.yaml",
3825
+ "overmux.config.ts",
3826
+ "src/server/index.ts",
3827
+ "src/ui/app.tsx",
3828
+ "src/ui/index.html",
3829
+ "src/ui/main.tsx",
3830
+ "src/ui/styles.css",
3831
+ "vite.config.ts"
3832
+ ];
3833
+ var packageJson = (overmuxVersion) => `${JSON.stringify({
3834
+ name: "my-overmux",
3835
+ private: true,
3836
+ type: "module",
3837
+ scripts: {
3838
+ build: "vite build",
3839
+ check: "overmux check --config ./overmux.config.ts",
3840
+ dev: "overmux serve --config ./overmux.config.ts"
3841
+ },
3842
+ dependencies: {
3843
+ overmux: overmuxVersion,
3844
+ react: "^19.0.0",
3845
+ "react-dom": "^19.0.0"
3846
+ },
3847
+ devDependencies: {
3848
+ "@types/react": "^19.0.0",
3849
+ "@types/react-dom": "^19.0.0",
3850
+ "@vitejs/plugin-react": "^6.0.0",
3851
+ typescript: "^5.9.0",
3852
+ vite: "^8.0.0"
3853
+ }
3854
+ }, null, 2)}\n`;
3855
+ var createInitScaffold = ({ overmuxVersion, toolchain }) => ({
3856
+ ".gitignore": "node_modules\ndist\n",
3857
+ "AGENTS.md": "Immediately run `overmux ai context` for documentation about Overmux.\n",
3858
+ "CLAUDE.md": "@AGENTS.md\n",
3859
+ ...toolchain === "mise" ? { "mise.toml": `[tools]
3860
+ node = "22"
3861
+ pnpm = "10"
3862
+ "npm:overmux" = "${overmuxVersion}"
3863
+ ` } : {},
3864
+ "package.json": packageJson(overmuxVersion),
3865
+ "overmux.config.ts": `import { defineOvermuxConfig } from "overmux";
3866
+
3867
+ import server from "./src/server/index";
3868
+
3869
+ export default defineOvermuxConfig({
3870
+ auth: { mode: "cli-login" },
3871
+ productionWebAssetsDir: "./dist",
3872
+ server,
3873
+ vite: "./vite.config.ts",
3874
+ });
3875
+ `,
3876
+ "src/server/index.ts": `import { defineOvermuxServer } from "overmux";
3877
+
3878
+ export default defineOvermuxServer({ resources: {} });
3879
+ `,
3880
+ "src/ui/app.tsx": `import { defineOvermuxClient } from "overmux/client";
3881
+
3882
+ const App = () => <main>Overmux is running.</main>;
3883
+
3884
+ export default defineOvermuxClient({
3885
+ commands: {},
3886
+ component: App,
3887
+ });
3888
+ `,
3889
+ "src/ui/index.html": `<!doctype html>
3890
+ <html lang="en">
3891
+ <head>
3892
+ <meta charset="UTF-8" />
3893
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3894
+ <title>Overmux</title>
3895
+ </head>
3896
+ <body>
3897
+ <div id="root">
3898
+ <p>Loading Overmux…</p>
3899
+ <button type="button" onclick="location.reload()">Retry</button>
3900
+ </div>
3901
+ <script type="module" src="/main.tsx"><\/script>
3902
+ </body>
3903
+ </html>
3904
+ `,
3905
+ "src/ui/main.tsx": `import { OvermuxHost } from "overmux/client";
3906
+ import { createRoot } from "react-dom/client";
3907
+
3908
+ import definition from "./app";
3909
+ import "./styles.css";
3910
+
3911
+ const root = document.querySelector("#root");
3912
+ if (!root) {
3913
+ throw new Error("Missing root element");
3914
+ }
3915
+
3916
+ createRoot(root).render(<OvermuxHost definition={definition} />);
3917
+ `,
3918
+ "src/ui/styles.css": `:root {
3919
+ color: #f5f5f5;
3920
+ background: #111;
3921
+ font-family: system-ui, sans-serif;
3922
+ }
3923
+
3924
+ body {
3925
+ margin: 0;
3926
+ }
3927
+
3928
+ main {
3929
+ display: grid;
3930
+ min-height: 100vh;
3931
+ place-items: center;
3932
+ }
3933
+ `,
3934
+ "vite.config.ts": `import viteReact from "@vitejs/plugin-react";
3935
+ import { defineConfig } from "vite";
3936
+
3937
+ export default defineConfig({
3938
+ build: { emptyOutDir: true, outDir: "../../dist" },
3939
+ plugins: [viteReact()],
3940
+ root: "src/ui",
3941
+ });
3942
+ `
3943
+ });
3944
+ //#endregion
3945
+ //#region src/internal/cli/commands/init.ts
3946
+ var miseActivationDocs = "https://mise.jdx.dev/getting-started.html#activate-mise";
3947
+ var runCommand = (command, args, options) => {
3948
+ const result = spawnSync(command, args, {
3949
+ cwd: options?.cwd,
3950
+ encoding: "utf8",
3951
+ maxBuffer: 20971520
3952
+ });
3953
+ return {
3954
+ error: result.error,
3955
+ status: result.status,
3956
+ stderr: result.stderr ?? "",
3957
+ stdout: result.stdout ?? ""
3958
+ };
3959
+ };
3960
+ var getLatestOvermuxVersion = async () => {
3961
+ const response = await fetch("https://registry.npmjs.org/overmux/latest", { headers: { accept: "application/json" } });
3962
+ if (!response.ok) throw new Error(`Could not resolve the latest Overmux version: npm returned ${response.status}`);
3963
+ const body = await response.json();
3964
+ const version = typeof body === "object" && body !== null && "version" in body ? body.version : void 0;
3965
+ if (typeof version !== "string") throw new Error("Could not resolve the latest Overmux version from npm");
3966
+ return version;
3967
+ };
3968
+ var commandExists = (result) => result.error?.code !== "ENOENT";
3969
+ var isMiseActivated = (dependencies) => {
3970
+ const doctor = dependencies.runCommand("mise", ["doctor", "--json"]);
3971
+ if (doctor.status !== 0) return false;
3972
+ try {
3973
+ const report = JSON.parse(doctor.stdout);
3974
+ return typeof report === "object" && report !== null && "activated" in report && report.activated === true;
3975
+ } catch {
3976
+ return false;
3977
+ }
3978
+ };
3979
+ var detectToolchain = (dependencies) => {
3980
+ const mise = dependencies.runCommand("mise", ["--version"]);
3981
+ if (commandExists(mise)) {
3982
+ if (mise.status !== 0) throw new Error(`Mise could not be validated. Activate mise and try again. ${miseActivationDocs}`);
3983
+ if (!isMiseActivated(dependencies)) throw new Error(`Mise must be activated in your shell before running overmux init. ${miseActivationDocs}`);
3984
+ return "mise";
3985
+ }
3986
+ const pnpm = dependencies.runCommand("pnpm", ["--version"]);
3987
+ const major = Number.parseInt(pnpm.stdout.trim().split(".")[0] ?? "", 10);
3988
+ if (!commandExists(pnpm) || pnpm.status !== 0 || !Number.isFinite(major)) throw new Error("pnpm 10 or newer is required when mise is not installed");
3989
+ if (major < 10) throw new Error(`pnpm 10 or newer is required when mise is not installed (found ${pnpm.stdout.trim()})`);
3990
+ return "pnpm";
3991
+ };
3992
+ var findCollisions = (directory) => {
3993
+ if (existsSync(directory) && !statSync(directory).isDirectory()) return [directory];
3994
+ return initScaffoldPaths.filter((path) => existsSync(join(directory, path)));
3995
+ };
3996
+ var writeScaffold = (directory, files) => {
3997
+ Object.entries(files).forEach(([path, content]) => {
3998
+ const destination = join(directory, path);
3999
+ mkdirSync(dirname(destination), { recursive: true });
4000
+ writeFileSync(destination, content);
4001
+ });
4002
+ };
4003
+ var runProjectCommand = ({ args, command, cwd, dependencies }) => {
4004
+ const result = dependencies.runCommand(command, args, { cwd });
4005
+ dependencies.stdout.write(result.stdout);
4006
+ dependencies.stderr.write(result.stderr);
4007
+ if (result.error || result.status !== 0) throw new Error(`Command failed: ${command} ${args.join(" ")}`);
4008
+ };
4009
+ var pnpmCommands = [["install", "--ignore-workspace"], [
4010
+ "exec",
4011
+ "overmux",
4012
+ "check",
4013
+ "--config",
4014
+ "./overmux.config.ts"
4015
+ ]];
4016
+ var installAndCheck = ({ directory, dependencies, toolchain }) => {
4017
+ const context = {
4018
+ cwd: directory,
4019
+ dependencies
4020
+ };
4021
+ if (toolchain === "mise") runProjectCommand({
4022
+ ...context,
4023
+ command: "mise",
4024
+ args: [
4025
+ "install",
4026
+ "node@22",
4027
+ "pnpm@10",
4028
+ "--yes"
4029
+ ]
4030
+ });
4031
+ pnpmCommands.forEach((args) => runProjectCommand({
4032
+ ...context,
4033
+ command: toolchain,
4034
+ args: toolchain === "mise" ? [
4035
+ "exec",
4036
+ "node@22",
4037
+ "pnpm@10",
4038
+ "--",
4039
+ "pnpm",
4040
+ ...args
4041
+ ] : args
4042
+ }));
4043
+ };
4044
+ var mergeScaffold = (source, target) => {
4045
+ if (!existsSync(target)) {
4046
+ renameSync(source, target);
4047
+ return;
4048
+ }
4049
+ cpSync(source, target, {
4050
+ errorOnExist: true,
4051
+ force: false,
4052
+ recursive: true
4053
+ });
4054
+ };
4055
+ var initializeOvermux = async (dependencies) => {
4056
+ const directory = resolve(dependencies.directory);
4057
+ const collisions = findCollisions(directory);
4058
+ if (collisions.length > 0) throw new Error(`Refusing to overwrite existing files:\n${collisions.map((path) => `- ${path}`).join("\n")}`);
4059
+ const toolchain = detectToolchain(dependencies);
4060
+ const files = createInitScaffold({
4061
+ overmuxVersion: await dependencies.getLatestOvermuxVersion(),
4062
+ toolchain
4063
+ });
4064
+ const parent = dirname(directory);
4065
+ mkdirSync(parent, { recursive: true });
4066
+ const stagingDirectory = mkdtempSync(join(parent, ".overmux-init-"));
4067
+ try {
4068
+ writeScaffold(stagingDirectory, Object.fromEntries(Object.entries(files).filter(([path]) => path !== "mise.toml")));
4069
+ installAndCheck({
4070
+ directory: stagingDirectory,
4071
+ dependencies,
4072
+ toolchain
4073
+ });
4074
+ if (!existsSync(join(stagingDirectory, "pnpm-lock.yaml"))) throw new Error("pnpm install did not generate pnpm-lock.yaml");
4075
+ if (files["mise.toml"]) writeFileSync(join(stagingDirectory, "mise.toml"), files["mise.toml"]);
4076
+ mergeScaffold(stagingDirectory, directory);
4077
+ } finally {
4078
+ rmSync(stagingDirectory, {
4079
+ force: true,
4080
+ recursive: true
4081
+ });
4082
+ }
4083
+ dependencies.stdout.write(`Initialized Overmux in ${directory}\nNext: overmux serve\n`);
4084
+ };
4085
+ var createInitCommand = (process) => buildCommand({
4086
+ func: () => initializeOvermux({
4087
+ directory: getOvermuxPaths({ environment: process.env }).configDir,
4088
+ getLatestOvermuxVersion,
4089
+ runCommand,
4090
+ stderr: process.stderr,
4091
+ stdout: process.stdout
4092
+ }),
4093
+ parameters: { flags: {} },
4094
+ docs: { brief: "Create a minimal Overmux application" }
4095
+ });
4096
+ //#endregion
4097
+ //#region src/internal/cli/commands/zellij-install.ts
4098
+ var resolveZellijInstall = (cwd) => {
4099
+ const projectUrl = pathToFileURL(resolve(cwd, "package.json"));
4100
+ const packagePath = findPackageJSON("@overmux/zellij/install", projectUrl);
4101
+ if (packagePath === void 0) throw new Error("package not found");
4102
+ const installExport = JSON.parse(readFileSync(packagePath, "utf8")).exports?.["./install"]?.import;
4103
+ if (typeof installExport !== "string") throw new Error("package has no importable ./install export");
4104
+ return resolve(dirname(packagePath), installExport);
4105
+ };
4106
+ var defaultDependencies = () => {
4107
+ const cwd = process.cwd();
4108
+ return {
4109
+ cwd,
4110
+ importModule: (specifier) => import(specifier),
4111
+ resolveInstall: () => resolveZellijInstall(cwd)
4112
+ };
4113
+ };
4114
+ var executeZellijInstall = async ({ session }, dependencies = defaultDependencies()) => {
4115
+ let modulePath;
4116
+ try {
4117
+ modulePath = dependencies.resolveInstall();
4118
+ } catch {
4119
+ throw new Error(`Could not resolve @overmux/zellij/install from ${dependencies.cwd}. Add @overmux/zellij to this project, then run the command again.`);
4120
+ }
4121
+ const integration = await dependencies.importModule(pathToFileURL(modulePath).href);
4122
+ if (typeof integration.installZellij !== "function") throw new Error("@overmux/zellij/install does not export installZellij. Update @overmux/zellij and run the command again.");
4123
+ return integration.installZellij({ session });
4124
+ };
4125
+ var runZellijInstall = async ({ session }) => {
4126
+ const result = await executeZellijInstall({ session });
4127
+ console.log(`Installed Zellij plugin ${result.pluginVersion} (protocol ${result.protocolVersion})\nSession: ${result.session}\nArtifact: ${result.artifactPath}\nSHA-256: ${result.sha256}`);
4128
+ };
4129
+ var integrationCommand = buildRouteMap({
4130
+ docs: { brief: "Manage Overmux integrations" },
4131
+ routes: { install: buildRouteMap({
4132
+ docs: { brief: "Install an Overmux integration" },
4133
+ routes: { zellij: buildCommand({
4134
+ docs: { brief: "Install and approve the @overmux/zellij plugin" },
4135
+ func: runZellijInstall,
4136
+ parameters: { flags: { session: {
4137
+ brief: "Target an active Zellij session",
4138
+ kind: "parsed",
4139
+ optional: true,
4140
+ parse: String,
4141
+ placeholder: "name"
4142
+ } } }
4143
+ }) }
4144
+ }) }
4145
+ });
4146
+ //#endregion
3817
4147
  //#region src/internal/shared/routes.ts
3818
4148
  var viteHmrPath = "/_overmux/vite/hmr";
3819
4149
  //#endregion
@@ -4721,7 +5051,7 @@ var detectCliEnvironment = async (process = globalThis.process) => {
4721
5051
  //#endregion
4722
5052
  //#region src/internal/cli/app.ts
4723
5053
  var createCliApp = (process = globalThis.process) => buildApplication(buildRouteMap({
4724
- docs: { brief: "Serve, validate, or invoke Overmux operations" },
5054
+ docs: { brief: "Initialize and manage Overmux applications" },
4725
5055
  routes: {
4726
5056
  ai: createAiRoute(process),
4727
5057
  auth: authRoute,
@@ -4732,6 +5062,8 @@ var createCliApp = (process = globalThis.process) => buildApplication(buildRoute
4732
5062
  docsRoot: resolveInstalledDocsRoot(import.meta.url),
4733
5063
  output: process.stdout
4734
5064
  }),
5065
+ init: createInitCommand(process),
5066
+ integration: integrationCommand,
4735
5067
  serve: serveCommand
4736
5068
  }
4737
5069
  }), {