@pugi/cli 1.0.0-alpha.251 → 1.0.0-alpha.253

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pugi/cli",
3
- "version": "1.0.0-alpha.251",
4
- "description": "Pugi CLI - terminal-native software execution system",
3
+ "version": "1.0.0-alpha.253",
4
+ "description": "Pugi CLI: an AI product team in your terminal that plans, builds, tests, security-checks and publishes from a plain-language brief",
5
5
  "license": "MIT",
6
6
  "homepage": "https://pugi.io",
7
7
  "repository": {
@@ -12,9 +12,13 @@
12
12
  "pugi",
13
13
  "cli",
14
14
  "ai",
15
- "agents",
16
- "engineering",
17
- "orchestrator"
15
+ "ai-agents",
16
+ "coding-agent",
17
+ "website-builder",
18
+ "app-builder",
19
+ "security",
20
+ "mcp",
21
+ "terminal"
18
22
  ],
19
23
  "bin": {
20
24
  "pugi": "./bin/pugi"
@@ -42,12 +46,12 @@
42
46
  "THIRD_PARTY_NOTICES.md"
43
47
  ],
44
48
  "optionalDependencies": {
45
- "@pugi/cli-darwin-arm64": "1.0.0-alpha.251",
46
- "@pugi/cli-darwin-x64": "1.0.0-alpha.251",
47
- "@pugi/cli-linux-arm64": "1.0.0-alpha.251",
48
- "@pugi/cli-linux-x64": "1.0.0-alpha.251",
49
- "@pugi/cli-windows-arm64": "1.0.0-alpha.251",
50
- "@pugi/cli-windows-x64": "1.0.0-alpha.251"
49
+ "@pugi/cli-darwin-arm64": "1.0.0-alpha.253",
50
+ "@pugi/cli-darwin-x64": "1.0.0-alpha.253",
51
+ "@pugi/cli-linux-arm64": "1.0.0-alpha.253",
52
+ "@pugi/cli-linux-x64": "1.0.0-alpha.253",
53
+ "@pugi/cli-windows-arm64": "1.0.0-alpha.253",
54
+ "@pugi/cli-windows-x64": "1.0.0-alpha.253"
51
55
  },
52
- "gitHead": "67921ea690b92301599c3514b8f6f9cb13c1feb8"
56
+ "gitHead": "d2335b6e88ed2f6b69acdb6172b27c8da3965387"
53
57
  }
package/postinstall.mjs CHANGED
@@ -1,31 +1,166 @@
1
1
  #!/usr/bin/env node
2
+ // Postinstall of the @pugi/cli npm package (PUG-44). `bin/pugi` ships as a Node
3
+ // launcher (launcher.cjs) that works wherever Node is installed. This script
4
+ // swaps it for the native binary where that is safe: on POSIX under npm or bun,
5
+ // whose global bin link is a symlink that stays valid when its target becomes a
6
+ // native binary. That also spares a bun user without Node the launcher's
7
+ // `#!/usr/bin/env node` (review on #363). Elsewhere (Windows, where npm's shims
8
+ // run the launcher with node; pnpm, whose shim fixes the interpreter from the
9
+ // shebang; an unknown or missing user agent, e.g. this script run by hand) it
10
+ // leaves the launcher alone.
11
+ //
12
+ // It never fails the install: the launcher already works without it. Until the
13
+ // final rename succeeds, `bin/pugi` is the original launcher.
14
+ import childProcess from "child_process"
2
15
  import fs from "fs"
3
16
  import os from "os"
4
17
  import path from "path"
5
18
  import { createRequire } from "module"
6
19
  import { fileURLToPath } from "url"
7
20
 
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
- const require = createRequire(import.meta.url)
10
-
11
- const platformMap = { darwin: "darwin", linux: "linux", win32: "windows" }
12
- const archMap = { x64: "x64", arm64: "arm64", arm: "arm" }
13
- const platform = platformMap[os.platform()] ?? os.platform()
14
- const arch = archMap[os.arch()] ?? os.arch()
15
- const sourceBinary = platform === "windows" ? "pugi.exe" : "pugi"
16
- const targetBinary = path.join(__dirname, "bin", "pugi")
17
-
18
- const pkgName = `@pugi/cli-${platform}-${arch}`
19
-
20
- try {
21
- const pkgJsonPath = require.resolve(`${pkgName}/package.json`)
22
- const binary = path.join(path.dirname(pkgJsonPath), "bin", sourceBinary)
23
- if (!fs.existsSync(binary)) throw new Error(`Binary not found: ${binary}`)
24
- if (fs.existsSync(targetBinary)) fs.unlinkSync(targetBinary)
25
- try { fs.linkSync(binary, targetBinary) } catch { fs.copyFileSync(binary, targetBinary) }
26
- fs.chmodSync(targetBinary, 0o755)
27
- } catch (err) {
28
- console.error(`@pugi/cli: unable to install native binary for ${platform}-${arch}: ${err.message}`)
29
- console.error(`Try installing manually: npm install -g ${pkgName}@${require("./package.json").version}`)
30
- process.exit(1)
21
+ const PLATFORMS = { darwin: "darwin", linux: "linux", win32: "windows" }
22
+ const ARCHES = { x64: "x64", arm64: "arm64" }
23
+ const VERIFY_TIMEOUT_MS = 20_000
24
+
25
+ export function shouldReplaceLauncher({ platform, userAgent }) {
26
+ return platform !== "win32" && typeof userAgent === "string" && /^(npm|bun)\//.test(userAgent)
27
+ }
28
+
29
+ /**
30
+ * The env for the `--version` check. The engine creates its XDG directories
31
+ * and `<tmpdir>/pugi` as soon as it loads (core/src/global.ts), before it reads
32
+ * its arguments. Under `sudo npm i -g` with HOME kept (the macOS default) they
33
+ * would be created as root in the user's home and /tmp, and the user's own
34
+ * `pugi` would later fail with EACCES (review on #363). So the check runs with
35
+ * all of them inside a throwaway directory.
36
+ */
37
+ export function sandboxEnv(dir, env = process.env) {
38
+ return {
39
+ ...env,
40
+ HOME: dir,
41
+ USERPROFILE: dir,
42
+ XDG_CONFIG_HOME: path.join(dir, "config"),
43
+ XDG_DATA_HOME: path.join(dir, "data"),
44
+ XDG_CACHE_HOME: path.join(dir, "cache"),
45
+ XDG_STATE_HOME: path.join(dir, "state"),
46
+ TMPDIR: path.join(dir, "tmp"),
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Put `source` in place of `binDir/pugi` atomically: prepare a temp file beside
52
+ * it, check that it runs, then rename it over the launcher. Returns the step
53
+ * that failed, or ok. On failure the launcher is untouched and the temp file is
54
+ * removed.
55
+ */
56
+ export function replaceLauncher({ binDir, source, pid = process.pid, fsApi = fs, spawnSync = childProcess.spawnSync }) {
57
+ const target = path.join(binDir, "pugi")
58
+ const tmp = path.join(binDir, `.pugi-native-${pid}.tmp`)
59
+ const cleanup = () => {
60
+ try {
61
+ fsApi.rmSync(tmp, { force: true })
62
+ } catch {
63
+ // Nothing more to do; the launcher is still in place.
64
+ }
65
+ }
66
+
67
+ try {
68
+ fsApi.rmSync(tmp, { force: true })
69
+ try {
70
+ fsApi.linkSync(source, tmp)
71
+ } catch {
72
+ fsApi.copyFileSync(source, tmp)
73
+ }
74
+ fsApi.chmodSync(tmp, 0o755)
75
+ } catch (error) {
76
+ cleanup()
77
+ return { ok: false, step: "prepare", error }
78
+ }
79
+
80
+ const removeDir = (dir) => {
81
+ try {
82
+ fsApi.rmSync(dir, { recursive: true, force: true })
83
+ } catch {
84
+ // A leftover directory under the system temp dir is harmless.
85
+ }
86
+ }
87
+ const sandbox = (() => {
88
+ const dir = (() => {
89
+ try {
90
+ return fsApi.mkdtempSync(path.join(os.tmpdir(), "pugi-postinstall-"))
91
+ } catch {
92
+ return undefined
93
+ }
94
+ })()
95
+ if (!dir) return undefined
96
+ try {
97
+ fsApi.mkdirSync(path.join(dir, "tmp"))
98
+ return dir
99
+ } catch {
100
+ // Created but unusable: remove it too (Codex review, round 3).
101
+ removeDir(dir)
102
+ return undefined
103
+ }
104
+ })()
105
+ if (!sandbox) {
106
+ cleanup()
107
+ return { ok: false, step: "verify" }
108
+ }
109
+ const check = spawnSync(tmp, ["--version"], {
110
+ stdio: "ignore",
111
+ timeout: VERIFY_TIMEOUT_MS,
112
+ killSignal: "SIGKILL",
113
+ env: sandboxEnv(sandbox),
114
+ })
115
+ removeDir(sandbox)
116
+ if (check.error || check.status !== 0) {
117
+ cleanup()
118
+ return { ok: false, step: "verify", error: check.error }
119
+ }
120
+
121
+ try {
122
+ fsApi.renameSync(tmp, target)
123
+ } catch (error) {
124
+ cleanup()
125
+ return { ok: false, step: "rename", error }
126
+ }
127
+ // When the target is already a hard link to the same file (a second run,
128
+ // e.g. `npm rebuild`), POSIX rename succeeds without moving anything and
129
+ // leaves the temp name behind (Codex review F2).
130
+ cleanup()
131
+ return { ok: true }
132
+ }
133
+
134
+ function main() {
135
+ if (!shouldReplaceLauncher({ platform: process.platform, userAgent: process.env.npm_config_user_agent })) return
136
+ const here = path.dirname(fileURLToPath(import.meta.url))
137
+ const require = createRequire(import.meta.url)
138
+ const name = `@pugi/cli-${PLATFORMS[process.platform] || process.platform}-${ARCHES[process.arch] || process.arch}`
139
+ const source = (() => {
140
+ try {
141
+ return path.join(path.dirname(require.resolve(`${name}/package.json`)), "bin", "pugi")
142
+ } catch {
143
+ return undefined
144
+ }
145
+ })()
146
+ if (!source) {
147
+ console.warn(`@pugi/cli: ${name} is not installed; pugi will report it when it starts.`)
148
+ return
149
+ }
150
+ const result = replaceLauncher({ binDir: path.join(here, "bin"), source })
151
+ if (!result.ok) {
152
+ const reason = result.error && result.error.message ? `: ${result.error.message}` : ""
153
+ console.warn(`@pugi/cli: kept the Node launcher (the native binary failed at "${result.step}"${reason}). pugi still works.`)
154
+ }
155
+ }
156
+
157
+ // Run only as the lifecycle script, not when a test imports this module. Real
158
+ // paths, because a package manager may start it through a symlink.
159
+ const realpath = (file) => {
160
+ try {
161
+ return fs.realpathSync(file)
162
+ } catch {
163
+ return undefined
164
+ }
31
165
  }
166
+ if (process.argv[1] && realpath(process.argv[1]) === realpath(fileURLToPath(import.meta.url))) main()
package/release.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "1.0.0-alpha.251",
2
+ "version": "1.0.0-alpha.253",
3
3
  "channel": "latest",
4
- "coreCommit": "67921ea690b92301599c3514b8f6f9cb13c1feb8",
5
- "sandboxCommit": "59f98372f88d463d5a245d67f8defabd4669a681",
6
- "consoleCommit": "a7e003aba16b90e2644faddff89f20d1f5502768",
7
- "consoleWebVersion": "1.14.45"
4
+ "coreCommit": "d2335b6e88ed2f6b69acdb6172b27c8da3965387",
5
+ "sandboxCommit": "b80da4206566be2301ce9cac0fcf998ec657bae7",
6
+ "consoleCommit": "886cfb44d8f9efd7034e164eb2e6ddb47f9df8f9",
7
+ "consoleWebVersion": "1.14.47"
8
8
  }