@youdie006/prodex 0.40.17 → 0.40.18
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 +4 -2
- package/SECURITY.md +45 -0
- package/dist/bridge-gitignore.js +47 -0
- package/dist/browser-handoff.js +29 -16
- package/dist/browser-process.js +196 -0
- package/dist/chatgpt-browser.js +51 -63
- package/dist/cli-args.js +8 -2
- package/dist/cli-pro.js +19 -4
- package/dist/cli.js +2 -4
- package/dist/config.js +13 -25
- package/dist/repo.js +9 -2
- package/dist/safe-file.js +57 -13
- package/dist/store-writer.js +20 -5
- package/dist/store.js +19 -26
- package/dist/tui-run.js +1 -1
- package/docs/cli-reference.md +3 -1
- package/docs/platform-verification.md +164 -0
- package/package.json +7 -2
- package/prodex.mjs +11 -0
- package/scripts/npm-command.mjs +71 -0
- package/scripts/release-check.mjs +18 -11
- package/scripts/release-pack.mjs +13 -4
package/prodex.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runCli } from "./dist/cli.js";
|
|
3
|
+
|
|
4
|
+
runCli(process.argv.slice(2))
|
|
5
|
+
.then((code) => {
|
|
6
|
+
if (code !== 0) process.exitCode = code;
|
|
7
|
+
})
|
|
8
|
+
.catch((error) => {
|
|
9
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
10
|
+
process.exitCode = 1;
|
|
11
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { realpathSync, statSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
|
|
8
|
+
export function resolveNpmCliPath(options = {}) {
|
|
9
|
+
const env = normalizeNpmEnvironment(options.env ?? process.env);
|
|
10
|
+
const execPath = options.execPath ?? process.execPath;
|
|
11
|
+
const cwd = options.cwd ?? process.cwd();
|
|
12
|
+
const candidates = [];
|
|
13
|
+
const npmExecPath = envValue(env, "npm_execpath");
|
|
14
|
+
if (npmExecPath && !isCompetingPackageManagerCli(npmExecPath)) candidates.push(path.resolve(cwd, npmExecPath));
|
|
15
|
+
|
|
16
|
+
const pathValue = envValue(env, "PATH");
|
|
17
|
+
for (const entry of pathValue?.split(path.delimiter) ?? []) {
|
|
18
|
+
if (!entry) continue;
|
|
19
|
+
for (const commandName of process.platform === "win32" ? ["npm.cmd", "npm.exe", "npm"] : ["npm"]) {
|
|
20
|
+
const commandPath = path.join(entry, commandName);
|
|
21
|
+
const resolvedCommand = existingJavaScriptFile(commandPath);
|
|
22
|
+
if (resolvedCommand) candidates.push(resolvedCommand);
|
|
23
|
+
}
|
|
24
|
+
candidates.push(path.join(entry, "node_modules", "npm", "bin", "npm-cli.js"));
|
|
25
|
+
candidates.push(path.resolve(entry, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const nodeDir = path.dirname(execPath);
|
|
29
|
+
candidates.push(path.join(nodeDir, "node_modules", "npm", "bin", "npm-cli.js"));
|
|
30
|
+
candidates.push(path.resolve(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"));
|
|
31
|
+
|
|
32
|
+
for (const candidate of candidates) {
|
|
33
|
+
const resolved = existingJavaScriptFile(candidate);
|
|
34
|
+
if (resolved) return resolved;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Could not locate npm's JavaScript CLI for ${execPath}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function execNpm(args, options = {}) {
|
|
40
|
+
const env = normalizeNpmEnvironment(options.env ?? process.env);
|
|
41
|
+
const npmCliPath = resolveNpmCliPath({ cwd: options.cwd, env, execPath: process.execPath });
|
|
42
|
+
return execFileAsync(process.execPath, [npmCliPath, ...args], { ...options, env, encoding: "utf8" });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function normalizeNpmEnvironment(env, platform = process.platform) {
|
|
46
|
+
if (platform !== "win32") return env;
|
|
47
|
+
// Node otherwise sorts duplicate Windows names and can keep the inherited
|
|
48
|
+
// spelling instead of an explicit override appended by the caller.
|
|
49
|
+
const entries = new Map();
|
|
50
|
+
for (const [name, value] of Object.entries(env)) entries.set(name.toLowerCase(), [name, value]);
|
|
51
|
+
return Object.fromEntries(entries.values());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function envValue(env, name) {
|
|
55
|
+
if (typeof env[name] === "string") return env[name];
|
|
56
|
+
const entry = Object.entries(env).find(([key, value]) => key.toLowerCase() === name.toLowerCase() && typeof value === "string");
|
|
57
|
+
return entry?.[1];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isCompetingPackageManagerCli(candidate) {
|
|
61
|
+
return /^(?:pnpm|pnpx|yarn|yarnpkg)(?:\.(?:cjs|mjs|js))?$/i.test(path.basename(candidate));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function existingJavaScriptFile(candidate) {
|
|
65
|
+
try {
|
|
66
|
+
const resolved = realpathSync(candidate);
|
|
67
|
+
return /\.(?:cjs|mjs|js)$/i.test(resolved) && statSync(resolved).isFile() ? resolved : undefined;
|
|
68
|
+
} catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -4,6 +4,7 @@ import { lstat, readFile } from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
+
import { execNpm } from "./npm-command.mjs";
|
|
7
8
|
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
9
10
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -159,7 +160,7 @@ async function runFullReleaseVerification(rootDir) {
|
|
|
159
160
|
];
|
|
160
161
|
for (const [command, commandArgs] of checks) {
|
|
161
162
|
const key = [command, ...commandArgs].join(" ");
|
|
162
|
-
await run(
|
|
163
|
+
await run(command, commandArgs, rootDir, CHECK_TIMEOUT_MS[key] ?? CHECK_TIMEOUT_MS.default);
|
|
163
164
|
}
|
|
164
165
|
}
|
|
165
166
|
|
|
@@ -205,7 +206,7 @@ async function assertPackedFileModes(rootDir, packageJson) {
|
|
|
205
206
|
async function readPackedFiles(rootDir) {
|
|
206
207
|
let stdout;
|
|
207
208
|
try {
|
|
208
|
-
({ stdout } = await
|
|
209
|
+
({ stdout } = await execNpm(["pack", "--json", "--dry-run", "--ignore-scripts"], {
|
|
209
210
|
cwd: rootDir,
|
|
210
211
|
timeout: 120_000,
|
|
211
212
|
maxBuffer: 20 * 1024 * 1024
|
|
@@ -282,6 +283,9 @@ function findNonExecutableBinPackedFiles(files, packageJson) {
|
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
async function findNonExecutableBinSourceFiles(rootDir, packageJson) {
|
|
286
|
+
// Windows ACLs do not map to POSIX execute bits. npm still reports package
|
|
287
|
+
// bin entries with canonical executable modes in the packed file metadata.
|
|
288
|
+
if (process.platform === "win32") return [];
|
|
285
289
|
const invalid = [];
|
|
286
290
|
for (const packagePath of packageBinPaths(packageJson)) {
|
|
287
291
|
const filePath = path.join(rootDir, packagePath);
|
|
@@ -349,11 +353,18 @@ async function run(command, commandArgs, cwd, timeoutMs = 300_000) {
|
|
|
349
353
|
const commandLine = [command, ...commandArgs].join(" ");
|
|
350
354
|
console.log(`release_check: ${commandLine}`);
|
|
351
355
|
try {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
356
|
+
const execute = command === "npm"
|
|
357
|
+
? execNpm(commandArgs, {
|
|
358
|
+
cwd,
|
|
359
|
+
timeout: timeoutMs,
|
|
360
|
+
maxBuffer: 20 * 1024 * 1024
|
|
361
|
+
})
|
|
362
|
+
: execFileAsync(command === "node" ? process.execPath : command, commandArgs, {
|
|
363
|
+
cwd,
|
|
364
|
+
timeout: timeoutMs,
|
|
365
|
+
maxBuffer: 20 * 1024 * 1024
|
|
366
|
+
});
|
|
367
|
+
await execute;
|
|
357
368
|
} catch (error) {
|
|
358
369
|
// Surface the real failure: the one-line detail used to show the FIRST
|
|
359
370
|
// stderr line, which npm noise ("npm warn ...") could occupy while the
|
|
@@ -376,10 +387,6 @@ function printCapturedOutputTail(error, commandLine) {
|
|
|
376
387
|
}
|
|
377
388
|
}
|
|
378
389
|
|
|
379
|
-
function commandForPlatform(command) {
|
|
380
|
-
return process.platform === "win32" && command === "npm" ? "npm.cmd" : command;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
390
|
function parseArgs(values) {
|
|
384
391
|
const parsed = {
|
|
385
392
|
root: undefined,
|
package/scripts/release-pack.mjs
CHANGED
|
@@ -5,10 +5,10 @@ import { tmpdir } from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
|
+
import { execNpm } from "./npm-command.mjs";
|
|
8
9
|
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
-
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
14
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -71,7 +71,7 @@ async function ensureDistBuiltForSource(root) {
|
|
|
71
71
|
const tsconfigPath = path.join(root, "tsconfig.json");
|
|
72
72
|
const srcDir = path.join(root, "src");
|
|
73
73
|
if (!(await pathExists(tsconfigPath)) || !(await pathExists(srcDir))) return false;
|
|
74
|
-
await
|
|
74
|
+
await execNpm(["run", "build"], { cwd: root, maxBuffer: 20 * 1024 * 1024 });
|
|
75
75
|
return true;
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -211,7 +211,11 @@ async function run(command, commandArgs, cwd) {
|
|
|
211
211
|
|
|
212
212
|
async function runNpmPack(commandArgs, cwd, label) {
|
|
213
213
|
try {
|
|
214
|
-
return await
|
|
214
|
+
return await execNpm(commandArgs, {
|
|
215
|
+
cwd,
|
|
216
|
+
timeout: 120_000,
|
|
217
|
+
maxBuffer: 20 * 1024 * 1024
|
|
218
|
+
});
|
|
215
219
|
} catch (error) {
|
|
216
220
|
throw new Error(`${label} failed: ${commandFailureDetail(error)}`);
|
|
217
221
|
}
|
|
@@ -340,7 +344,12 @@ function errorMessage(error) {
|
|
|
340
344
|
}
|
|
341
345
|
|
|
342
346
|
function shellQuote(value) {
|
|
343
|
-
|
|
347
|
+
const shellSafe = /^[A-Za-z0-9_./:@=-]+$/.test(value);
|
|
348
|
+
if (shellSafe && (process.platform !== "win32" || !value.startsWith("@"))) return value;
|
|
349
|
+
const escaped = process.platform === "win32"
|
|
350
|
+
? value.replaceAll("'", "''")
|
|
351
|
+
: value.replaceAll("'", "'\\''");
|
|
352
|
+
return `'${escaped}'`;
|
|
344
353
|
}
|
|
345
354
|
|
|
346
355
|
async function readReleaseGitStatus(root) {
|