@auroraflow/code 0.0.19 → 0.0.20

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
@@ -37,6 +37,9 @@ aurora update-clients
37
37
  aurora install codex
38
38
  aurora install claude
39
39
  aurora install all
40
+ aurora repair codex
41
+ aurora repair claude
42
+ aurora doctor
40
43
  ```
41
44
 
42
45
  ## Commands
@@ -50,6 +53,8 @@ node bin/aurora.js claude
50
53
  node bin/aurora.js codex
51
54
  node bin/aurora.js install codex
52
55
  node bin/aurora.js install claude
56
+ node bin/aurora.js repair codex
57
+ node bin/aurora.js doctor
53
58
  node bin/aurora.js install-clients
54
59
  node bin/aurora.js update-clients
55
60
  node bin/aurora.js status
@@ -59,6 +64,8 @@ Running `aurora` without a subcommand opens an interactive client selector. Use
59
64
  Up/Down (or `j`/`k`) and Enter to launch Claude or Codex through Aurora.
60
65
  If the selected official client is missing, Aurora prompts to install that
61
66
  specific client before launch.
67
+ If the package exists but its command is not usable, Aurora reports a broken
68
+ client state and prompts to repair instead of calling it "not installed".
62
69
 
63
70
  The launcher stores shared local state under `~/.aurora` and starts a local
64
71
  sidecar at `127.0.0.1:17878`. Official clients talk to the sidecar; the sidecar
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auroraflow/code",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "description": "Aurora launcher and sidecar for official Codex and Claude Code clients.",
6
6
  "repository": {
@@ -1,5 +1,5 @@
1
1
  import { ensureLayout, readAccount, readState, writeAccount, writeState } from "../../state/src/index.js";
2
- import { installOfficialClient, officialClientStatus, officialClientStatuses, runClient, updateOfficialClients } from "../../clients/src/index.js";
2
+ import { installOfficialClient, officialClientStatus, officialClientStatuses, repairOfficialClient, runClient, updateOfficialClients } from "../../clients/src/index.js";
3
3
  import { ensureSidecarRunning, installSidecarService, sidecarServiceStatus, uninstallSidecarService } from "../../service/src/index.js";
4
4
  import { chooseClient, promptChoice, promptFields } from "../../../lib/prompt.js";
5
5
 
@@ -28,9 +28,15 @@ export async function runAuroraCLI(argv = process.argv.slice(2)) {
28
28
  officialClients: officialClientStatuses()
29
29
  }, null, 2));
30
30
  return;
31
+ case "doctor":
32
+ await doctorCommand();
33
+ return;
31
34
  case "install":
32
35
  await installCommand(rest);
33
36
  return;
37
+ case "repair":
38
+ await repairCommand(rest);
39
+ return;
34
40
  case "install-clients":
35
41
  case "install-official-clients":
36
42
  case "update-clients":
@@ -93,6 +99,41 @@ async function installCommand(args) {
93
99
  }
94
100
  }
95
101
 
102
+ async function repairCommand(args) {
103
+ const target = args[0] ?? "";
104
+ switch (target) {
105
+ case "codex":
106
+ case "claude":
107
+ await repairOfficialClient(target);
108
+ console.log(JSON.stringify({ officialClients: officialClientStatuses() }, null, 2));
109
+ return;
110
+ case "all":
111
+ case "clients":
112
+ case "official-clients":
113
+ await repairOfficialClient("claude");
114
+ await repairOfficialClient("codex");
115
+ console.log(JSON.stringify({ officialClients: officialClientStatuses() }, null, 2));
116
+ return;
117
+ default:
118
+ console.error("Usage: aurora repair [codex|claude|all]");
119
+ process.exit(1);
120
+ }
121
+ }
122
+
123
+ async function doctorCommand() {
124
+ const statuses = officialClientStatuses();
125
+ console.log(JSON.stringify({ officialClients: statuses }, null, 2));
126
+ for (const status of statuses) {
127
+ if (status.state === "broken") {
128
+ console.error(`\n${status.name} exists but is not usable (${status.problem}).`);
129
+ console.error(`Repair: aurora repair ${status.id}`);
130
+ if (process.platform === "win32") {
131
+ console.error(`If repair reports EBUSY, close ${status.name} and run: taskkill /F /IM ${status.id === "claude" ? "claude.exe" : "codex.exe"}`);
132
+ }
133
+ }
134
+ }
135
+ }
136
+
96
137
  export async function runAuroraClaude(argv = process.argv.slice(2)) {
97
138
  await ensureLayout();
98
139
  await runClient("claude", argv);
@@ -116,12 +157,22 @@ async function startInteractive(args) {
116
157
  async function ensureOfficialClientForInteractive(client) {
117
158
  const status = officialClientStatus(client);
118
159
  if (status?.installed) return;
119
- const action = await promptChoice(`${status?.name ?? client} is not installed`, [
120
- { label: `Install ${status?.name ?? client} now`, value: "install" },
160
+ const broken = status?.state === "broken";
161
+ const action = await promptChoice(`${status?.name ?? client} ${broken ? "is installed but not usable" : "is not installed"}`, [
162
+ { label: `${broken ? "Repair" : "Install"} ${status?.name ?? client} now`, value: broken ? "repair" : "install" },
163
+ { label: "Show fix command", value: "manual" },
121
164
  { label: "Exit", value: "exit" }
122
165
  ]);
166
+ if (action === "manual") {
167
+ const command = broken ? `aurora repair ${client}` : `aurora install ${client}`;
168
+ throw new Error(`${status?.name ?? client} is required. Run: ${command}`);
169
+ }
170
+ if (action === "repair") {
171
+ await repairOfficialClient(client);
172
+ return;
173
+ }
123
174
  if (action !== "install") {
124
- throw new Error(`${status?.name ?? client} is required. Run: aurora install ${client}`);
175
+ throw new Error(`${status?.name ?? client} is required. Run: aurora ${broken ? "repair" : "install"} ${client}`);
125
176
  }
126
177
  await installOfficialClient(client);
127
178
  }
@@ -204,6 +255,9 @@ function usage(exitCode) {
204
255
  aurora install codex
205
256
  aurora install claude
206
257
  aurora install all
258
+ aurora repair codex
259
+ aurora repair claude
260
+ aurora doctor
207
261
  aurora install-clients
208
262
  aurora update-clients
209
263
  aurora status
@@ -67,14 +67,20 @@ export function officialClientStatuses() {
67
67
  return Object.entries(OFFICIAL_CLIENTS).map(([id, client]) => {
68
68
  const bin = resolveOfficialClientBin(client.binName);
69
69
  const packagePath = officialClientPackageJSONPath(client.packageName);
70
- const version = readOfficialClientVersion(bin) ?? (packagePath ? JSON.parse(readFileSync(packagePath, "utf8")).version : null);
70
+ const binVersion = readOfficialClientVersion(bin);
71
+ const packageVersion = packagePath ? JSON.parse(readFileSync(packagePath, "utf8")).version : null;
72
+ const state = bin && binVersion ? "installed" : packagePath ? "broken" : "missing";
71
73
  return {
72
74
  id,
73
75
  name: client.name,
74
76
  packageName: client.packageName,
75
77
  bin,
76
- installed: Boolean(bin),
77
- version
78
+ packagePath,
79
+ installed: state === "installed",
80
+ packagePresent: Boolean(packagePath),
81
+ state,
82
+ version: binVersion ?? packageVersion,
83
+ problem: officialClientProblem(state, bin, packagePath)
78
84
  };
79
85
  });
80
86
  }
@@ -88,15 +94,21 @@ export async function installOfficialClients() {
88
94
  await installOfficialClient("codex");
89
95
  }
90
96
 
91
- export async function installOfficialClient(client) {
97
+ export async function installOfficialClient(client, options = {}) {
92
98
  const spec = OFFICIAL_CLIENTS[client];
93
99
  if (!spec) throw new Error(`unknown official client: ${client}`);
94
100
  const npm = npmCommandSpec();
95
- const args = ["install", "-g", `${spec.packageName}@latest`];
96
- console.error(`[aurora] installing ${spec.name}: ${npm.command} ${[...npm.args, ...args].join(" ")}`);
101
+ const args = ["install", "-g"];
102
+ if (options.repair) args.push("--force");
103
+ args.push(`${spec.packageName}@latest`);
104
+ console.error(`[aurora] ${options.repair ? "repairing" : "installing"} ${spec.name}: ${npm.command} ${[...npm.args, ...args].join(" ")}`);
97
105
  await spawnAndWait(npm.command, [...npm.args, ...args], { ...process.env });
98
106
  }
99
107
 
108
+ export async function repairOfficialClient(client) {
109
+ await installOfficialClient(client, { repair: true });
110
+ }
111
+
100
112
  export function isGlobalNpmLifecycle() {
101
113
  return process.env.npm_config_global === "true" || process.env.npm_config_location === "global";
102
114
  }
@@ -108,11 +120,14 @@ export function officialClientStatus(client) {
108
120
  function officialClientBin(client) {
109
121
  const spec = OFFICIAL_CLIENTS[client];
110
122
  if (!spec) throw new Error(`unknown official client: ${client}`);
111
- const bin = resolveOfficialClientBin(spec.binName);
112
- if (!bin) {
123
+ const status = officialClientStatus(client);
124
+ if (!status?.installed) {
125
+ if (status?.state === "broken") {
126
+ throw new Error(`${spec.name} is installed but not usable. Run: aurora doctor, then aurora repair ${client}`);
127
+ }
113
128
  throw new Error(`${spec.name} is not installed. Run: aurora install ${client}`);
114
129
  }
115
- return bin;
130
+ return status.bin;
116
131
  }
117
132
 
118
133
  function resolveOfficialClientBin(binName) {
@@ -139,8 +154,29 @@ function findPathCommand(binName) {
139
154
 
140
155
  function officialClientPackageJSONPath(packageName) {
141
156
  const parts = packageName.startsWith("@") ? packageName.split("/") : [packageName];
142
- const packagePath = join(packageRoot, "node_modules", ...parts, "package.json");
143
- return existsSync(packagePath) ? packagePath : null;
157
+ for (const root of officialClientPackageRoots()) {
158
+ const packagePath = join(root, ...parts, "package.json");
159
+ if (existsSync(packagePath)) return packagePath;
160
+ }
161
+ return null;
162
+ }
163
+
164
+ function officialClientPackageRoots() {
165
+ const roots = [join(packageRoot, "node_modules")];
166
+ const npmBin = findPathCommand("npm");
167
+ if (npmBin) roots.push(join(dirname(npmBin), "node_modules"));
168
+ if (process.platform === "win32" && process.env.APPDATA) {
169
+ roots.push(join(process.env.APPDATA, "npm", "node_modules"));
170
+ }
171
+ return [...new Set(roots)];
172
+ }
173
+
174
+ function officialClientProblem(state, bin, packagePath) {
175
+ if (state === "installed") return null;
176
+ if (state === "missing") return "package_not_found";
177
+ if (!bin) return "package_present_but_command_not_found";
178
+ if (packagePath) return "command_found_but_version_check_failed";
179
+ return "unknown";
144
180
  }
145
181
 
146
182
  function readOfficialClientVersion(bin) {