forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1#!/usr/bin/env node
2import process from 'node:process'
3import { spawn } from 'node:child_process'
4import { styleText } from 'node:util'
5import * as p from '@clack/prompts'
6import { defineCommand, runMain } from 'citty'
7import { serve } from 'srvx'
8import { createConnectorApp, generateToken, CONNECTOR_VERSION } from './server.ts'
9import { getNpmUser } from './npm-client.ts'
10import { initLogger, showToken, logInfo, logWarning, logError } from './logger.ts'
11
12const DEFAULT_PORT = 31415
13const DEFAULT_FRONTEND_URL = 'https://npmx.dev/'
14const DEV_FRONTEND_URL = 'http://127.0.0.1:3000/'
15
16async function runNpmLogin(): Promise<boolean> {
17 return new Promise(resolve => {
18 const child = spawn('npm', ['login'], {
19 stdio: 'inherit',
20 shell: true,
21 })
22
23 child.on('close', code => {
24 resolve(code === 0)
25 })
26
27 child.on('error', () => {
28 resolve(false)
29 })
30 })
31}
32
33const main = defineCommand({
34 meta: {
35 name: 'npmx-connector',
36 version: CONNECTOR_VERSION,
37 description: 'Local connector for npmx.dev',
38 },
39 args: {
40 port: {
41 type: 'string',
42 description: 'Port to listen on',
43 default: String(DEFAULT_PORT),
44 },
45 },
46 async run({ args }) {
47 const port = Number.parseInt(args.port as string, 10) || DEFAULT_PORT
48 const frontendUrl =
49 process.env.NPMX_CLI_DEV === 'true' ? DEV_FRONTEND_URL : DEFAULT_FRONTEND_URL
50
51 initLogger()
52
53 // Warning message and accept prompt
54 logWarning(
55 `This allows ${styleText('underline', 'npmx.dev')} to access your npm cli and any authenticated contexts.`,
56 )
57 const accept = await p.confirm({
58 message: 'Do you accept?',
59 initialValue: true,
60 })
61
62 if (!accept || p.isCancel(accept)) {
63 logError('Connector setup cancelled.')
64 process.exit(0)
65 }
66
67 // Check npm authentication before starting
68 logInfo('Checking npm authentication...')
69 let npmUser = await getNpmUser()
70
71 if (!npmUser) {
72 logWarning('Not logged in to npm. Starting npm login...')
73 // oxlint-disable-next-line no-console -- deliberate spacing
74 console.log()
75
76 const loginSuccess = await runNpmLogin()
77
78 // oxlint-disable-next-line no-console -- deliberate spacing
79 console.log()
80
81 if (!loginSuccess) {
82 logWarning('npm login failed or was cancelled.')
83 process.exit(1)
84 }
85
86 // Check again after login
87 npmUser = await getNpmUser()
88 if (!npmUser) {
89 logWarning('Still not authenticated after login attempt.')
90 process.exit(1)
91 }
92 }
93
94 logInfo(`Authenticated as: ${npmUser}`)
95
96 const token = generateToken()
97 showToken(token, port, frontendUrl)
98
99 const app = createConnectorApp(token)
100
101 const server = serve({
102 port,
103 hostname: '127.0.0.1',
104 fetch: app.fetch,
105 })
106
107 await server.ready()
108
109 logInfo('Waiting for connection... (Press Ctrl+C to stop)')
110 },
111})
112
113runMain(main)