hirify-cli 0.4.6 → 0.5.0

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.
@@ -0,0 +1,171 @@
1
+ import { realpathSync, readFileSync, existsSync, mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync, openSync, fsyncSync, closeSync, lstatSync } from 'node:fs'
2
+ import { join, dirname, resolve, relative, isAbsolute } from 'node:path'
3
+ import { createHash, randomUUID } from 'node:crypto'
4
+ import spawn from 'cross-spawn'
5
+ import semver from 'semver'
6
+ import lockfile from 'proper-lockfile'
7
+ import { CliError, aborted } from './errors.js'
8
+ import { makePrivate } from './store.js'
9
+ import { requireJSON, delay } from './http.js'
10
+
11
+ export const PACKAGE_NAME = 'hirify-cli'
12
+ export function runProcess(command, args, { signal, timeout = 120000, env = process.env, cwd } = {}) {
13
+ aborted(signal)
14
+ return new Promise((resolve, reject) => {
15
+ const child = spawn(command, args, { env, cwd, detached: process.platform !== 'win32', windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
16
+ let stdout = '', stderr = '', expired = false
17
+ let forceTimer
18
+ const stop = () => {
19
+ if (!child.pid) return
20
+ if (process.platform === 'win32') spawn('taskkill.exe', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' }).on('error', () => child.kill())
21
+ else { try { process.kill(-child.pid, 'SIGTERM') } catch {}; forceTimer = setTimeout(() => { try { process.kill(-child.pid, 'SIGKILL') } catch {} }, 800); forceTimer.unref() }
22
+ }
23
+ const timer = setTimeout(() => { expired = true; stop() }, timeout)
24
+ const cancel = stop
25
+ signal?.addEventListener('abort', cancel, { once: true })
26
+ child.stdout?.on('data', v => { if (stdout.length < 1024 * 1024) stdout += v })
27
+ child.stderr?.on('data', v => { if (stderr.length < 1024 * 1024) stderr += v })
28
+ child.once('error', () => { clearTimeout(timer); signal?.removeEventListener('abort', cancel); reject(new CliError('process_start_failed', 'Could not start the package manager. Check npm on PATH.')) })
29
+ child.once('close', (code, childSignal) => { clearTimeout(timer); clearTimeout(forceTimer); signal?.removeEventListener('abort', cancel); if (signal?.aborted) reject(signal.reason); else if (expired) reject(new CliError('update_timeout', 'The package manager did not finish in time. The active CLI was not replaced.')); else resolve({ code, signal: childSignal, stdout, stderr }) })
30
+ })
31
+ }
32
+ const real = path => { try { return realpathSync(path) } catch { return resolve(path) } }
33
+ export function updateDirectory(configDir, baseRoot) {
34
+ return join(configDir, 'updates', createHash('sha256').update(real(baseRoot)).digest('hex').slice(0, 24))
35
+ }
36
+ function readJSON(file) { try { return JSON.parse(readFileSync(file, 'utf8')) } catch { return null } }
37
+ async function privateDirectory(path, signal) { mkdirSync(path, { recursive: true, mode: 0o700 }); await makePrivate(path, true, signal) }
38
+ async function atomicJSON(file, value, signal) {
39
+ await privateDirectory(dirname(file), signal)
40
+ const temp = file + '.' + randomUUID() + '.tmp'
41
+ let fd
42
+ try {
43
+ fd = openSync(temp, 'wx', 0o600); writeFileSync(fd, JSON.stringify(value, null, 2) + '\n'); fsyncSync(fd); closeSync(fd); fd = undefined
44
+ if (process.platform === 'win32') await makePrivate(temp, false, signal)
45
+ renameSync(temp, file)
46
+ if (process.platform !== 'win32') { const dir = openSync(dirname(file), 'r'); try { fsyncSync(dir) } finally { closeSync(dir) } }
47
+ } finally { if (fd !== undefined) closeSync(fd); try { rmSync(temp) } catch {} }
48
+ }
49
+ export function activeInstallation(configDir, baseRoot, baseVersion, pin = '') {
50
+ const dir = updateDirectory(configDir, baseRoot)
51
+ const state = readJSON(join(dir, 'active.json'))
52
+ if (!state || state.base_version !== baseVersion || !semver.valid(state.version) || (pin && pin !== state.version)) return null
53
+ if (!state.pinned && !semver.gt(state.version, baseVersion)) return null
54
+ // The pointer contains only a validated version, never an arbitrary executable path.
55
+ const root = join(dir, 'versions', state.version, 'node_modules', PACKAGE_NAME)
56
+ const pkg = readJSON(join(root, 'package.json'))
57
+ if (pkg?.name !== PACKAGE_NAME || pkg.version !== state.version || !semver.satisfies(process.version, pkg.engines?.node || '*') || !existsSync(join(root, 'bin', 'lib', 'main.js'))) return null
58
+ // Selection is read-only, including for offline version. ACLs are set during installation.
59
+ for (const directory of [configDir, join(configDir, 'updates'), dir, join(dir, 'versions'), dirname(dirname(root))]) {
60
+ const stat = lstatSync(directory)
61
+ if (!stat.isDirectory() || stat.isSymbolicLink() || (process.platform !== 'win32' && (stat.uid !== process.getuid() || (stat.mode & 0o022)))) throw new CliError('unsafe_storage', 'The managed CLI directory must be private and owned by this user.')
62
+ }
63
+ const canonical = real(root)
64
+ if (relative(real(dir), canonical).startsWith('..') || isAbsolute(relative(real(dir), canonical)) || lstatSync(root).isSymbolicLink()) return null
65
+ return { root, version: state.version, pinned: Boolean(state.pinned), previous: state.previous || null }
66
+ }
67
+ export function createUpdater({ config, http, signal, output, event, packageRoot, baseRoot = packageRoot, baseVersion = config.version, run = runProcess }) {
68
+ const dir = updateDirectory(config.dir, baseRoot)
69
+ const stateFile = join(dir, 'active.json')
70
+ async function installation() {
71
+ // A checkout or a local/npx dependency is never modified or auto-managed.
72
+ const canonical = real(baseRoot)
73
+ if (!canonical.includes('node_modules') || config.env.npm_command === 'exec' || config.env.npm_lifecycle_event === 'npx') return { owner: 'unmanaged', reason: 'checkout_or_ephemeral' }
74
+ try {
75
+ const answer = await run('npm', ['root', '--global'], { signal, timeout: 3000 })
76
+ if (answer.code !== 0) return { owner: 'unknown', reason: 'npm_root_failed' }
77
+ return real(join(answer.stdout.trim(), PACKAGE_NAME)) === canonical ? { owner: 'npm-global', reason: 'verified' } : { owner: 'unmanaged', reason: 'different_prefix' }
78
+ } catch (error) { if (signal?.aborted) throw error; return { owner: 'unknown', reason: error.code || 'npm_unavailable' } }
79
+ }
80
+ async function registry() {
81
+ const answer = await run('npm', ['config', 'get', 'registry'], { signal, timeout: 3000 })
82
+ if (answer.code !== 0) throw new CliError('update_registry', 'Could not determine the npm registry.')
83
+ let url
84
+ try { url = new URL(answer.stdout.trim()) } catch { throw new CliError('update_registry', 'The npm registry URL is invalid.') }
85
+ if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) throw new CliError('update_registry', 'Use an HTTPS npm registry without credentials in its URL.')
86
+ return url.href.replace(/\/$/, '')
87
+ }
88
+ async function check(version = 'latest') {
89
+ if (version !== 'latest' && !semver.valid(version)) throw new CliError('invalid_arguments', 'Use an exact semantic version, such as hirify update 0.5.0.')
90
+ const registryURL = await registry()
91
+ const res = await http.request(`${registryURL}/${PACKAGE_NAME}/${encodeURIComponent(version)}`, { safe: true, timeout: 3000 })
92
+ if (!res.ok) throw new CliError('update_check_failed', 'Could not check the requested CLI version.', { status: res.status })
93
+ const pkg = requireJSON(res)
94
+ if (pkg.name !== PACKAGE_NAME || !semver.valid(pkg.version) || (version !== 'latest' && pkg.version !== version) || !pkg.dist?.integrity || !semver.satisfies(process.version, pkg.engines?.node || '*')) throw new CliError('update_incompatible', 'The requested CLI release is invalid or does not support this Node version.')
95
+ return { version: pkg.version, registry: registryURL, integrity: pkg.dist.integrity, available: semver.gt(pkg.version, config.version) }
96
+ }
97
+ async function install(target, { pin = false } = {}) {
98
+ const owner = await installation()
99
+ if (owner.owner !== 'npm-global') throw new CliError('update_owner_unknown', 'This installation is managed elsewhere. Update it with the package manager that installed it, or run npx hirify-cli@<version>.')
100
+ await privateDirectory(config.dir, signal); await privateDirectory(dir, signal); await privateDirectory(join(dir, 'versions'), signal)
101
+ let release
102
+ while (!release) {
103
+ aborted(signal)
104
+ try { release = await lockfile.lock(stateFile, { realpath: false, stale: 600000, update: 10000 }) }
105
+ catch (error) { if (error.code !== 'ELOCKED') throw new CliError('update_lock_failed', 'Could not lock the update directory.'); await delay(100, signal) }
106
+ }
107
+ let staging
108
+ try {
109
+ const previousState = readJSON(stateFile)
110
+ const destination = join(dir, 'versions', target.version)
111
+ if (!existsSync(destination)) {
112
+ // npm misidentifies its root when --prefix contains an ancestor symlink (macOS /var).
113
+ staging = realpathSync(mkdtempSync(join(dir, '.staging-'))); await makePrivate(staging, true, signal)
114
+ const result = await run('npm', ['install', '--prefix', staging, '--global=false', '--save=true', '--package-lock=true', '--ignore-scripts', '--no-audit', '--no-fund', '--strict-ssl=true', '--registry', target.registry, `${PACKAGE_NAME}@${target.version}`], { signal, timeout: 180000, env: { ...config.env, HIRIFY_NO_AUTO_UPDATE: '1' } })
115
+ if (result.code !== 0) throw new CliError('update_install_failed', 'The update could not be installed. The active CLI was not replaced.')
116
+ const pkgPath = join(staging, 'node_modules', PACKAGE_NAME)
117
+ const pkg = readJSON(join(pkgPath, 'package.json'))
118
+ const lock = readJSON(join(staging, 'package-lock.json'))
119
+ if (pkg?.name !== PACKAGE_NAME || pkg.version !== target.version || !existsSync(join(pkgPath, 'bin', 'lib', 'main.js')) || lock?.packages?.[`node_modules/${PACKAGE_NAME}`]?.integrity !== target.integrity) throw new CliError('update_verification_failed', 'The installed package did not match the selected release. The active CLI was not replaced.')
120
+ const verify = await run(process.execPath, [join(pkgPath, 'bin', 'hirify.js'), 'version'], { signal, timeout: 10000, env: { ...config.env, HIRIFY_NO_AUTO_UPDATE: '1', HIRIFY_INTERNAL_REEXEC: '1' } })
121
+ if (verify.code !== 0 || verify.stdout.trim() !== target.version) throw new CliError('update_verification_failed', 'The new CLI could not start. The active CLI was not replaced.')
122
+ renameSync(staging, destination); staging = null
123
+ }
124
+ const cachedRoot = join(destination, 'node_modules', PACKAGE_NAME)
125
+ const cachedPackage = readJSON(join(cachedRoot, 'package.json'))
126
+ const cachedLock = readJSON(join(destination, 'package-lock.json'))
127
+ if (cachedPackage?.name !== PACKAGE_NAME || cachedPackage.version !== target.version || !semver.satisfies(process.version, cachedPackage.engines?.node || '*') || !existsSync(join(cachedRoot, 'bin', 'lib', 'main.js')) || cachedLock?.packages?.[`node_modules/${PACKAGE_NAME}`]?.integrity !== target.integrity) throw new CliError('update_verification_failed', 'The cached artifact does not match the selected release. The active CLI was not replaced.')
128
+ aborted(signal)
129
+ const previous = previousState?.base_version === baseVersion ? (previousState.version === target.version ? previousState.previous : previousState.version) : baseVersion
130
+ const pinned = pin || Boolean(previousState?.version === target.version && previousState.pinned)
131
+ await atomicJSON(stateFile, { schema_version: 1, base_version: baseVersion, version: target.version, integrity: target.integrity, pinned, previous: previous || baseVersion }, signal)
132
+ return { version: target.version, previous: previous || baseVersion, pinned }
133
+ } finally { if (staging) rmSync(staging, { recursive: true, force: true }); await release().catch(() => {}) }
134
+ }
135
+ async function rollback() {
136
+ const state = readJSON(stateFile)
137
+ if (!state || state.base_version !== baseVersion || !semver.valid(state.previous)) throw new CliError('update_no_previous', 'No previous managed CLI version is available. Install an exact package version with npm instead.')
138
+ if (state.previous !== baseVersion && !existsSync(join(dir, 'versions', state.previous, 'node_modules', PACKAGE_NAME, 'bin', 'lib', 'main.js'))) throw new CliError('update_no_previous', 'The previous CLI artifact is unavailable. Install its exact package version with npm instead.')
139
+ let release
140
+ while (!release) {
141
+ aborted(signal)
142
+ try { release = await lockfile.lock(stateFile, { realpath: false, stale: 600000, update: 10000 }) }
143
+ catch (error) { if (error.code !== 'ELOCKED') throw new CliError('update_lock_failed', 'Could not lock the update directory.'); await delay(100, signal) }
144
+ }
145
+ try {
146
+ const current = readJSON(stateFile)
147
+ if (current?.version !== state.version) throw new CliError('update_changed', 'Another process changed the active version. Check hirify version before retrying.')
148
+ await atomicJSON(stateFile, { ...state, version: state.previous, previous: state.version, integrity: null, pinned: true }, signal)
149
+ return { version: state.previous, previous: state.version, pinned: true }
150
+ } finally { await release().catch(() => {}) }
151
+ }
152
+ async function automatic() {
153
+ if (config.env.HIRIFY_NO_AUTO_UPDATE === '1' || config.env.HIRIFY_INTERNAL_REEXEC === '1' || config.env.HIRIFY_VERSION_PIN || readJSON(stateFile)?.pinned) return null
154
+ const owner = await installation()
155
+ if (owner.owner !== 'npm-global') { event('update', { code: owner.reason }); return null }
156
+ try {
157
+ const target = await check()
158
+ if (!target.available) { event('update', { code: 'up_to_date' }); return null }
159
+ output.progress(`Installing Hirify CLI ${target.version} in a separate directory...`)
160
+ const result = await install(target)
161
+ output.progress(`Hirify CLI ${result.version} is ready.`)
162
+ return activeInstallation(config.dir, baseRoot, baseVersion)
163
+ } catch (error) {
164
+ if (signal?.aborted) throw error
165
+ event('update', { code: error.code })
166
+ output.progress(`Update check or installation did not complete (${error.code || 'update_failed'}). Continuing with ${config.version}. Run hirify update --check for details.`)
167
+ return null
168
+ }
169
+ }
170
+ return { installation, check, install, rollback, automatic, dir }
171
+ }
@@ -0,0 +1,2 @@
1
+ // Compatibility export for consumers of the browser adapter.
2
+ export { canOpenBrowser, openBrowser } from './lib/browser.js'
@@ -0,0 +1,349 @@
1
+ {
2
+ "name": "hirify-cli",
3
+ "version": "0.5.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "hirify-cli",
9
+ "version": "0.5.0",
10
+ "license": "Apache-2.0",
11
+ "dependencies": {
12
+ "cross-spawn": "7.0.6",
13
+ "http-proxy-agent": "7.0.2",
14
+ "https-proxy-agent": "7.0.6",
15
+ "open": "10.2.0",
16
+ "proper-lockfile": "4.1.2",
17
+ "proxy-from-env": "1.1.0",
18
+ "semver": "7.8.5"
19
+ },
20
+ "bin": {
21
+ "hirify": "bin/hirify.js"
22
+ },
23
+ "engines": {
24
+ "node": ">=18"
25
+ }
26
+ },
27
+ "node_modules/agent-base": {
28
+ "version": "7.1.4",
29
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
30
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">= 14"
34
+ }
35
+ },
36
+ "node_modules/bundle-name": {
37
+ "version": "4.1.0",
38
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
39
+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "run-applescript": "^7.0.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
47
+ "funding": {
48
+ "url": "https://github.com/sponsors/sindresorhus"
49
+ }
50
+ },
51
+ "node_modules/cross-spawn": {
52
+ "version": "7.0.6",
53
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
54
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
55
+ "license": "MIT",
56
+ "dependencies": {
57
+ "path-key": "^3.1.0",
58
+ "shebang-command": "^2.0.0",
59
+ "which": "^2.0.1"
60
+ },
61
+ "engines": {
62
+ "node": ">= 8"
63
+ }
64
+ },
65
+ "node_modules/debug": {
66
+ "version": "4.4.3",
67
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
68
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
69
+ "license": "MIT",
70
+ "dependencies": {
71
+ "ms": "^2.1.3"
72
+ },
73
+ "engines": {
74
+ "node": ">=6.0"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "supports-color": {
78
+ "optional": true
79
+ }
80
+ }
81
+ },
82
+ "node_modules/default-browser": {
83
+ "version": "5.5.1",
84
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz",
85
+ "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==",
86
+ "license": "MIT",
87
+ "dependencies": {
88
+ "bundle-name": "^4.1.0",
89
+ "default-browser-id": "^5.0.0"
90
+ },
91
+ "engines": {
92
+ "node": ">=18"
93
+ },
94
+ "funding": {
95
+ "url": "https://github.com/sponsors/sindresorhus"
96
+ }
97
+ },
98
+ "node_modules/default-browser-id": {
99
+ "version": "5.0.1",
100
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
101
+ "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
102
+ "license": "MIT",
103
+ "engines": {
104
+ "node": ">=18"
105
+ },
106
+ "funding": {
107
+ "url": "https://github.com/sponsors/sindresorhus"
108
+ }
109
+ },
110
+ "node_modules/define-lazy-prop": {
111
+ "version": "3.0.0",
112
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
113
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
114
+ "license": "MIT",
115
+ "engines": {
116
+ "node": ">=12"
117
+ },
118
+ "funding": {
119
+ "url": "https://github.com/sponsors/sindresorhus"
120
+ }
121
+ },
122
+ "node_modules/graceful-fs": {
123
+ "version": "4.2.11",
124
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
125
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
126
+ "license": "ISC"
127
+ },
128
+ "node_modules/http-proxy-agent": {
129
+ "version": "7.0.2",
130
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
131
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
132
+ "license": "MIT",
133
+ "dependencies": {
134
+ "agent-base": "^7.1.0",
135
+ "debug": "^4.3.4"
136
+ },
137
+ "engines": {
138
+ "node": ">= 14"
139
+ }
140
+ },
141
+ "node_modules/https-proxy-agent": {
142
+ "version": "7.0.6",
143
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
144
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
145
+ "license": "MIT",
146
+ "dependencies": {
147
+ "agent-base": "^7.1.2",
148
+ "debug": "4"
149
+ },
150
+ "engines": {
151
+ "node": ">= 14"
152
+ }
153
+ },
154
+ "node_modules/is-docker": {
155
+ "version": "3.0.0",
156
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
157
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
158
+ "license": "MIT",
159
+ "bin": {
160
+ "is-docker": "cli.js"
161
+ },
162
+ "engines": {
163
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
164
+ },
165
+ "funding": {
166
+ "url": "https://github.com/sponsors/sindresorhus"
167
+ }
168
+ },
169
+ "node_modules/is-inside-container": {
170
+ "version": "1.0.0",
171
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
172
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
173
+ "license": "MIT",
174
+ "dependencies": {
175
+ "is-docker": "^3.0.0"
176
+ },
177
+ "bin": {
178
+ "is-inside-container": "cli.js"
179
+ },
180
+ "engines": {
181
+ "node": ">=14.16"
182
+ },
183
+ "funding": {
184
+ "url": "https://github.com/sponsors/sindresorhus"
185
+ }
186
+ },
187
+ "node_modules/is-wsl": {
188
+ "version": "3.1.1",
189
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
190
+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
191
+ "license": "MIT",
192
+ "dependencies": {
193
+ "is-inside-container": "^1.0.0"
194
+ },
195
+ "engines": {
196
+ "node": ">=16"
197
+ },
198
+ "funding": {
199
+ "url": "https://github.com/sponsors/sindresorhus"
200
+ }
201
+ },
202
+ "node_modules/isexe": {
203
+ "version": "2.0.0",
204
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
205
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
206
+ "license": "ISC"
207
+ },
208
+ "node_modules/ms": {
209
+ "version": "2.1.3",
210
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
211
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
212
+ "license": "MIT"
213
+ },
214
+ "node_modules/open": {
215
+ "version": "10.2.0",
216
+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
217
+ "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
218
+ "license": "MIT",
219
+ "dependencies": {
220
+ "default-browser": "^5.2.1",
221
+ "define-lazy-prop": "^3.0.0",
222
+ "is-inside-container": "^1.0.0",
223
+ "wsl-utils": "^0.1.0"
224
+ },
225
+ "engines": {
226
+ "node": ">=18"
227
+ },
228
+ "funding": {
229
+ "url": "https://github.com/sponsors/sindresorhus"
230
+ }
231
+ },
232
+ "node_modules/path-key": {
233
+ "version": "3.1.1",
234
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
235
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
236
+ "license": "MIT",
237
+ "engines": {
238
+ "node": ">=8"
239
+ }
240
+ },
241
+ "node_modules/proper-lockfile": {
242
+ "version": "4.1.2",
243
+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
244
+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
245
+ "license": "MIT",
246
+ "dependencies": {
247
+ "graceful-fs": "^4.2.4",
248
+ "retry": "^0.12.0",
249
+ "signal-exit": "^3.0.2"
250
+ }
251
+ },
252
+ "node_modules/proxy-from-env": {
253
+ "version": "1.1.0",
254
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
255
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
256
+ "license": "MIT"
257
+ },
258
+ "node_modules/retry": {
259
+ "version": "0.12.0",
260
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
261
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
262
+ "license": "MIT",
263
+ "engines": {
264
+ "node": ">= 4"
265
+ }
266
+ },
267
+ "node_modules/run-applescript": {
268
+ "version": "7.1.0",
269
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
270
+ "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
271
+ "license": "MIT",
272
+ "engines": {
273
+ "node": ">=18"
274
+ },
275
+ "funding": {
276
+ "url": "https://github.com/sponsors/sindresorhus"
277
+ }
278
+ },
279
+ "node_modules/semver": {
280
+ "version": "7.8.5",
281
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
282
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
283
+ "license": "ISC",
284
+ "bin": {
285
+ "semver": "bin/semver.js"
286
+ },
287
+ "engines": {
288
+ "node": ">=10"
289
+ }
290
+ },
291
+ "node_modules/shebang-command": {
292
+ "version": "2.0.0",
293
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
294
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
295
+ "license": "MIT",
296
+ "dependencies": {
297
+ "shebang-regex": "^3.0.0"
298
+ },
299
+ "engines": {
300
+ "node": ">=8"
301
+ }
302
+ },
303
+ "node_modules/shebang-regex": {
304
+ "version": "3.0.0",
305
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
306
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
307
+ "license": "MIT",
308
+ "engines": {
309
+ "node": ">=8"
310
+ }
311
+ },
312
+ "node_modules/signal-exit": {
313
+ "version": "3.0.7",
314
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
315
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
316
+ "license": "ISC"
317
+ },
318
+ "node_modules/which": {
319
+ "version": "2.0.2",
320
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
321
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
322
+ "license": "ISC",
323
+ "dependencies": {
324
+ "isexe": "^2.0.0"
325
+ },
326
+ "bin": {
327
+ "node-which": "bin/node-which"
328
+ },
329
+ "engines": {
330
+ "node": ">= 8"
331
+ }
332
+ },
333
+ "node_modules/wsl-utils": {
334
+ "version": "0.1.0",
335
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
336
+ "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
337
+ "license": "MIT",
338
+ "dependencies": {
339
+ "is-wsl": "^3.1.0"
340
+ },
341
+ "engines": {
342
+ "node": ">=18"
343
+ },
344
+ "funding": {
345
+ "url": "https://github.com/sponsors/sindresorhus"
346
+ }
347
+ }
348
+ }
349
+ }
package/package.json CHANGED
@@ -1,15 +1,27 @@
1
1
  {
2
2
  "name": "hirify-cli",
3
- "version": "0.4.6",
3
+ "version": "0.5.0",
4
4
  "description": "Hirify CLI for AI agents: browser sign-in, job feeds, search, reading a vacancy in full, contacts to apply to, and applying on Hirify.",
5
5
  "bin": {
6
6
  "hirify": "bin/hirify.js"
7
7
  },
8
8
  "scripts": {
9
- "test": "node scripts/test.mjs",
10
- "prepublishOnly": "node scripts/prepublish-check.mjs"
9
+ "test": "node --test scripts/test.mjs scripts/runtime.test.mjs scripts/network.test.mjs scripts/update.test.mjs scripts/storage.test.mjs",
10
+ "build": "node scripts/build.mjs",
11
+ "test:package": "node scripts/verify-package.mjs",
12
+ "prepublishOnly": "node scripts/prepublish-check.mjs",
13
+ "test:install": "node scripts/install-update-smoke.mjs"
11
14
  },
12
15
  "type": "module",
16
+ "dependencies": {
17
+ "cross-spawn": "7.0.6",
18
+ "http-proxy-agent": "7.0.2",
19
+ "https-proxy-agent": "7.0.6",
20
+ "open": "10.2.0",
21
+ "proper-lockfile": "4.1.2",
22
+ "proxy-from-env": "1.1.0",
23
+ "semver": "7.8.5"
24
+ },
13
25
  "engines": {
14
26
  "node": ">=18"
15
27
  },
@@ -17,7 +29,8 @@
17
29
  "bin",
18
30
  "skills",
19
31
  "README.md",
20
- "NOTICE"
32
+ "NOTICE",
33
+ "npm-shrinkwrap.json"
21
34
  ],
22
35
  "keywords": [
23
36
  "hirify",