Initial commit: move CLI (milestone 2)

- src/move.ts entry point with CLI parsing, --help, --version
- src/cli.ts: parseCliArgs, printHelp, ParsedCliArgs, CliError, VERSION
- src/config.ts: Config type, DEFAULT_CONFIG, resolveConfig
- src/keeper.ts: synthetic-activity sweep + idle-watch loop
- package.json bin entry + shebang for 'bun link' global install
- install.sh: contributor bootstrap (will be repurposed; see
  DISTRIBUTION-PLAN.md for the end-user installer design)
- DISTRIBUTION-PLAN.md captures the tabled end-user distribution work
This commit is contained in:
2026-06-15 14:31:31 -05:00
commit 83d91c5630
12 changed files with 1886 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
/**
* config.ts
* ---------
* Runtime configuration types, defaults, and the CLI->config resolver.
*
* The keeper is driven by a single `Config` object that carries the four
* tunables it cares about. Defaults live in `DEFAULT_CONFIG`; CLI overrides
* are layered on top by `resolveConfig` rather than mutating the defaults,
* so the defaults stay genuinely constant and the resolved config stays
* structurally typed.
*
* All fields are in their internal units (ms, pixels). The CLI exposes the
* time-valued fields in seconds for ergonomics; `resolveConfig` performs
* the seconds->ms conversion at the boundary so downstream code never has
* to think about it.
*/
/**
* The shape of a resolved runtime configuration. `readonly` to make
* accidental mutation a type error.
*
* - `moveInterval` — how long the mouse must be idle (no real movement)
* before a synthetic sweep is triggered. Milliseconds.
* - `checkInterval` — cadence of the idleness poll in the main loop.
* Milliseconds.
* - `stepDelay` — pause between individual synthetic mouse steps inside
* a sweep. Also the window in which the user can
* "interrupt" by moving the cursor. Milliseconds.
* - `stepCount` — number of pixel-steps in a single sweep. Pixels.
*/
export interface Config {
readonly moveInterval: number;
readonly checkInterval: number;
readonly stepDelay: number;
readonly stepCount: number;
}
/**
* Built-in defaults used when the user did not supply an explicit CLI
* override for the corresponding flag.
*/
export const DEFAULT_CONFIG: Config = {
moveInterval: 4 * 60 * 1000, // 4 minutes in milliseconds
checkInterval: 10 * 1000, // Check every 10 seconds
stepDelay: 50, // ms between mouse steps
stepCount: 250, // pixels to move per sweep
};
/**
* Subset of `ParsedCliArgs` that `resolveConfig` actually consumes. Declared
* locally instead of importing from `cli.ts` to keep the dependency arrow
* pointing one way (cli -> config), which lets `config.ts` stay a leaf
* module with no internal imports.
*/
export interface ConfigOverrides {
readonly moveInterval: number | undefined; // seconds (CLI units)
readonly checkInterval: number | undefined; // seconds (CLI units)
readonly stepDelay: number | undefined; // milliseconds
readonly stepCount: number | undefined; // pixels
}
/**
* Overlay user-supplied CLI values on top of `DEFAULT_CONFIG` and return a
* resolved `Config`. Time-valued CLI inputs (move/check interval) are in
* seconds; this is where they're converted to milliseconds for internal use.
*
* Any field left `undefined` in the overrides falls back to the default.
*/
export function resolveConfig(overrides: ConfigOverrides): Config {
return {
moveInterval: overrides.moveInterval !== undefined ? overrides.moveInterval * 1000 : DEFAULT_CONFIG.moveInterval,
checkInterval: overrides.checkInterval !== undefined ? overrides.checkInterval * 1000 : DEFAULT_CONFIG.checkInterval,
stepDelay: overrides.stepDelay ?? DEFAULT_CONFIG.stepDelay,
stepCount: overrides.stepCount ?? DEFAULT_CONFIG.stepCount,
};
}