codex-mirror 0.1.1
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/LICENSE +21 -0
- package/README.md +154 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +244 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/clone-manager.d.ts +21 -0
- package/dist/core/clone-manager.js +229 -0
- package/dist/core/clone-manager.js.map +1 -0
- package/dist/core/clone-name.d.ts +8 -0
- package/dist/core/clone-name.js +93 -0
- package/dist/core/clone-name.js.map +1 -0
- package/dist/core/context.d.ts +6 -0
- package/dist/core/context.js +12 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/doctor.d.ts +10 -0
- package/dist/core/doctor.js +102 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/launcher.d.ts +7 -0
- package/dist/core/launcher.js +36 -0
- package/dist/core/launcher.js.map +1 -0
- package/dist/core/registry.d.ts +19 -0
- package/dist/core/registry.js +134 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/runtime-cloner.d.ts +10 -0
- package/dist/core/runtime-cloner.js +74 -0
- package/dist/core/runtime-cloner.js.map +1 -0
- package/dist/core/wrapper-manager.d.ts +15 -0
- package/dist/core/wrapper-manager.js +56 -0
- package/dist/core/wrapper-manager.js.map +1 -0
- package/dist/tui/index.d.ts +12 -0
- package/dist/tui/index.js +512 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/menu.d.ts +71 -0
- package/dist/tui/menu.js +487 -0
- package/dist/tui/menu.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/fs.d.ts +9 -0
- package/dist/utils/fs.js +53 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +22 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/process.d.ts +20 -0
- package/dist/utils/process.js +90 -0
- package/dist/utils/process.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { copyDir, ensureDir, exists, removePath, writeJsonFile } from "../utils/fs.js";
|
|
4
|
+
import { assertValidCloneName } from "./clone-name.js";
|
|
5
|
+
import { installRuntime } from "./runtime-cloner.js";
|
|
6
|
+
export class CloneManager {
|
|
7
|
+
registry;
|
|
8
|
+
wrappers;
|
|
9
|
+
constructor(registry, wrappers) {
|
|
10
|
+
this.registry = registry;
|
|
11
|
+
this.wrappers = wrappers;
|
|
12
|
+
}
|
|
13
|
+
async listClones() {
|
|
14
|
+
const clones = await this.registry.list();
|
|
15
|
+
return clones.slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
16
|
+
}
|
|
17
|
+
async getClone(name) {
|
|
18
|
+
const cloneName = assertValidCloneName(name);
|
|
19
|
+
const clone = await this.registry.findByName(cloneName);
|
|
20
|
+
if (!clone) {
|
|
21
|
+
throw new Error(`Clone '${cloneName}' was not found`);
|
|
22
|
+
}
|
|
23
|
+
return clone;
|
|
24
|
+
}
|
|
25
|
+
async createClone(options) {
|
|
26
|
+
const name = assertValidCloneName(options.name);
|
|
27
|
+
const existing = await this.registry.findByName(name);
|
|
28
|
+
if (existing) {
|
|
29
|
+
throw new Error(`Clone '${name}' already exists`);
|
|
30
|
+
}
|
|
31
|
+
const rootPath = resolve(options.rootPath);
|
|
32
|
+
const paths = deriveClonePaths(rootPath);
|
|
33
|
+
if (await exists(paths.metadataPath)) {
|
|
34
|
+
throw new Error(`Root path already contains a clone: ${rootPath}`);
|
|
35
|
+
}
|
|
36
|
+
let clone;
|
|
37
|
+
let rollbackCloneBase = false;
|
|
38
|
+
let wrapperInstalled = false;
|
|
39
|
+
let metadataWritten = false;
|
|
40
|
+
let registryWritten = false;
|
|
41
|
+
try {
|
|
42
|
+
await ensureDir(paths.homeDir);
|
|
43
|
+
await ensureDir(paths.codexHomeDir);
|
|
44
|
+
await ensureDir(paths.logsDir);
|
|
45
|
+
rollbackCloneBase = true;
|
|
46
|
+
const runtime = await installRuntime(paths.runtimeDir);
|
|
47
|
+
const now = new Date().toISOString();
|
|
48
|
+
clone = {
|
|
49
|
+
id: randomUUID(),
|
|
50
|
+
name,
|
|
51
|
+
rootPath,
|
|
52
|
+
runtimePath: paths.runtimeDir,
|
|
53
|
+
runtimeEntryPath: runtime.entryPath,
|
|
54
|
+
runtimeKind: runtime.kind,
|
|
55
|
+
wrapperPath: this.wrappers.getPathForClone(name),
|
|
56
|
+
codexVersionPinned: runtime.version,
|
|
57
|
+
createdAt: now,
|
|
58
|
+
updatedAt: now,
|
|
59
|
+
};
|
|
60
|
+
clone.wrapperPath = await this.wrappers.installWrapper(clone);
|
|
61
|
+
wrapperInstalled = true;
|
|
62
|
+
await this.writeCloneMetadata(clone);
|
|
63
|
+
metadataWritten = true;
|
|
64
|
+
await this.registry.upsert(clone);
|
|
65
|
+
registryWritten = true;
|
|
66
|
+
rollbackCloneBase = false;
|
|
67
|
+
return clone;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const rollbackErrors = [];
|
|
71
|
+
if (registryWritten) {
|
|
72
|
+
try {
|
|
73
|
+
await this.registry.removeByName(name);
|
|
74
|
+
}
|
|
75
|
+
catch (rollbackError) {
|
|
76
|
+
rollbackErrors.push(`registry rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (wrapperInstalled) {
|
|
80
|
+
try {
|
|
81
|
+
await this.wrappers.removeWrapper(name);
|
|
82
|
+
}
|
|
83
|
+
catch (rollbackError) {
|
|
84
|
+
rollbackErrors.push(`wrapper rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (rollbackCloneBase || metadataWritten) {
|
|
88
|
+
try {
|
|
89
|
+
await removePath(paths.cloneBaseDir);
|
|
90
|
+
}
|
|
91
|
+
catch (rollbackError) {
|
|
92
|
+
rollbackErrors.push(`clone directory rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
throw buildTransactionError("create", name, error, rollbackErrors);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async updateClone(name) {
|
|
99
|
+
const cloneName = assertValidCloneName(name);
|
|
100
|
+
const current = await this.getClone(cloneName);
|
|
101
|
+
const previous = { ...current };
|
|
102
|
+
const backupRuntimePath = `${current.runtimePath}.backup-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
103
|
+
let backupExists = false;
|
|
104
|
+
try {
|
|
105
|
+
if (await exists(current.runtimePath)) {
|
|
106
|
+
await copyDir(current.runtimePath, backupRuntimePath);
|
|
107
|
+
backupExists = true;
|
|
108
|
+
}
|
|
109
|
+
const runtime = await installRuntime(current.runtimePath);
|
|
110
|
+
const updated = {
|
|
111
|
+
...current,
|
|
112
|
+
runtimeEntryPath: runtime.entryPath,
|
|
113
|
+
runtimeKind: runtime.kind,
|
|
114
|
+
codexVersionPinned: runtime.version,
|
|
115
|
+
updatedAt: new Date().toISOString(),
|
|
116
|
+
};
|
|
117
|
+
updated.wrapperPath = await this.wrappers.installWrapper(updated);
|
|
118
|
+
await this.writeCloneMetadata(updated);
|
|
119
|
+
await this.registry.upsert(updated);
|
|
120
|
+
if (backupExists) {
|
|
121
|
+
await removePath(backupRuntimePath);
|
|
122
|
+
}
|
|
123
|
+
return updated;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
const rollbackErrors = [];
|
|
127
|
+
if (backupExists) {
|
|
128
|
+
try {
|
|
129
|
+
await removePath(current.runtimePath);
|
|
130
|
+
await copyDir(backupRuntimePath, current.runtimePath);
|
|
131
|
+
}
|
|
132
|
+
catch (rollbackError) {
|
|
133
|
+
rollbackErrors.push(`runtime rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
await this.wrappers.installWrapper(previous);
|
|
138
|
+
}
|
|
139
|
+
catch (rollbackError) {
|
|
140
|
+
rollbackErrors.push(`wrapper rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await this.writeCloneMetadata(previous);
|
|
144
|
+
}
|
|
145
|
+
catch (rollbackError) {
|
|
146
|
+
rollbackErrors.push(`metadata rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
await this.registry.upsert(previous);
|
|
150
|
+
}
|
|
151
|
+
catch (rollbackError) {
|
|
152
|
+
rollbackErrors.push(`registry rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
153
|
+
}
|
|
154
|
+
if (backupExists) {
|
|
155
|
+
try {
|
|
156
|
+
await removePath(backupRuntimePath);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Ignore backup cleanup failure after rollback.
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw buildTransactionError("update", cloneName, error, rollbackErrors);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async updateAll() {
|
|
166
|
+
const clones = await this.listClones();
|
|
167
|
+
const updated = [];
|
|
168
|
+
for (const clone of clones) {
|
|
169
|
+
updated.push(await this.updateClone(clone.name));
|
|
170
|
+
}
|
|
171
|
+
return updated;
|
|
172
|
+
}
|
|
173
|
+
async removeClone(name) {
|
|
174
|
+
const cloneName = assertValidCloneName(name);
|
|
175
|
+
const clone = await this.getClone(cloneName);
|
|
176
|
+
const paths = deriveClonePaths(clone.rootPath);
|
|
177
|
+
await this.registry.removeByName(cloneName);
|
|
178
|
+
try {
|
|
179
|
+
await removePath(paths.cloneBaseDir);
|
|
180
|
+
await this.wrappers.removeWrapper(cloneName);
|
|
181
|
+
return clone;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
const rollbackErrors = [];
|
|
185
|
+
try {
|
|
186
|
+
await this.registry.upsert(clone);
|
|
187
|
+
}
|
|
188
|
+
catch (rollbackError) {
|
|
189
|
+
rollbackErrors.push(`registry rollback failed: ${toErrorMessage(rollbackError)}`);
|
|
190
|
+
}
|
|
191
|
+
throw buildTransactionError("remove", cloneName, error, rollbackErrors);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async saveClone(clone) {
|
|
195
|
+
assertValidCloneName(clone.name);
|
|
196
|
+
await this.writeCloneMetadata(clone);
|
|
197
|
+
await this.registry.upsert(clone);
|
|
198
|
+
}
|
|
199
|
+
async writeCloneMetadata(clone) {
|
|
200
|
+
const paths = deriveClonePaths(clone.rootPath);
|
|
201
|
+
await writeJsonFile(paths.metadataPath, clone);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function buildTransactionError(operation, cloneName, rootError, rollbackErrors) {
|
|
205
|
+
const message = [`Failed to ${operation} clone '${cloneName}': ${toErrorMessage(rootError)}`];
|
|
206
|
+
if (rollbackErrors.length > 0) {
|
|
207
|
+
message.push(`Rollback issues: ${rollbackErrors.join("; ")}`);
|
|
208
|
+
}
|
|
209
|
+
return new Error(message.join(". "));
|
|
210
|
+
}
|
|
211
|
+
function toErrorMessage(error) {
|
|
212
|
+
return error instanceof Error ? error.message : String(error);
|
|
213
|
+
}
|
|
214
|
+
export function deriveClonePaths(rootPath) {
|
|
215
|
+
const cloneBaseDir = resolve(rootPath, ".codex-mirror");
|
|
216
|
+
const runtimeDir = resolve(cloneBaseDir, "runtime");
|
|
217
|
+
const homeDir = resolve(cloneBaseDir, "home");
|
|
218
|
+
const codexHomeDir = resolve(homeDir, ".codex");
|
|
219
|
+
return {
|
|
220
|
+
cloneBaseDir,
|
|
221
|
+
metadataPath: resolve(cloneBaseDir, "clone.json"),
|
|
222
|
+
runtimeDir,
|
|
223
|
+
runtimeEntryPath: resolve(runtimeDir, "bin", "codex"),
|
|
224
|
+
homeDir,
|
|
225
|
+
codexHomeDir,
|
|
226
|
+
logsDir: resolve(cloneBaseDir, "logs"),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=clone-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clone-manager.js","sourceRoot":"","sources":["../../src/core/clone-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface CloneNameValidation {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
normalized: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function validateCloneName(input: string): CloneNameValidation;
|
|
7
|
+
export declare function assertValidCloneName(input: string): string;
|
|
8
|
+
export declare function sanitizeCloneName(input: string): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const MAX_CLONE_NAME_LENGTH = 64;
|
|
2
|
+
const CLONE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
3
|
+
const WINDOWS_RESERVED_NAMES = new Set([
|
|
4
|
+
"con",
|
|
5
|
+
"prn",
|
|
6
|
+
"aux",
|
|
7
|
+
"nul",
|
|
8
|
+
"com1",
|
|
9
|
+
"com2",
|
|
10
|
+
"com3",
|
|
11
|
+
"com4",
|
|
12
|
+
"com5",
|
|
13
|
+
"com6",
|
|
14
|
+
"com7",
|
|
15
|
+
"com8",
|
|
16
|
+
"com9",
|
|
17
|
+
"lpt1",
|
|
18
|
+
"lpt2",
|
|
19
|
+
"lpt3",
|
|
20
|
+
"lpt4",
|
|
21
|
+
"lpt5",
|
|
22
|
+
"lpt6",
|
|
23
|
+
"lpt7",
|
|
24
|
+
"lpt8",
|
|
25
|
+
"lpt9",
|
|
26
|
+
]);
|
|
27
|
+
export function validateCloneName(input) {
|
|
28
|
+
const normalized = input.trim();
|
|
29
|
+
if (!normalized) {
|
|
30
|
+
return { ok: false, normalized, error: "Clone name cannot be empty" };
|
|
31
|
+
}
|
|
32
|
+
if (normalized.length > MAX_CLONE_NAME_LENGTH) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
normalized,
|
|
36
|
+
error: `Clone name must be ${MAX_CLONE_NAME_LENGTH} characters or fewer`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (normalized.includes("/") || normalized.includes("\\")) {
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
normalized,
|
|
43
|
+
error: "Clone name cannot contain path separators",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (normalized.includes("..")) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
normalized,
|
|
50
|
+
error: "Clone name cannot include '..'",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!CLONE_NAME_RE.test(normalized)) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
normalized,
|
|
57
|
+
error: "Use letters, numbers, dot, underscore, and hyphen; must start with letter or number",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (WINDOWS_RESERVED_NAMES.has(normalized.toLowerCase())) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
normalized,
|
|
64
|
+
error: "Clone name is reserved on Windows",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { ok: true, normalized };
|
|
68
|
+
}
|
|
69
|
+
export function assertValidCloneName(input) {
|
|
70
|
+
const result = validateCloneName(input);
|
|
71
|
+
if (!result.ok) {
|
|
72
|
+
throw new Error(result.error ?? "Invalid clone name");
|
|
73
|
+
}
|
|
74
|
+
return result.normalized;
|
|
75
|
+
}
|
|
76
|
+
export function sanitizeCloneName(input) {
|
|
77
|
+
const base = input
|
|
78
|
+
.trim()
|
|
79
|
+
.replace(/[\\/]/g, "-")
|
|
80
|
+
.replace(/\.\.+/g, ".")
|
|
81
|
+
.replace(/[^a-zA-Z0-9._-]/g, "-")
|
|
82
|
+
.replace(/^[^a-zA-Z0-9]+/, "")
|
|
83
|
+
.replace(/-+/g, "-")
|
|
84
|
+
.slice(0, MAX_CLONE_NAME_LENGTH);
|
|
85
|
+
if (!base) {
|
|
86
|
+
return "clone";
|
|
87
|
+
}
|
|
88
|
+
if (!/^[a-zA-Z0-9]/.test(base)) {
|
|
89
|
+
return `c-${base}`.slice(0, MAX_CLONE_NAME_LENGTH);
|
|
90
|
+
}
|
|
91
|
+
return base;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=clone-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clone-name.js","sourceRoot":"","sources":["../../src/core/clone-name.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function resolveContext() {
|
|
4
|
+
const globalRoot = process.env.CODEX_MIRROR_HOME ?? join(homedir(), ".codex-mirror");
|
|
5
|
+
const defaultBinDir = process.env.CODEX_MIRROR_BIN_DIR ?? join(homedir(), ".local", "bin");
|
|
6
|
+
return {
|
|
7
|
+
globalRoot,
|
|
8
|
+
registryPath: join(globalRoot, "registry.json"),
|
|
9
|
+
defaultBinDir,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,MAAM,UAAU,cAAc;IAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;IACrF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC3F,OAAO;QACL,UAAU;QACV,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC;QAC/C,aAAa;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CloneRecord, DoctorResult } from "../types.js";
|
|
2
|
+
import { Launcher } from "./launcher.js";
|
|
3
|
+
export declare class Doctor {
|
|
4
|
+
private readonly launcher;
|
|
5
|
+
private readonly authTimeoutMs;
|
|
6
|
+
private readonly concurrency;
|
|
7
|
+
constructor(launcher: Launcher, authTimeoutMs?: number, concurrency?: number);
|
|
8
|
+
checkOne(clone: CloneRecord): Promise<DoctorResult>;
|
|
9
|
+
checkMany(clones: CloneRecord[]): Promise<DoctorResult[]>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { exists, isFile, isWritable } from "../utils/fs.js";
|
|
2
|
+
import { deriveClonePaths } from "./clone-manager.js";
|
|
3
|
+
export class Doctor {
|
|
4
|
+
launcher;
|
|
5
|
+
authTimeoutMs;
|
|
6
|
+
concurrency;
|
|
7
|
+
constructor(launcher, authTimeoutMs = 5_000, concurrency = 4) {
|
|
8
|
+
this.launcher = launcher;
|
|
9
|
+
this.authTimeoutMs = authTimeoutMs;
|
|
10
|
+
this.concurrency = concurrency;
|
|
11
|
+
}
|
|
12
|
+
async checkOne(clone) {
|
|
13
|
+
const errors = [];
|
|
14
|
+
const paths = deriveClonePaths(clone.rootPath);
|
|
15
|
+
if (!(await isFile(clone.runtimeEntryPath))) {
|
|
16
|
+
errors.push(`Runtime entry missing: ${clone.runtimeEntryPath}`);
|
|
17
|
+
}
|
|
18
|
+
if (!(await isFile(clone.wrapperPath))) {
|
|
19
|
+
errors.push(`Wrapper missing: ${clone.wrapperPath}`);
|
|
20
|
+
}
|
|
21
|
+
const writablePaths = [paths.cloneBaseDir, paths.homeDir, paths.codexHomeDir, paths.logsDir];
|
|
22
|
+
const writableChecks = await Promise.all(writablePaths.map(async (path) => ({
|
|
23
|
+
path,
|
|
24
|
+
exists: await exists(path),
|
|
25
|
+
writable: await isWritable(path),
|
|
26
|
+
})));
|
|
27
|
+
for (const check of writableChecks) {
|
|
28
|
+
if (!check.exists) {
|
|
29
|
+
errors.push(`Missing directory: ${check.path}`);
|
|
30
|
+
}
|
|
31
|
+
else if (!check.writable) {
|
|
32
|
+
errors.push(`Directory is not writable: ${check.path}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
let authStatus = "unknown";
|
|
36
|
+
const auth = await this.launcher.capture(clone, ["login", "status"], process.cwd(), this.authTimeoutMs);
|
|
37
|
+
const authOutput = `${auth.stdout}\n${auth.stderr}`;
|
|
38
|
+
if (auth.timedOut) {
|
|
39
|
+
errors.push(`Auth check timed out after ${this.authTimeoutMs}ms`);
|
|
40
|
+
}
|
|
41
|
+
if (/Not logged in/i.test(authOutput)) {
|
|
42
|
+
authStatus = "not_logged_in";
|
|
43
|
+
}
|
|
44
|
+
else if (/Logged in/i.test(authOutput)) {
|
|
45
|
+
authStatus = "logged_in";
|
|
46
|
+
}
|
|
47
|
+
if (authStatus === "unknown") {
|
|
48
|
+
errors.push("Could not determine auth status");
|
|
49
|
+
}
|
|
50
|
+
const writable = writableChecks.every((check) => check.exists && check.writable);
|
|
51
|
+
return {
|
|
52
|
+
name: clone.name,
|
|
53
|
+
ok: errors.length === 0,
|
|
54
|
+
runtimePath: clone.runtimeEntryPath,
|
|
55
|
+
wrapperPath: clone.wrapperPath,
|
|
56
|
+
authStatus,
|
|
57
|
+
writable,
|
|
58
|
+
errors,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async checkMany(clones) {
|
|
62
|
+
if (clones.length === 0) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const results = new Array(clones.length);
|
|
66
|
+
const workers = Math.min(Math.max(1, this.concurrency), clones.length);
|
|
67
|
+
let index = 0;
|
|
68
|
+
await Promise.all(Array.from({ length: workers }, async () => {
|
|
69
|
+
// eslint-disable-next-line no-constant-condition
|
|
70
|
+
while (true) {
|
|
71
|
+
const current = index;
|
|
72
|
+
index += 1;
|
|
73
|
+
if (current >= clones.length) {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
const clone = clones[current];
|
|
77
|
+
if (!clone) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
results[current] = await this.checkOne(clone);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
results[current] = {
|
|
85
|
+
name: clone.name,
|
|
86
|
+
ok: false,
|
|
87
|
+
runtimePath: clone.runtimeEntryPath,
|
|
88
|
+
wrapperPath: clone.wrapperPath,
|
|
89
|
+
authStatus: "unknown",
|
|
90
|
+
writable: false,
|
|
91
|
+
errors: [`Unexpected doctor error: ${toErrorMessage(error)}`],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}));
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function toErrorMessage(error) {
|
|
100
|
+
return error instanceof Error ? error.message : String(error);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CloneRecord } from "../types.js";
|
|
2
|
+
import { CaptureResult } from "../utils/process.js";
|
|
3
|
+
export declare class Launcher {
|
|
4
|
+
run(clone: CloneRecord, args: string[], cwd?: string): Promise<number>;
|
|
5
|
+
capture(clone: CloneRecord, args: string[], cwd?: string, timeoutMs?: number): Promise<CaptureResult>;
|
|
6
|
+
}
|
|
7
|
+
export declare function buildEnv(clone: CloneRecord): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { runCapture, runInteractive } from "../utils/process.js";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export class Launcher {
|
|
4
|
+
async run(clone, args, cwd = process.cwd()) {
|
|
5
|
+
const { command, commandArgs } = resolveRuntimeInvocation(clone, args);
|
|
6
|
+
const result = await runInteractive(command, commandArgs, buildEnv(clone), cwd);
|
|
7
|
+
return result.code;
|
|
8
|
+
}
|
|
9
|
+
capture(clone, args, cwd = process.cwd(), timeoutMs = 8_000) {
|
|
10
|
+
const { command, commandArgs } = resolveRuntimeInvocation(clone, args);
|
|
11
|
+
return runCapture(command, commandArgs, buildEnv(clone), cwd, timeoutMs);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function resolveRuntimeInvocation(clone, args) {
|
|
15
|
+
if (clone.runtimeKind === "npm-package") {
|
|
16
|
+
return {
|
|
17
|
+
command: process.execPath,
|
|
18
|
+
commandArgs: [clone.runtimeEntryPath, ...args],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
command: clone.runtimeEntryPath,
|
|
23
|
+
commandArgs: args,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function buildEnv(clone) {
|
|
27
|
+
const home = join(clone.rootPath, ".codex-mirror", "home");
|
|
28
|
+
return {
|
|
29
|
+
...process.env,
|
|
30
|
+
HOME: home,
|
|
31
|
+
XDG_CONFIG_HOME: join(home, ".config"),
|
|
32
|
+
XDG_DATA_HOME: join(home, ".local", "share"),
|
|
33
|
+
XDG_CACHE_HOME: join(home, ".cache"),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=launcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/core/launcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,OAAO,QAAQ;IACnB,KAAK,CAAC,GAAG,CAAC,KAAkB,EAAE,IAAc,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;QAC/D,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,wBAAwB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QAChF,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,OAAO,CAAC,KAAkB,EAAE,IAAc,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,GAAG,KAAK;QAChF,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,wBAAwB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACvE,OAAO,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;IAC3E,CAAC;CACF;AAED,SAAS,wBAAwB,CAAC,KAAkB,EAAE,IAAc;IAClE,IAAI,KAAK,CAAC,WAAW,KAAK,aAAa,EAAE,CAAC;QACxC,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,QAAQ;YACzB,WAAW,EAAE,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC;SAC/C,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,gBAAgB;QAC/B,WAAW,EAAE,IAAI;KAClB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAkB;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;IAC3D,OAAO;QACL,GAAG,OAAO,CAAC,GAAG;QACd,IAAI,EAAE,IAAI;QACV,eAAe,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QACtC,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC;QAC5C,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;KACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CloneRecord, Registry } from "../types.js";
|
|
2
|
+
export declare class RegistryStore {
|
|
3
|
+
private readonly registryPath;
|
|
4
|
+
private readonly lockTimeoutMs;
|
|
5
|
+
private readonly lockPollIntervalMs;
|
|
6
|
+
private readonly lockPath;
|
|
7
|
+
private readonly staleLockMs;
|
|
8
|
+
constructor(registryPath: string, lockTimeoutMs?: number, lockPollIntervalMs?: number);
|
|
9
|
+
load(): Promise<Registry>;
|
|
10
|
+
save(registry: Registry): Promise<void>;
|
|
11
|
+
list(): Promise<CloneRecord[]>;
|
|
12
|
+
findByName(name: string): Promise<CloneRecord | undefined>;
|
|
13
|
+
upsert(clone: CloneRecord): Promise<void>;
|
|
14
|
+
removeByName(name: string): Promise<CloneRecord | undefined>;
|
|
15
|
+
private saveUnlocked;
|
|
16
|
+
private withLock;
|
|
17
|
+
private acquireLock;
|
|
18
|
+
private maybeBreakStaleLock;
|
|
19
|
+
}
|