@qpfai/pf-gate-cli 2.0.1-win32-arm64 → 2.0.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/README.md CHANGED
@@ -1,14 +1,64 @@
1
- # @qpfai/pf-gate-cli (win32-arm64)
1
+ # @qpfai/pf-gate-cli
2
2
 
3
- Platform runtime payload for PF Gate CLI.
3
+ Global CLI launcher for PF Gate with Codex-style platform runtime packages.
4
4
 
5
- The bundled launcher at `vendor/<target-triple>/pf-gate/` runs PF Gate through Python.
5
+ ## Install
6
6
 
7
- Default install source:
8
- - bundled wheel: `vendor/<target-triple>/python/persons_field-1.0.0-py3-none-any.whl`
7
+ ```bash
8
+ npm i -g @qpfai/pf-gate-cli
9
+ ```
9
10
 
10
- Runtime behavior:
11
- - Uses `PF_GATE_PYTHON` when set, else probes `python3` then `python` (Windows also tries `py -3`).
12
- - Installs from bundled wheel by default.
13
- - Optional override: set `PF_GATE_PIP_SPEC` to force a different install source.
14
- - Starts `persons_field.terminal.launcher` with forwarded arguments.
11
+ ## Use
12
+
13
+ Open the PF Gate terminal UX:
14
+
15
+ ```bash
16
+ pf gate
17
+ ```
18
+
19
+ Run direct CLI commands:
20
+
21
+ ```bash
22
+ pf --version
23
+ pf gate --selftest
24
+ ```
25
+
26
+ The launcher resolves a platform package at install time and executes a bundled runtime binary at run time.
27
+
28
+ ## Maintainer release flow
29
+
30
+ Set the release version for all npm package manifests:
31
+
32
+ ```bash
33
+ node npm/tools/set-version.mjs 2.0.1
34
+ ```
35
+
36
+ Validate release topology without publishing:
37
+
38
+ ```bash
39
+ node npm/tools/publish-all.mjs --dry-run
40
+ ```
41
+
42
+ The dry run warns on placeholder runtime payloads by default.
43
+ To fail dry run on placeholders, use:
44
+
45
+ ```bash
46
+ node npm/tools/publish-all.mjs --dry-run --strict-placeholder-check
47
+ ```
48
+
49
+ Publish all platform variants, then the meta package:
50
+
51
+ ```bash
52
+ node npm/tools/publish-all.mjs --registry=https://registry.npmjs.org/
53
+ ```
54
+
55
+ With npm 2FA:
56
+
57
+ ```bash
58
+ node npm/tools/publish-all.mjs --registry=https://registry.npmjs.org/ --otp=<code>
59
+ ```
60
+
61
+ Each platform package must contain:
62
+
63
+ - `vendor/<target-triple>/pf-gate/<binary>`
64
+ - optional helper tools in `vendor/<target-triple>/path/`
package/bin/pf.mjs ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCli } from "../lib/main.mjs";
4
+
5
+ runCli(process.argv.slice(2)).catch((error) => {
6
+ const message = error instanceof Error ? error.message : String(error);
7
+ console.error(`PF Gate launcher error: ${message}`);
8
+ process.exit(1);
9
+ });
10
+
package/lib/main.mjs ADDED
@@ -0,0 +1,229 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { createRequire } from "node:module";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
11
+ const require = createRequire(import.meta.url);
12
+
13
+ const PLATFORM_PACKAGE_BY_TARGET = Object.freeze({
14
+ "x86_64-unknown-linux-musl": "@qpfai/pf-gate-cli-linux-x64",
15
+ "aarch64-unknown-linux-musl": "@qpfai/pf-gate-cli-linux-arm64",
16
+ "x86_64-apple-darwin": "@qpfai/pf-gate-cli-darwin-x64",
17
+ "aarch64-apple-darwin": "@qpfai/pf-gate-cli-darwin-arm64",
18
+ "x86_64-pc-windows-msvc": "@qpfai/pf-gate-cli-win32-x64",
19
+ "aarch64-pc-windows-msvc": "@qpfai/pf-gate-cli-win32-arm64",
20
+ });
21
+
22
+ function commandForPackageManager(packageManager) {
23
+ if (packageManager === "bun") {
24
+ return "bun install -g @qpfai/pf-gate-cli";
25
+ }
26
+ return "npm install -g @qpfai/pf-gate-cli";
27
+ }
28
+
29
+ function firstExisting(paths, existsSyncFn) {
30
+ for (const candidate of paths) {
31
+ if (existsSyncFn(candidate)) {
32
+ return candidate;
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+
38
+ export function resolveTargetTriple(platform = process.platform, arch = process.arch) {
39
+ switch (platform) {
40
+ case "linux":
41
+ case "android":
42
+ if (arch === "x64") {
43
+ return "x86_64-unknown-linux-musl";
44
+ }
45
+ if (arch === "arm64") {
46
+ return "aarch64-unknown-linux-musl";
47
+ }
48
+ return null;
49
+ case "darwin":
50
+ if (arch === "x64") {
51
+ return "x86_64-apple-darwin";
52
+ }
53
+ if (arch === "arm64") {
54
+ return "aarch64-apple-darwin";
55
+ }
56
+ return null;
57
+ case "win32":
58
+ if (arch === "x64") {
59
+ return "x86_64-pc-windows-msvc";
60
+ }
61
+ if (arch === "arm64") {
62
+ return "aarch64-pc-windows-msvc";
63
+ }
64
+ return null;
65
+ default:
66
+ return null;
67
+ }
68
+ }
69
+
70
+ export function packageAliasForTarget(targetTriple) {
71
+ return PLATFORM_PACKAGE_BY_TARGET[targetTriple] || null;
72
+ }
73
+
74
+ export function binaryNameCandidates(platform = process.platform) {
75
+ if (platform === "win32") {
76
+ return ["pf-gate.exe", "pf-gate.cmd", "pf-gate.bat"];
77
+ }
78
+ return ["pf-gate"];
79
+ }
80
+
81
+ export function prependPath(existingPath, newDirs, platform = process.platform) {
82
+ const separator = platform === "win32" ? ";" : ":";
83
+ const current = String(existingPath || "")
84
+ .split(separator)
85
+ .filter(Boolean);
86
+ return [...newDirs, ...current].join(separator);
87
+ }
88
+
89
+ export function detectPackageManager(env = process.env, dirnameHint = __dirname) {
90
+ const userAgent = String(env.npm_config_user_agent || "");
91
+ if (/\bbun\//.test(userAgent)) {
92
+ return "bun";
93
+ }
94
+ const execPath = String(env.npm_execpath || "");
95
+ if (execPath.includes("bun")) {
96
+ return "bun";
97
+ }
98
+ if (
99
+ dirnameHint.includes(".bun/install/global") ||
100
+ dirnameHint.includes(".bun\\install\\global")
101
+ ) {
102
+ return "bun";
103
+ }
104
+ if (userAgent) {
105
+ return "npm";
106
+ }
107
+ return null;
108
+ }
109
+
110
+ export function shouldUseShellForBinary(binaryPath, platform = process.platform) {
111
+ if (platform !== "win32") {
112
+ return false;
113
+ }
114
+ return /\.(cmd|bat)$/i.test(binaryPath);
115
+ }
116
+
117
+ export function resolveRuntimeBinary(options = {}) {
118
+ const platform = options.platform || process.platform;
119
+ const arch = options.arch || process.arch;
120
+ const packageRoot = options.packageRoot || PACKAGE_ROOT;
121
+ const existsSyncFn = options.existsSync || existsSync;
122
+ const requireResolve = options.requireResolve || ((specifier) => require.resolve(specifier));
123
+
124
+ const targetTriple = resolveTargetTriple(platform, arch);
125
+ if (!targetTriple) {
126
+ throw new Error(`Unsupported platform: ${platform} (${arch})`);
127
+ }
128
+
129
+ const packageAlias = packageAliasForTarget(targetTriple);
130
+ if (!packageAlias) {
131
+ throw new Error(`Unsupported target triple: ${targetTriple}`);
132
+ }
133
+
134
+ const names = binaryNameCandidates(platform);
135
+ const localVendorRoot = path.join(packageRoot, "vendor");
136
+ const localBinaryCandidates = names.map((name) =>
137
+ path.join(localVendorRoot, targetTriple, "pf-gate", name),
138
+ );
139
+
140
+ let vendorRoot = null;
141
+ try {
142
+ const packageJsonPath = requireResolve(`${packageAlias}/package.json`);
143
+ vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
144
+ } catch {
145
+ const localBinary = firstExisting(localBinaryCandidates, existsSyncFn);
146
+ if (localBinary) {
147
+ vendorRoot = localVendorRoot;
148
+ }
149
+ }
150
+
151
+ if (!vendorRoot) {
152
+ const packageManager = detectPackageManager();
153
+ const reinstallCommand = commandForPackageManager(packageManager);
154
+ throw new Error(
155
+ `Missing optional dependency ${packageAlias}. Reinstall PF Gate CLI: ${reinstallCommand}`,
156
+ );
157
+ }
158
+
159
+ const binaryCandidates = names.map((name) => path.join(vendorRoot, targetTriple, "pf-gate", name));
160
+ const binaryPath = firstExisting(binaryCandidates, existsSyncFn);
161
+ if (!binaryPath) {
162
+ throw new Error(
163
+ `PF Gate binary missing for ${targetTriple}. Expected one of: ${binaryCandidates.join(", ")}`,
164
+ );
165
+ }
166
+
167
+ const pathDir = path.join(vendorRoot, targetTriple, "path");
168
+ const additionalPathDirs = existsSyncFn(pathDir) ? [pathDir] : [];
169
+ return {
170
+ targetTriple,
171
+ packageAlias,
172
+ vendorRoot,
173
+ binaryPath,
174
+ additionalPathDirs,
175
+ };
176
+ }
177
+
178
+ function forwardSignal(child, signal) {
179
+ if (child.killed) {
180
+ return;
181
+ }
182
+ try {
183
+ child.kill(signal);
184
+ } catch {
185
+ // Ignore child process signaling failures.
186
+ }
187
+ }
188
+
189
+ export async function runCli(args) {
190
+ const runtime = resolveRuntimeBinary();
191
+ const env = { ...process.env };
192
+ if (runtime.additionalPathDirs.length > 0) {
193
+ env.PATH = prependPath(env.PATH, runtime.additionalPathDirs);
194
+ }
195
+ const packageManager = detectPackageManager();
196
+ const managedByVar = packageManager === "bun" ? "PF_GATE_MANAGED_BY_BUN" : "PF_GATE_MANAGED_BY_NPM";
197
+ env[managedByVar] = "1";
198
+
199
+ const child = spawn(runtime.binaryPath, args, {
200
+ stdio: "inherit",
201
+ env,
202
+ shell: shouldUseShellForBinary(runtime.binaryPath),
203
+ });
204
+
205
+ child.on("error", (error) => {
206
+ console.error(error);
207
+ process.exit(1);
208
+ });
209
+
210
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
211
+ process.on(signal, () => forwardSignal(child, signal));
212
+ }
213
+
214
+ const childResult = await new Promise((resolve) => {
215
+ child.on("exit", (code, signal) => {
216
+ if (signal) {
217
+ resolve({ type: "signal", signal });
218
+ return;
219
+ }
220
+ resolve({ type: "code", exitCode: code ?? 1 });
221
+ });
222
+ });
223
+
224
+ if (childResult.type === "signal") {
225
+ process.kill(process.pid, childResult.signal);
226
+ return;
227
+ }
228
+ process.exit(childResult.exitCode);
229
+ }
package/package.json CHANGED
@@ -1,22 +1,33 @@
1
1
  {
2
2
  "name": "@qpfai/pf-gate-cli",
3
- "version": "2.0.1-win32-arm64",
4
- "description": "PF Gate runtime payload for win32 arm64.",
3
+ "version": "2.0.1",
4
+ "description": "PF Gate platform launcher with optional native runtime packages.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
- "os": [
8
- "win32"
9
- ],
10
- "cpu": [
11
- "arm64"
12
- ],
7
+ "bin": {
8
+ "pf": "bin/pf.mjs",
9
+ "pf-gate": "bin/pf.mjs"
10
+ },
13
11
  "files": [
12
+ "bin",
13
+ "lib",
14
14
  "vendor",
15
15
  "README.md"
16
16
  ],
17
+ "scripts": {
18
+ "test": "node --test tests/*.test.mjs"
19
+ },
17
20
  "engines": {
18
21
  "node": ">=18"
19
22
  },
23
+ "optionalDependencies": {
24
+ "@qpfai/pf-gate-cli-linux-x64": "npm:@qpfai/pf-gate-cli@2.0.1-linux-x64",
25
+ "@qpfai/pf-gate-cli-linux-arm64": "npm:@qpfai/pf-gate-cli@2.0.1-linux-arm64",
26
+ "@qpfai/pf-gate-cli-darwin-x64": "npm:@qpfai/pf-gate-cli@2.0.1-darwin-x64",
27
+ "@qpfai/pf-gate-cli-darwin-arm64": "npm:@qpfai/pf-gate-cli@2.0.1-darwin-arm64",
28
+ "@qpfai/pf-gate-cli-win32-x64": "npm:@qpfai/pf-gate-cli@2.0.1-win32-x64",
29
+ "@qpfai/pf-gate-cli-win32-arm64": "npm:@qpfai/pf-gate-cli@2.0.1-win32-arm64"
30
+ },
20
31
  "publishConfig": {
21
32
  "access": "public"
22
33
  }
@@ -1,50 +0,0 @@
1
- @echo off
2
- setlocal
3
-
4
- set "SCRIPT_DIR=%~dp0"
5
- set "BUNDLED_WHEEL=%SCRIPT_DIR%..\python\persons_field-1.0.0-py3-none-any.whl"
6
-
7
- set "PYTHON_BIN=%PF_GATE_PYTHON%"
8
- if "%PYTHON_BIN%"=="" (
9
- where python >nul 2>nul
10
- if not errorlevel 1 (
11
- set "PYTHON_BIN=python"
12
- ) else (
13
- where py >nul 2>nul
14
- if not errorlevel 1 (
15
- set "PYTHON_BIN=py -3"
16
- ) else (
17
- echo PF Gate runtime error: Python 3.13+ not found. Install Python or set PF_GATE_PYTHON. 1>&2
18
- exit /b 1
19
- )
20
- )
21
- )
22
-
23
- set "PIP_TARGET=%PF_GATE_PIP_SPEC%"
24
- if "%PIP_TARGET%"=="" set "PIP_TARGET=%BUNDLED_WHEEL%"
25
- if "%PF_GATE_PIP_SPEC%"=="" (
26
- if not exist "%BUNDLED_WHEEL%" (
27
- echo PF Gate runtime error: missing bundled wheel at %BUNDLED_WHEEL% 1>&2
28
- exit /b 1
29
- )
30
- )
31
-
32
- call %PYTHON_BIN% -c "import persons_field.terminal.launcher" >nul 2>nul
33
- if errorlevel 1 (
34
- echo PF Gate runtime: persons_field is missing; installing %PIP_TARGET%... 1>&2
35
- call %PYTHON_BIN% -m pip --version >nul 2>nul
36
- if errorlevel 1 (
37
- call %PYTHON_BIN% -m ensurepip --upgrade >nul 2>nul
38
- )
39
- call %PYTHON_BIN% -m pip install --upgrade --disable-pip-version-check "%PIP_TARGET%"
40
- if errorlevel 1 (
41
- call %PYTHON_BIN% -m pip install --user --upgrade --disable-pip-version-check "%PIP_TARGET%"
42
- if errorlevel 1 (
43
- echo PF Gate runtime error: failed to install %PIP_TARGET%. Set PF_GATE_PYTHON and PF_GATE_PIP_SPEC, then retry. 1>&2
44
- exit /b 1
45
- )
46
- )
47
- )
48
-
49
- call %PYTHON_BIN% -m persons_field.terminal.launcher %*
50
- exit /b %ERRORLEVEL%